Fixes for uninitialised garbage collected pointers, jumps and identifiers in the compiler. Built but erroring on eq.

This commit is contained in:
Christian Lincoln 2026-08-25 21:24:34 +01:00
parent d2f47604bf
commit 44b389a6a1
5 changed files with 2922 additions and 2640 deletions

BIN
mars-49d70575ae6f08.core Normal file

Binary file not shown.

View file

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::fmt::Debug;
@ -24,7 +24,7 @@ in the allocator ensure location is consistent
#[derive(Debug)]
pub struct Gc<T: ?Sized + Traverse> {
it: NonNull<RefCell<Header<T>>>
it: NonNull<RefCell<Header<T>>>,
}
pub struct Agc {}
@ -49,17 +49,24 @@ impl<T: ?Sized + Traverse> PartialEq for Gc<T> {
impl<T: ?Sized + Traverse> Gc<T> {
pub fn borrow(&self) -> impl Deref<Target = T> {
Ref::map(unsafe { // ???
Ref::map(
unsafe {
// ???
(*self.it.as_ptr()).borrow()
},|it| &it.data)
},
|it| &it.data,
)
}
pub fn borrow_mut(&mut self) -> impl DerefMut<Target = T> {
RefMut::map(unsafe {
(*self.it.as_mut()).borrow_mut()
},|it| &mut it.data)
RefMut::map(unsafe { (*self.it.as_mut()).borrow_mut() }, |it| {
&mut it.data
})
}
pub fn replace(&mut self, new: T) where T: Sized {
pub fn replace(&mut self, new: T)
where
T: Sized,
{
unsafe {
(*self.it.as_mut()).borrow_mut().data = new;
}
@ -79,13 +86,13 @@ enum Color {
struct Header<T: ?Sized + Traverse> {
layout: Layout,
color: Color, // NOT for incremental tri-color right now; white == mark-free, black == mark-keep
data: T
data: T,
}
pub struct Allocator {
// An array of pointers is used here since I did not want to use a compactor and larger types
// held on the GC (game objects) will most likely be massive anyway.
objects: Vec<Option<*mut RefCell<Header<dyn Traverse>>>>,
objects: Vec<Option<NonNull<RefCell<Header<dyn Traverse>>>>>,
}
impl Allocator {
@ -94,15 +101,19 @@ impl Allocator {
objects: Vec::new(),
}
}
pub fn alloc<T: Traverse + 'static>(&mut self, it: T) -> Gc<T> {
pub fn alloc<T: Traverse>(&mut self, it: T) -> Gc<T> {
let layout = Layout::new::<RefCell<Header<T>>>();
let pointer = unsafe { alloc(layout) as *mut RefCell<Header<T>> }; // todo: maybe use a compacting GC
assert!(!pointer.is_null()); // lol idk how to fix this rn
let mut header = unsafe { pointer.as_mut() }.unwrap().borrow_mut();
header.layout = layout;
header.color = Color::Black;
header.data = it;
self.objects.push(Some(pointer as *mut RefCell<Header<dyn Traverse>>));
let pointer = unsafe {
let pointer = alloc(layout) as *mut RefCell<Header<T>>;
assert!(!pointer.is_null()); // lol idk how to fix this rn (gah use a result!!!)
pointer.write(RefCell::new(Header {
layout,
color: Color::Black,
data: it,
}));
pointer
}; // todo: maybe use a compacting GC
self.objects.push(NonNull::new(pointer));
Gc {
it: NonNull::new(pointer).unwrap(),
}
@ -110,20 +121,23 @@ impl Allocator {
fn mark_white(&mut self) {
for i in 0..self.objects.len() {
if let Some(object) = self.objects[i] {
unsafe { (*object).borrow_mut().color = Color::White };
unsafe { (*object.as_ptr()).borrow_mut().color = Color::White };
}
}
}
fn collect(&mut self) {
self.mark_white();
self.objects.retain(|object| {
if let Some(object) = object {
let header = unsafe { (**object).borrow_mut() };
let header = unsafe { (*object.as_ptr()).borrow_mut() };
match header.color {
Color::White => {
unsafe { dealloc(*object as *mut u8,header.layout); }
unsafe {
dealloc(object.as_ptr() as *mut u8, header.layout);
}
true
}
Color::Black => false
Color::Black => false,
}
} else {
false
@ -133,7 +147,8 @@ impl Allocator {
}
pub trait Traverse: Any + Debug {
fn traverse(&self) { // mark or grey
fn traverse(&self) {
// mark or grey
()
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
use std::hash::{DefaultHasher, Hash, Hasher};
use std::rc::Rc;
use crate::gc::{Gc, Traverse};
use crate::{Callable, RunError, Value};
use std::rc::Rc;
// I've already spent 2 months on this interpreter, and I'm tired, so I've cut a few corners...
/* See some of Lua's notes on this table: https://www.lua.org/source/5.5/ltable.c.html
@ -25,6 +24,7 @@ Notes:
*/
type Displacement = u8;
pub type Hashed = usize;
#[derive(Debug, Clone)]
struct Entry {
@ -45,31 +45,22 @@ impl Default for Entry {
}
}
pub fn hash(value: &Value) -> usize {
pub fn hash(value: &Value) -> Hashed {
match value {
Value::Nil => { 0 }
Value::Bool(boolean) => { if *boolean { 1 } else { 0 } }
Value::String(string) => {
let mut hasher = DefaultHasher::new();
string.hash(&mut hasher);
hasher.finish() as usize
}
Value::Phrase(phrase) => {
phrase.1
}
Value::Function(callable) => {
match callable {
Callable::Rust(native) => {
Rc::as_ptr(native).addr()
}
Callable::Mars(closure) => {
closure.addr()
Value::Nil => 0,
Value::Bool(boolean) => {
if *boolean {
1
} else {
0
}
}
}
Value::Integer(integer) => {
*integer as usize
}
Value::String(_string, hash) => *hash,
Value::Function(callable) => match callable {
Callable::Rust(native) => Rc::as_ptr(native).addr(),
Callable::Mars(closure) => closure.addr(),
},
Value::Integer(integer) => *integer as usize,
Value::Number(number) => {
if number.is_nan() {
0
@ -77,12 +68,8 @@ pub fn hash(value: &Value) -> usize {
number.to_bits() as usize
}
}
Value::Table(table) => {
table.addr()
}
Value::Object(object) => {
object.addr()
}
Value::Table(table) => table.addr(),
Value::Object(object) => object.addr(),
#[cfg(feature = "vector3")]
Value::Vector(vec) => {
let mut hasher = DefaultHasher::new();
@ -99,7 +86,7 @@ pub struct Table {
array: Vec<Value>,
table_bounds: std::ops::Range<usize>,
table_count: usize, // number of elements in table
pub meta: Option<Gc<Table>>
pub meta: Option<Gc<Table>>,
}
impl Traverse for Table {
@ -139,9 +126,11 @@ impl Table {
self.table.len() / 3
}
fn ensure_table(&mut self) {
if self.table_count > self.table_upper() { // free space is short
if self.table_count > self.table_upper() {
// free space is short
self.exchange_table((self.table.len() + 4) * 2);
} else if self.table_count < self.table_lower() { // too much free space
} else if self.table_count < self.table_lower() {
// too much free space
self.exchange_table(self.table.len() / 2)
}
self.table_bounds = self.table_lower()..self.table_upper()
@ -151,26 +140,30 @@ impl Table {
assert_ne!(index, Value::Nil);
#[cfg(feature = "assertions")]
assert_ne!(item, Value::Nil);
if !self.table_bounds.contains(&self.table_count) { // make sure the table is appropriately sized
if !self.table_bounds.contains(&self.table_count) {
// make sure the table is appropriately sized
self.ensure_table();
}
let range = self.table.len();
let home = hash(&index) % range;
if self.table[home].index == Value::Nil { // attempt to place it directly in an empty space
if self.table[home].index == Value::Nil {
// attempt to place it directly in an empty space
// INSERT
self.table[home].home = home;
self.table[home].item = item; // home.item.soft_drop()
self.table[home].index = index;
self.table[home].displacement = self.table[home].displacement.max(0);
self.table_count += 1;
} else if self.table[home].index == index { // attempt to replace it directly
} else if self.table[home].index == index {
// attempt to replace it directly
// REPLACE
self.table[home].item = item; // home.item.soft_drop()
} else {
// attempt to replace it in a collided neighbour location
for i in 1..self.table[home].displacement + 1 {
let neighbour = &mut self.table[(home + i as usize) % range]; // todo: is mod expensive?
if neighbour.index == index { // found where it was displaced to
if neighbour.index == index {
// found where it was displaced to
// REPLACE
neighbour.item = item;
return;
@ -179,7 +172,8 @@ impl Table {
// at this point it must be added, probe to place in an empty space
for j in self.table[home].displacement + 1..Displacement::MAX - 1 {
let neighbour = &mut self.table[(home + j as usize) % range];
if neighbour.index == Value::Nil { // new empty slot hooray!
if neighbour.index == Value::Nil {
// new empty slot hooray!
// INSERT
neighbour.home = home;
neighbour.item = item;
@ -274,7 +268,8 @@ impl Table {
}
pub fn set(&mut self, index: Value, item: Value) -> Result<(), RunError> {
match index {
Value::Integer(index) => { // This is an integer index, try the array first
Value::Integer(index) => {
// This is an integer index, try the array first
match item {
Value::Nil => {
if (0..self.array.len() + 1).contains(&(index as usize)) {
@ -285,7 +280,7 @@ impl Table {
}
}
self.rem_table(Value::Integer(index));
},
}
item => {
if (0..self.array.len() + 1).contains(&(index as usize)) {
if self.array.len() == index as usize {
@ -299,14 +294,20 @@ impl Table {
}
}
Ok(())
},
Value::Nil => Err(RunError("Attempt to set new index of tabel with key: nil".to_string())),
index => Ok(self.set_table(index,item))
}
Value::Nil => Err(RunError(
"Attempt to set new index of tabel with key: nil".to_string(),
)),
index => Ok(self.set_table(index, item)),
}
}
pub fn get(&self, index: Value) -> Result<Value, RunError> {
match index {
Value::Integer(index) => Ok(self.array.get(index as usize).unwrap_or(&Value::Nil).clone()),
Value::Integer(index) => Ok(self
.array
.get(index as usize)
.unwrap_or(&Value::Nil)
.clone()),
Value::Nil => Err(RunError("Attempt to index table with key: nil".to_string())),
index => {
let location = hash(&index) % self.table.len();
@ -317,7 +318,7 @@ impl Table {
for i in 1..home.displacement as usize {
let neighbour = &self.table[location + i];
if neighbour.index == index {
return Ok(neighbour.item.clone())
return Ok(neighbour.item.clone());
}
}
Ok(Value::Nil)
@ -334,7 +335,7 @@ impl Table {
array: Vec::new(),
table_count: 0,
table_bounds: (0..0).into(),
meta: None
meta: None,
}
}
}

View file

@ -1 +1,5 @@
do end
if true then
print("whats good")
else
print("oh no")
end