use std::collections::HashMap;
use std::ops::Index;
use crate::{Bytecode, Opcode, Resolve, Str};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
pub struct Reg(pub u32);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct RefInt(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct RefFloat(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct RefBytes(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct RefString(pub usize);
impl RefString {
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct ValBool(pub bool);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)]
pub struct RefGlobal(pub usize);
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ObjField {
pub name: RefString,
pub t: RefType,
}
impl ObjField {
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct RefField(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ObjProto {
pub name: RefString,
pub findex: RefFun,
pub pindex: i32,
}
impl ObjProto {
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumConstruct {
pub name: RefString,
pub params: Vec<RefType>,
}
impl EnumConstruct {
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RefEnumConstruct(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeFun {
pub args: Vec<RefType>,
pub ret: RefType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeObj {
pub name: RefString,
pub super_: Option<RefType>,
pub global: RefGlobal,
pub own_fields: Vec<ObjField>,
pub protos: Vec<ObjProto>,
pub bindings: HashMap<RefField, RefFun>,
pub fields: Vec<ObjField>,
}
impl TypeObj {
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
pub fn get_static_type<'a>(&self, ctx: &'a Bytecode) -> Option<&'a TypeObj> {
if self.global.0 > 0 {
ctx.globals[self.global.0 - 1].as_obj(ctx)
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
Void,
UI8,
UI16,
I32,
I64,
F32,
F64,
Bool,
Bytes,
Dyn,
Fun(TypeFun),
Obj(TypeObj),
Array,
Type,
Ref(RefType),
Virtual {
fields: Vec<ObjField>,
},
DynObj,
Abstract {
name: RefString,
},
Enum {
name: RefString,
global: RefGlobal,
constructs: Vec<EnumConstruct>,
},
Null(RefType),
Method(TypeFun),
Struct(TypeObj),
Packed(RefType),
}
impl Type {
pub fn is_wrapper_type(&self) -> bool {
matches!(self, Type::Ref(_) | Type::Null(_) | Type::Packed(_))
}
pub fn get_type_obj(&self) -> Option<&TypeObj> {
match self {
Type::Obj(obj) => Some(obj),
Type::Struct(obj) => Some(obj),
_ => None,
}
}
pub fn get_type_obj_mut(&mut self) -> Option<&mut TypeObj> {
match self {
Type::Obj(obj) => Some(obj),
Type::Struct(obj) => Some(obj),
_ => None,
}
}
pub fn get_type_fun(&self) -> Option<&TypeFun> {
match self {
Type::Fun(fun) => Some(fun),
Type::Method(fun) => Some(fun),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct RefType(pub usize);
impl RefType {
pub fn is_void(&self) -> bool {
self.0 == 0
}
pub fn is_known(&self) -> bool {
self.0 <= 9 || self.0 == 11 || self.0 == 14
}
pub fn to_known(&self) -> Type {
match self.0 {
0 => Type::Void,
1 => Type::UI8,
2 => Type::UI16,
3 => Type::I32,
4 => Type::I64,
5 => Type::F32,
6 => Type::F64,
7 => Type::Bool,
8 => Type::Type,
9 => Type::Dyn,
11 => Type::Array,
14 => Type::Bytes,
_ => {
panic!("This not a known type")
}
}
}
pub fn as_fun<'a>(&self, ctx: &'a Bytecode) -> Option<&'a TypeFun> {
match ctx.get(*self) {
Type::Fun(fun) => Some(fun),
Type::Method(fun) => Some(fun),
_ => None,
}
}
pub fn as_obj<'a>(&self, ctx: &'a Bytecode) -> Option<&'a TypeObj> {
ctx.get(*self).get_type_obj()
}
pub fn field<'a>(&self, field: RefField, ctx: &'a Bytecode) -> Option<&'a ObjField> {
self.as_obj(ctx).map(|obj| &obj.fields[field.0])
}
pub fn method<'a>(&self, meth: usize, ctx: &'a Bytecode) -> Option<&'a ObjProto> {
self.as_obj(ctx).map(|obj| &obj.protos[meth])
}
}
#[derive(Debug, Clone)]
pub struct Native {
pub name: RefString,
pub lib: RefString,
pub t: RefType,
pub findex: RefFun,
}
impl Native {
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
pub fn lib(&self, code: &Bytecode) -> Str {
code.get(self.lib)
}
pub fn ty<'a>(&self, code: &'a Bytecode) -> &'a TypeFun {
self.t.as_fun(code).expect("Unknown type ?")
}
pub fn args<'a>(&self, code: &'a Bytecode) -> &'a [RefType] {
&self.ty(code).args
}
pub fn ret<'a>(&self, code: &'a Bytecode) -> &'a Type {
code.get(self.ty(code).ret)
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub name: RefString,
pub t: RefType,
pub findex: RefFun,
pub regs: Vec<RefType>,
pub ops: Vec<Opcode>,
pub debug_info: Option<Vec<(usize, usize)>>,
pub assigns: Option<Vec<(RefString, usize)>>,
pub parent: Option<RefType>,
}
impl Function {
pub fn regtype(&self, reg: Reg) -> RefType {
self[reg]
}
pub fn name(&self, code: &Bytecode) -> Str {
code.get(self.name)
}
pub fn ty<'a>(&self, code: &'a Bytecode) -> &'a TypeFun {
self.t.as_fun(code).expect("Unknown type ?")
}
pub fn args<'a>(&self, code: &'a Bytecode) -> &'a [RefType] {
&self.ty(code).args
}
pub fn ret<'a>(&self, code: &'a Bytecode) -> &'a Type {
code.index(self.ty(code).ret)
}
pub fn arg_name(&self, code: &Bytecode, pos: usize) -> Option<Str> {
self.assigns.as_ref().and_then(|a| {
a.iter()
.filter(|&&(_, i)| i == 0)
.enumerate()
.find_map(|(j, &(s, _))| {
if j == pos {
Some(code[s].clone())
} else {
None
}
})
})
}
pub fn var_name(&self, code: &Bytecode, pos: usize) -> Option<Str> {
self.assigns.as_ref().and_then(|a| {
a.iter().find_map(|&(s, i)| {
if pos + 1 == i {
Some(code[s].clone())
} else {
None
}
})
})
}
pub fn is_method(&self) -> bool {
self.parent
.map(|parent| !self.regs.is_empty() && self.regs[0] == parent)
.unwrap_or(false)
}
}
impl Index<Reg> for Function {
type Output = RefType;
fn index(&self, index: Reg) -> &Self::Output {
&self.regs[index.0 as usize]
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
pub struct RefFun(pub usize);
impl RefFun {
pub fn as_fn<'a>(&self, code: &'a Bytecode) -> Option<&'a Function> {
code.get(*self).as_fn()
}
pub fn name(&self, code: &Bytecode) -> Str {
match code.get(*self) {
FunPtr::Fun(fun) => fun.name(code),
FunPtr::Native(n) => n.name(code),
}
}
pub fn ty<'a>(&self, code: &'a Bytecode) -> &'a TypeFun {
match code.get(*self) {
FunPtr::Fun(fun) => fun.ty(code),
FunPtr::Native(n) => n.ty(code),
}
}
pub fn args<'a>(&self, code: &'a Bytecode) -> &'a [RefType] {
&self.ty(code).args
}
pub fn ret<'a>(&self, code: &'a Bytecode) -> &'a Type {
code.get(self.ty(code).ret)
}
}
#[derive(Debug, Copy, Clone)]
pub enum FunPtr<'a> {
Fun(&'a Function),
Native(&'a Native),
}
impl<'a> FunPtr<'a> {
pub fn as_fn(self: FunPtr<'a>) -> Option<&'a Function> {
match self {
FunPtr::Fun(fun) => Some(fun),
FunPtr::Native(_) => None,
}
}
pub fn findex(&self) -> RefFun {
match self {
FunPtr::Fun(fun) => fun.findex,
FunPtr::Native(n) => n.findex,
}
}
pub fn name(&self, code: &Bytecode) -> Str {
match *self {
FunPtr::Fun(fun) => fun.name(code),
FunPtr::Native(n) => n.name(code),
}
}
pub fn is_fun(&self) -> bool {
matches!(self, FunPtr::Fun(_))
}
pub fn is_native(&self) -> bool {
matches!(self, FunPtr::Native(_))
}
}
#[derive(Debug, Clone)]
pub struct ConstantDef {
pub global: RefGlobal,
pub fields: Vec<usize>,
}