Yay! Class lookup and allocation e2e.

This commit is contained in:
Jesse Brault 2026-03-13 20:21:49 -05:00
parent 0e24ce1784
commit f7e8cef380
14 changed files with 343 additions and 112 deletions

View File

@ -41,6 +41,9 @@ pub fn std_core_println(args: &[Value]) -> Result<Value, Box<dyn Error>> {
None => Err(Box::new(StdCoreError::new("Missing to_print arg"))), None => Err(Box::new(StdCoreError::new("Missing to_print arg"))),
Some(to_print) => { Some(to_print) => {
match to_print { match to_print {
Value::Object(o) => {
println!("{}", o.borrow());
}
Value::Int(i) => { Value::Int(i) => {
println!("{}", i); println!("{}", i);
} }

View File

@ -12,6 +12,7 @@ use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand; use dvm_lib::vm::operand::Operand;
use dvm_lib::vm::{CallStack, DvmContext, call}; use dvm_lib::vm::{CallStack, DvmContext, call};
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc;
pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usize) { pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usize) {
let input = std::fs::read_to_string(script).unwrap(); let input = std::fs::read_to_string(script).unwrap();
@ -51,7 +52,7 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz
} }
} }
let mut ir_functions = compilation_unit.to_ir(&symbol_table); let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table);
if show_ir { if show_ir {
for ir_function in &ir_functions { for ir_function in &ir_functions {
@ -68,6 +69,11 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz
functions.push(function); functions.push(function);
} }
let classes = ir_classes
.iter()
.map(|ir_class| ir_class.to_vm_class())
.collect::<Vec<_>>();
if show_asm { if show_asm {
for function in &functions { for function in &functions {
println!("{}", function); println!("{}", function);
@ -85,6 +91,12 @@ pub fn run(script: &PathBuf, show_ir: bool, show_asm: bool, register_count: usiz
.insert(function.name_owned(), function); .insert(function.name_owned(), function);
} }
for class in classes {
dvm_context
.classes_mut()
.insert(class.fqn().into(), Rc::new(class));
}
for (name, content) in &constants_table.string_constants() { for (name, content) in &constants_table.string_constants() {
dvm_context.constants_mut().insert( dvm_context.constants_mut().insert(
name.clone(), name.clone(),

View File

@ -1,10 +1,13 @@
use crate::ast::constructor::Constructor; use crate::ast::constructor::Constructor;
use crate::ast::field::Field; use crate::ast::field::Field;
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::diagnostic::{Diagnostic, SecondaryLabel};
use crate::ir::ir_class::{IrClass, IrField};
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::{SymbolInsertError, SymbolTable};
use std::cell::RefCell; use std::cell::RefCell;
@ -43,16 +46,17 @@ impl Class {
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
fqn_context: &mut FqnContext, fqn_context: &mut FqnContext,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
// 0. Push class name on fqn
fqn_context.push(self.declared_name.clone());
// 1. insert class symbol // 1. insert class symbol
let to_insert = ClassSymbol::new( let to_insert = ClassSymbol::new(
&self.declared_name, &self.declared_name,
self.declared_name_source_range.clone(), self.declared_name_source_range.clone(),
fqn_context.resolve(&self.declared_name),
false, false,
); );
// 1a. Push class name on fqn
fqn_context.push(self.declared_name.clone());
let class_symbol = symbol_table let class_symbol = symbol_table
.insert_class_symbol(to_insert) .insert_class_symbol(to_insert)
.map_err(|e| match e { .map_err(|e| match e {
@ -260,7 +264,7 @@ impl Class {
} }
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> { pub fn to_ir(&self, symbol_table: &SymbolTable) -> (IrClass, Vec<IrFunction>) {
let mut ir_functions: Vec<IrFunction> = vec![]; let mut ir_functions: Vec<IrFunction> = vec![];
if let Some(constructor) = &self.constructor { if let Some(constructor) = &self.constructor {
ir_functions.push(constructor.to_ir( ir_functions.push(constructor.to_ir(
@ -277,77 +281,23 @@ impl Class {
)); ));
} }
ir_functions let class_symbol = self.class_symbol.as_ref().unwrap().borrow();
}
}
#[cfg(test)] let ir_class = IrClass::new(
mod tests { class_symbol.declared_name_owned(),
use super::*; fqn_parts_to_string(class_symbol.fqn_parts()).into(),
use crate::constants_table::ConstantsTable; self.fields
use crate::parser::parse_compilation_unit; .iter()
.map(|field| {
#[test] IrField::new(
fn complete_example_no_diagnostics() { field.declared_name().into(),
let parse_result = parse_compilation_unit( field.field_symbol().borrow().field_index(),
" field.field_symbol().borrow().type_info().clone(),
class Foo )
mut bar: Int = 42 })
.collect(),
ctor(_bar: Int)
end
fn baz() -> Int
bar
end
end
class Qux
fn foo() -> Foo
Foo(42)
end
end
",
); );
let mut compilation_unit = match parse_result { (ir_class, ir_functions)
Ok(compilation_unit) => compilation_unit,
Err(diagnostics) => {
panic!("{:?}", diagnostics);
}
};
let mut symbol_table = SymbolTable::new();
match compilation_unit.gather_declared_names(&mut symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
panic!("{:?}", diagnostics);
}
}
match compilation_unit.check_name_usages(&symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
panic!("{:?}", diagnostics);
}
}
match compilation_unit.type_check(&symbol_table) {
Ok(_) => {}
Err(diagnostics) => {
panic!("{:?}", diagnostics);
}
}
let mut ir_functions = compilation_unit.to_ir(&symbol_table);
for ir_function in &mut ir_functions {
println!("{}", ir_function);
}
let mut constants_table = ConstantsTable::new();
for ir_function in &mut ir_functions {
let (_, stack_size) = ir_function.assign_registers(8);
let vm_function = ir_function.assemble(stack_size, &mut constants_table);
println!("{}", vm_function)
}
} }
} }

View File

@ -3,6 +3,7 @@ use crate::ast::extern_function::ExternFunction;
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::ir::ir_class::IrClass;
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
@ -138,16 +139,21 @@ impl CompilationUnit {
} }
} }
pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec<IrFunction> { pub fn to_ir(&self, symbol_table: &SymbolTable) -> (Vec<IrClass>, Vec<IrFunction>) {
let mut functions: Vec<IrFunction> = vec![]; let mut functions: Vec<IrFunction> = vec![];
let mut classes: Vec<IrClass> = vec![];
self.functions self.functions
.iter() .iter()
.map(|f| f.to_ir(symbol_table, None)) .map(|f| f.to_ir(symbol_table, None))
.for_each(|f| functions.push(f)); .for_each(|f| functions.push(f));
self.classes
.iter() for class in &self.classes {
.flat_map(|c| c.to_ir(symbol_table)) let (class, mut class_functions) = class.to_ir(symbol_table);
.for_each(|f| functions.push(f)); functions.append(&mut class_functions);
functions classes.push(class);
}
(classes, functions)
} }
} }

View File

@ -10,7 +10,7 @@ pub mod extern_function;
pub mod field; pub mod field;
pub mod fqn; pub mod fqn;
pub mod fqn_context; pub mod fqn_context;
mod fqn_util; pub mod fqn_util;
pub mod function; pub mod function;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;

View File

@ -0,0 +1,61 @@
use crate::ast::fqn_util::fqn_parts_to_string;
use crate::type_info::TypeInfo;
use dvm_lib::vm::class::{Class, Field};
use dvm_lib::vm::type_info::TypeInfo as VmTypeInfo;
use std::rc::Rc;
pub struct IrClass {
declared_name: Rc<str>,
fqn: Rc<str>,
fields: Vec<IrField>,
}
impl IrClass {
pub fn new(declared_name: Rc<str>, fqn: Rc<str>, fields: Vec<IrField>) -> Self {
Self {
declared_name,
fqn,
fields,
}
}
pub fn to_vm_class(&self) -> Class {
Class::new(
self.declared_name.clone(),
self.fqn.clone(),
self.fields.iter().map(IrField::to_vm_field).collect(),
)
}
}
pub struct IrField {
debug_name: Rc<str>,
field_index: usize,
type_info: TypeInfo,
}
impl IrField {
pub fn new(debug_name: Rc<str>, field_index: usize, type_info: TypeInfo) -> Self {
Self {
debug_name,
field_index,
type_info,
}
}
pub fn to_vm_field(&self) -> Field {
Field::new(
self.debug_name.clone(),
self.field_index,
match &self.type_info {
TypeInfo::Integer => VmTypeInfo::Int,
TypeInfo::Double => VmTypeInfo::Double,
TypeInfo::String => VmTypeInfo::String,
TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance(
fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(),
),
_ => panic!(),
},
)
}
}

View File

@ -4,6 +4,7 @@ pub mod ir_allocate;
pub mod ir_assign; pub mod ir_assign;
pub mod ir_block; pub mod ir_block;
pub mod ir_call; pub mod ir_call;
pub mod ir_class;
pub mod ir_expression; pub mod ir_expression;
pub mod ir_function; pub mod ir_function;
pub mod ir_get_field_ref; pub mod ir_get_field_ref;

View File

@ -10,6 +10,7 @@ use std::rc::Rc;
pub struct ClassSymbol { pub struct ClassSymbol {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>, constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
@ -20,11 +21,13 @@ impl ClassSymbol {
pub fn new( pub fn new(
declared_name: &Rc<str>, declared_name: &Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
) -> Self { ) -> Self {
Self { Self {
declared_name: declared_name.clone(), declared_name: declared_name.clone(),
declared_name_source_range, declared_name_source_range,
fqn_parts,
is_extern, is_extern,
constructor_symbol: None, constructor_symbol: None,
fields: HashMap::new(), fields: HashMap::new(),
@ -32,6 +35,10 @@ impl ClassSymbol {
} }
} }
pub fn fqn_parts(&self) -> &[Rc<str>] {
&self.fqn_parts
}
pub fn set_constructor_symbol( pub fn set_constructor_symbol(
&mut self, &mut self,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>, constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,

57
dvm-lib/src/vm/class.rs Normal file
View File

@ -0,0 +1,57 @@
use crate::vm::type_info::TypeInfo;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
pub struct Class {
declared_name: Rc<str>,
fqn: Rc<str>,
fields: Vec<Field>,
}
impl Class {
pub fn new(declared_name: Rc<str>, fqn: Rc<str>, fields: Vec<Field>) -> Self {
Self {
declared_name,
fqn,
fields,
}
}
pub fn fqn(&self) -> &str {
&self.fqn
}
pub fn fields(&self) -> &[Field] {
&self.fields
}
}
impl Debug for Class {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.fqn)
}
}
impl PartialEq for Class {
fn eq(&self, other: &Self) -> bool {
self.fqn == other.fqn
}
}
impl Eq for Class {}
pub struct Field {
debug_name: Rc<str>,
field_index: usize,
type_info: TypeInfo,
}
impl Field {
pub fn new(debug_name: Rc<str>, field_index: usize, type_info: TypeInfo) -> Self {
Self {
debug_name,
field_index,
type_info,
}
}
}

View File

@ -1,7 +1,9 @@
use crate::instruction::Instruction; use crate::instruction::Instruction;
use crate::platform_function::PlatformFunction; use crate::platform_function::PlatformFunction;
use crate::vm::class::Class;
use crate::vm::constant::Constant; use crate::vm::constant::Constant;
use crate::vm::function::Function; use crate::vm::function::Function;
use crate::vm::object::get_object;
use crate::vm::operand::Operand; use crate::vm::operand::Operand;
use crate::vm::operand_helpers::{ use crate::vm::operand_helpers::{
add_operand_to_value, move_operand_to_value, multiply_operand_to_value, push_operand_to_value, add_operand_to_value, move_operand_to_value, multiply_operand_to_value, push_operand_to_value,
@ -9,19 +11,23 @@ use crate::vm::operand_helpers::{
}; };
use crate::vm::util::*; use crate::vm::util::*;
use crate::vm::value::Value; use crate::vm::value::Value;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
pub mod class;
pub mod constant; pub mod constant;
pub mod function; pub mod function;
pub mod object; pub mod object;
pub mod operand; pub mod operand;
mod operand_helpers; mod operand_helpers;
pub mod type_info;
mod util; mod util;
pub mod value; pub mod value;
pub struct DvmContext { pub struct DvmContext {
classes: HashMap<Rc<str>, Rc<Class>>,
functions: HashMap<Rc<str>, Function>, functions: HashMap<Rc<str>, Function>,
platform_functions: HashMap<Rc<str>, PlatformFunction>, platform_functions: HashMap<Rc<str>, PlatformFunction>,
constants: HashMap<Rc<str>, Constant>, constants: HashMap<Rc<str>, Constant>,
@ -30,12 +36,21 @@ pub struct DvmContext {
impl DvmContext { impl DvmContext {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
classes: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
platform_functions: HashMap::new(), platform_functions: HashMap::new(),
constants: HashMap::new(), constants: HashMap::new(),
} }
} }
pub fn classes(&self) -> &HashMap<Rc<str>, Rc<Class>> {
&self.classes
}
pub fn classes_mut(&mut self) -> &mut HashMap<Rc<str>, Rc<Class>> {
&mut self.classes
}
pub fn functions(&self) -> &HashMap<Rc<str>, Function> { pub fn functions(&self) -> &HashMap<Rc<str>, Function> {
&self.functions &self.functions
} }
@ -320,11 +335,15 @@ pub fn call<'a>(
/* Object instructions */ /* Object instructions */
Instruction::Allocate(class_fqn, destination) => { Instruction::Allocate(class_fqn, destination) => {
// let class = context.classes().get(class_fqn).expect("No such class loaded: {}") let class = context
// let object: Gc<GcCell<Object>> = Object::new(class, heap) .classes()
// let value = Value::Object(object) .get(class_fqn)
// put_value(registers, call_stack.top_mut(), destination, result) .expect(&format!("No such class loaded: {}", class_fqn));
todo!() let object = get_object(class);
let as_rc = Rc::new(RefCell::new(object));
let value = Value::Object(as_rc);
put_value(registers, call_stack.top_mut(), destination, value);
call_stack.top_mut().increment_ip();
} }
Instruction::GetFieldPointer(self_location, field_index, destination) => { Instruction::GetFieldPointer(self_location, field_index, destination) => {

View File

@ -1,6 +1,10 @@
use crate::vm::class::Class;
use crate::vm::value::Value; use crate::vm::value::Value;
use std::alloc::{Layout, alloc}; use std::alloc::{Layout, alloc};
use std::fmt::{Display, Formatter};
use std::rc::Rc;
#[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct Object { pub struct Object {
header: ObjectHeader, header: ObjectHeader,
@ -8,6 +12,10 @@ pub struct Object {
} }
impl Object { impl Object {
pub fn header(&self) -> &ObjectHeader {
&self.header
}
pub fn fields(&self) -> &[Value] { pub fn fields(&self) -> &[Value] {
&self.fields &self.fields
} }
@ -17,22 +25,45 @@ impl Object {
} }
} }
pub struct ObjectHeader { impl PartialEq for Object {
field_count: usize, fn eq(&self, other: &Self) -> bool {
todo!("eq on Object not yet supported")
}
} }
fn get_object(field_count: usize) -> Box<Object> { impl Display for Object {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let self_class = &self.header.self_class;
write!(f, "{}@{:p}", self_class.fqn(), &self)
}
}
#[derive(Debug)]
pub struct ObjectHeader {
self_class: Rc<Class>,
}
impl ObjectHeader {
pub fn self_class(&self) -> &Rc<Class> {
&self.self_class
}
}
// this will be more tied to the gc later
pub fn get_object(class: &Rc<Class>) -> Box<Object> {
let field_count = class.fields().len();
let (layout, fields_base) = Layout::array::<Value>(field_count) let (layout, fields_base) = Layout::array::<Value>(field_count)
.and_then(|fields_array_layout| Layout::new::<ObjectHeader>().extend(fields_array_layout)) .and_then(|fields_array_layout| Layout::new::<ObjectHeader>().extend(fields_array_layout))
.unwrap(); .unwrap();
println!("{:?}", layout);
let ptr = unsafe { alloc(layout) }; let ptr = unsafe { alloc(layout) };
if ptr.is_null() { if ptr.is_null() {
panic!("failed to allocate memory"); panic!("failed to allocate memory");
} }
unsafe { unsafe {
ptr.cast::<ObjectHeader>() ptr.cast::<ObjectHeader>().write(ObjectHeader {
.write(ObjectHeader { field_count }); self_class: class.clone(),
});
let fields_ptr = ptr.add(fields_base).cast::<Value>(); let fields_ptr = ptr.add(fields_base).cast::<Value>();
// initialize each field to Value::Null // initialize each field to Value::Null
for i in 0..field_count { for i in 0..field_count {
@ -44,17 +75,3 @@ fn get_object(field_count: usize) -> Box<Object> {
Box::from_raw(std::ptr::slice_from_raw_parts(ptr as *mut Value, field_count) as *mut Object) Box::from_raw(std::ptr::slice_from_raw_parts(ptr as *mut Value, field_count) as *mut Object)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn melt_the_processor() {
let mut o = get_object(3);
assert_eq!(o.header.field_count, 3);
assert_eq!(o.fields.len(), 3);
o.fields.fill(Value::Int(42));
assert!(o.fields.iter().all(|v| *v == Value::Int(42)));
}
}

View File

@ -0,0 +1,41 @@
use crate::vm::class::Class;
use crate::vm::value::Value;
use std::collections::HashMap;
use std::rc::Rc;
pub enum TypeInfo {
ClassInstance(Rc<str>),
Int,
Double,
String,
}
impl TypeInfo {
pub fn is_instance_of(&self, value: &Value, classes: &HashMap<Rc<str>, Rc<Class>>) -> bool {
match self {
TypeInfo::ClassInstance(class_fqn) => match value {
Value::Object(o) => {
let o_ref = o.borrow();
let o_self_class = o_ref.header().self_class();
let check_class = classes.get(class_fqn);
if check_class.is_none() {
panic!("No such class {}", class_fqn);
}
*check_class.unwrap() == *o_self_class
}
_ => false,
},
TypeInfo::Int => {
matches!(value, Value::Int(_))
}
TypeInfo::Double => {
matches!(value, Value::Double(_))
}
TypeInfo::String => {
matches!(value, Value::String(_))
}
}
}
}

View File

@ -1,10 +1,11 @@
use crate::vm::object::Object; use crate::vm::object::Object;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::{Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Value { pub enum Value {
Object(Rc<RefCell<Box<Object>>>),
Int(i32), Int(i32),
Double(f64), Double(f64),
String(Rc<str>), String(Rc<str>),
@ -39,18 +40,27 @@ impl Value {
} }
} }
pub fn unwrap_object(&self) -> &Rc<RefCell<Object>> { pub fn unwrap_object(&self) -> &Rc<RefCell<Box<Object>>> {
todo!() match self {
Value::Object(o) => o,
_ => panic!("Attempt to unwrap Object; found {:?}", self),
}
} }
pub fn unwrap_object_mut(&mut self) -> &mut Rc<RefCell<Object>> { pub fn unwrap_object_mut(&mut self) -> &mut Rc<RefCell<Box<Object>>> {
todo!() match self {
Value::Object(o) => o,
_ => panic!("Attempt to unwrap Object; found {:?}", self),
}
} }
} }
impl Display for Value { impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Value::Object(o) => {
write!(f, "{}", o.borrow())
}
Value::Int(i) => { Value::Int(i) => {
write!(f, "{}", i) write!(f, "{}", i)
} }

View File

@ -4,6 +4,7 @@ mod e2e_tests {
use dmc_lib::diagnostic::Diagnostic; use dmc_lib::diagnostic::Diagnostic;
use dmc_lib::parser::parse_compilation_unit; use dmc_lib::parser::parse_compilation_unit;
use dmc_lib::symbol_table::SymbolTable; use dmc_lib::symbol_table::SymbolTable;
use dvm_lib::vm::class::Class;
use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::constant::{Constant, StringConstant};
use dvm_lib::vm::function::Function; use dvm_lib::vm::function::Function;
use dvm_lib::vm::operand::Operand; use dvm_lib::vm::operand::Operand;
@ -57,7 +58,7 @@ mod e2e_tests {
} }
} }
let mut ir_functions = compilation_unit.to_ir(&symbol_table); let (ir_classes, mut ir_functions) = compilation_unit.to_ir(&symbol_table);
let mut functions: Vec<Function> = vec![]; let mut functions: Vec<Function> = vec![];
let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
@ -68,8 +69,19 @@ mod e2e_tests {
functions.push(function); functions.push(function);
} }
let mut classes: Vec<Class> = vec![];
for ir_class in &ir_classes {
classes.push(ir_class.to_vm_class());
}
let mut dvm_context = DvmContext::new(); let mut dvm_context = DvmContext::new();
for class in classes {
dvm_context
.classes_mut()
.insert(class.fqn().into(), Rc::new(class));
}
for function in functions { for function in functions {
dvm_context dvm_context
.functions_mut() .functions_mut()
@ -201,6 +213,41 @@ mod e2e_tests {
Value::Double(1.0), Value::Double(1.0),
) )
} }
#[test]
fn two_classes() {
let context = prepare_context(
"
class Foo
mut bar: Int = 42
ctor(_bar: Int)
end
fn baz() -> Int
bar
end
end
class Qux
fn foo() -> Foo
Foo(42)
end
end
fn foo(n: Int) -> Foo
Foo(n)
end
",
);
let result = get_result(&context, "foo", &[Value::Int(42)]);
assert!(result.is_some());
let value = result.unwrap();
assert!(matches!(value, Value::Object(_)));
let o = value.unwrap_object().borrow();
assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42);
}
} }
#[cfg(test)] #[cfg(test)]