This commit is contained in:
bQUARKz 2026-02-18 16:24:04 +00:00
parent 650f1e0716
commit 46146993aa
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 80 additions and 103 deletions

View File

@ -0,0 +1,74 @@
use crate::{ObjectHeader, ObjectKind};
use prometeu_bytecode::HeapRef;
/// Internal stored object: header plus opaque payload bytes.
#[derive(Debug, Clone)]
pub struct StoredObject {
pub header: ObjectHeader,
pub payload: Vec<u8>,
}
/// Simple vector-backed heap. No GC or compaction.
#[derive(Debug, Default, Clone)]
pub struct Heap {
objects: Vec<StoredObject>,
}
impl Heap {
pub fn new() -> Self { Self { objects: Vec::new() } }
/// Allocate a new object with the given kind and raw payload bytes.
/// Returns an opaque `HeapRef` handle.
pub fn allocate_object(&mut self, kind: ObjectKind, payload: &[u8]) -> HeapRef {
let header = ObjectHeader::new(kind, payload.len() as u32);
let obj = StoredObject { header, payload: payload.to_vec() };
let idx = self.objects.len();
self.objects.push(obj);
HeapRef(idx as u32)
}
/// Returns true if this handle refers to an allocated object.
pub fn is_valid(&self, r: HeapRef) -> bool {
(r.0 as usize) < self.objects.len()
}
/// Get immutable access to an object's header by handle.
pub fn header(&self, r: HeapRef) -> Option<&ObjectHeader> {
self.objects.get(r.0 as usize).map(|o| &o.header)
}
/// Current number of allocated objects.
pub fn len(&self) -> usize { self.objects.len() }
pub fn is_empty(&self) -> bool { self.objects.is_empty() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_allocation_returns_valid_refs() {
let mut heap = Heap::new();
let r1 = heap.allocate_object(ObjectKind::String, b"hello");
let r2 = heap.allocate_object(ObjectKind::Bytes, &[1, 2, 3, 4]);
let r3 = heap.allocate_object(ObjectKind::Array, &[]);
assert!(heap.is_valid(r1));
assert!(heap.is_valid(r2));
assert!(heap.is_valid(r3));
assert_eq!(heap.len(), 3);
let h1 = heap.header(r1).unwrap();
assert_eq!(h1.kind, ObjectKind::String);
assert_eq!(h1.payload_len, 5);
let h2 = heap.header(r2).unwrap();
assert_eq!(h2.kind, ObjectKind::Bytes);
assert_eq!(h2.payload_len, 4);
let h3 = heap.header(r3).unwrap();
assert_eq!(h3.kind, ObjectKind::Array);
assert_eq!(h3.payload_len, 0);
}
}

View File

@ -4,7 +4,9 @@ pub mod verifier;
mod virtual_machine;
pub mod vm_init_error;
pub mod object;
pub mod heap;
pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
pub use object::{object_flags, ObjectHeader, ObjectKind};
pub use heap::{Heap, StoredObject};

View File

@ -5,6 +5,7 @@ use crate::{HostContext, NativeInterface};
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
use prometeu_bytecode::ProgramImage;
use prometeu_bytecode::Value;
use crate::heap::Heap;
use prometeu_bytecode::{
TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB,
TRAP_STACK_UNDERFLOW, TRAP_TYPE, TrapInfo,
@ -83,7 +84,7 @@ pub struct VirtualMachine {
/// The loaded executable (Bytecode + Constant Pool), that is the ROM translated.
pub program: ProgramImage,
/// Heap Memory: Dynamic allocation pool.
pub heap: Vec<Value>,
pub heap: Heap,
/// Total virtual cycles consumed since the VM started.
pub cycles: u64,
/// Stop flag: true if a `HALT` opcode was encountered.
@ -114,7 +115,7 @@ impl VirtualMachine {
None,
std::collections::HashMap::new(),
),
heap: Vec::new(),
heap: Heap::new(),
cycles: 0,
halted: false,
breakpoints: std::collections::HashSet::new(),
@ -135,7 +136,7 @@ impl VirtualMachine {
self.operand_stack.clear();
self.call_stack.clear();
self.globals.clear();
self.heap.clear();
self.heap = Heap::new();
self.cycles = 0;
self.halted = true; // execution is impossible until a successful load

View File

@ -1,103 +1,3 @@
# PR-3.3 — Implement Basic Heap Allocator (No GC Yet)
### Briefing
We need a simple heap allocator that can store objects and return handles. This PR introduces a minimal allocator without garbage collection.
### Target
* Create a heap structure that stores objects.
* Return `HeapRef` handles for allocated objects.
### Work items
* Implement a `Heap` struct.
* Store objects in a vector or arena-like container.
* Provide methods such as:
* `allocate_object(kind, payload)`.
* Return a `HeapRef` handle.
* Integrate heap into VM state.
### Acceptance checklist
* [ ] Heap exists and can allocate objects.
* [ ] VM can hold a heap instance.
* [ ] Allocation returns valid `HeapRef`.
* [ ] `cargo test` passes.
### Tests
* Add a unit test allocating a few objects and verifying handles are valid.
### Junie instructions
**You MAY:**
* Use a simple vector-backed heap.
* Implement minimal allocation logic.
**You MUST NOT:**
* Implement garbage collection here.
* Add compaction or generational strategies.
**If unclear:**
* Ask before choosing allocation layout.
---
# PR-3.4 — Define GC Root Set (Stack, Frames, Globals)
### Briefing
The GC must know where roots are located. This PR defines the root set abstraction without running a full GC yet.
### Target
* Identify and enumerate all GC roots.
* Provide a mechanism to iterate them.
### Work items
* Define a root traversal interface or helper.
* Enumerate roots from:
* Value stack.
* Call frames.
* Globals or constant pools if applicable.
* Provide a function like `visit_roots(visitor)`.
### Acceptance checklist
* [ ] Root set traversal exists.
* [ ] Stack and frames are included as roots.
* [ ] Code compiles.
* [ ] `cargo test` passes.
### Tests
* Add a test that inserts a `HeapRef` in the stack and confirms it is visited by root traversal.
### Junie instructions
**You MAY:**
* Add root iteration helpers.
* Traverse stack and frames.
**You MUST NOT:**
* Implement marking logic yet.
* Change frame or stack architecture.
**If unclear:**
* Ask which structures must be roots.
---
# PR-3.5 — Implement Mark Phase (Reachability Traversal)
### Briefing