use std::sync::{atomic::AtomicBool, Arc, OnceLock};
use crate::{
metadata::{
marshalling::MarshallingInfo,
tables::{Param, ParamRc},
token::Token,
typesystem::{
CilFlavor, CilPrimitive, CilTypeRef, CompleteTypeSpec, TypeRegistry, TypeSource,
},
},
prelude::{CilPrimitiveData, CilPrimitiveKind, ParamAttributes},
test::{builders::FieldBuilder, factories::metadata::customattributes::get_test_type_registry},
};
#[derive(Debug, Clone, Copy)]
pub enum ParamDirection {
In,
Out,
InOut,
}
#[derive(Debug, Clone)]
pub enum ParamDefault {
Bool(bool),
I1(i8),
U1(u8),
I2(i16),
U2(u16),
I4(i32),
U4(u32),
I8(i64),
U8(u64),
R4(f32),
R8(f64),
String(String),
Null,
}
pub struct ParamBuilder {
rid: u32,
sequence: u32,
name: Option<String>,
flags: u32,
direction: ParamDirection,
default_value: Option<ParamDefault>,
marshal_info: Option<MarshallingInfo>,
is_optional: bool,
type_flavor: Option<CilFlavor>,
}
impl ParamBuilder {
pub fn new(sequence: u32, name: Option<String>) -> Self {
Self {
rid: sequence,
sequence,
name,
flags: 0,
direction: ParamDirection::In,
default_value: None,
marshal_info: None,
is_optional: false,
type_flavor: None,
}
}
pub fn return_value() -> Self {
Self::new(0, None)
}
pub fn input_param(sequence: u32, name: &str) -> Self {
Self::new(sequence, Some(name.to_string())).with_direction(ParamDirection::In)
}
pub fn output_param(sequence: u32, name: &str) -> Self {
Self::new(sequence, Some(name.to_string())).with_direction(ParamDirection::Out)
}
pub fn inout_param(sequence: u32, name: &str) -> Self {
Self::new(sequence, Some(name.to_string())).with_direction(ParamDirection::InOut)
}
pub fn with_rid(mut self, rid: u32) -> Self {
self.rid = rid;
self
}
pub fn with_direction(mut self, direction: ParamDirection) -> Self {
self.direction = direction;
self
}
pub fn with_default_value(mut self, default: ParamDefault) -> Self {
self.default_value = Some(default);
self
}
pub fn with_marshal_info(mut self, marshal: MarshallingInfo) -> Self {
self.marshal_info = Some(marshal);
self
}
pub fn with_optional(mut self) -> Self {
self.is_optional = true;
self
}
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = flags;
self
}
pub fn with_type_from_flavor(mut self, flavor: CilFlavor) -> Self {
self.type_flavor = Some(flavor);
self
}
pub fn with_primitive_type(mut self, kind: CilPrimitiveKind) -> Self {
self.type_flavor = Some(match kind {
CilPrimitiveKind::Boolean => CilFlavor::Boolean,
CilPrimitiveKind::Char => CilFlavor::Char,
CilPrimitiveKind::I1 => CilFlavor::I1,
CilPrimitiveKind::U1 => CilFlavor::U1,
CilPrimitiveKind::I2 => CilFlavor::I2,
CilPrimitiveKind::U2 => CilFlavor::U2,
CilPrimitiveKind::I4 => CilFlavor::I4,
CilPrimitiveKind::U4 => CilFlavor::U4,
CilPrimitiveKind::I8 => CilFlavor::I8,
CilPrimitiveKind::U8 => CilFlavor::U8,
CilPrimitiveKind::R4 => CilFlavor::R4,
CilPrimitiveKind::R8 => CilFlavor::R8,
CilPrimitiveKind::I => CilFlavor::I,
CilPrimitiveKind::U => CilFlavor::U,
CilPrimitiveKind::String => CilFlavor::String,
CilPrimitiveKind::Object => CilFlavor::Object,
CilPrimitiveKind::Void => CilFlavor::Void,
CilPrimitiveKind::Null => CilFlavor::Object, CilPrimitiveKind::TypedReference => CilFlavor::Object, CilPrimitiveKind::ValueType => CilFlavor::ValueType,
_ => CilFlavor::Object, });
self
}
pub fn build(self, type_registry: Option<&Arc<TypeRegistry>>) -> ParamRc {
let mut flags = self.flags;
match self.direction {
ParamDirection::In => flags |= ParamAttributes::IN,
ParamDirection::Out => flags |= ParamAttributes::OUT,
ParamDirection::InOut => {
flags |= ParamAttributes::IN | ParamAttributes::OUT;
}
}
if self.is_optional {
flags |= ParamAttributes::OPTIONAL;
}
if self.default_value.is_some() {
flags |= ParamAttributes::HAS_DEFAULT;
}
if self.marshal_info.is_some() {
flags |= ParamAttributes::HAS_FIELD_MARSHAL;
}
let param = Arc::new(Param {
rid: self.rid,
token: Token::new(0x08000000 + self.rid),
offset: 0,
flags,
sequence: self.sequence,
name: self.name,
default: OnceLock::new(),
marshal: OnceLock::new(),
modifiers: Arc::new(boxcar::Vec::new()),
base: OnceLock::new(),
is_by_ref: AtomicBool::new(false),
custom_attributes: Arc::new(boxcar::Vec::new()),
});
if let Some(default) = self.default_value {
let primitive = match default {
ParamDefault::Bool(v) => CilPrimitive {
kind: CilPrimitiveKind::Boolean,
data: CilPrimitiveData::Boolean(v),
},
ParamDefault::I1(v) => CilPrimitive {
kind: CilPrimitiveKind::I1,
data: CilPrimitiveData::I1(v),
},
ParamDefault::U1(v) => CilPrimitive {
kind: CilPrimitiveKind::U1,
data: CilPrimitiveData::U1(v),
},
ParamDefault::I2(v) => CilPrimitive {
kind: CilPrimitiveKind::I2,
data: CilPrimitiveData::I2(v),
},
ParamDefault::U2(v) => CilPrimitive {
kind: CilPrimitiveKind::U2,
data: CilPrimitiveData::U2(v),
},
ParamDefault::I4(v) => CilPrimitive {
kind: CilPrimitiveKind::I4,
data: CilPrimitiveData::I4(v),
},
ParamDefault::U4(v) => CilPrimitive {
kind: CilPrimitiveKind::U4,
data: CilPrimitiveData::U4(v),
},
ParamDefault::I8(v) => CilPrimitive {
kind: CilPrimitiveKind::I8,
data: CilPrimitiveData::I8(v),
},
ParamDefault::U8(v) => CilPrimitive {
kind: CilPrimitiveKind::U8,
data: CilPrimitiveData::U8(v),
},
ParamDefault::R4(v) => CilPrimitive {
kind: CilPrimitiveKind::R4,
data: CilPrimitiveData::R4(v),
},
ParamDefault::R8(v) => CilPrimitive {
kind: CilPrimitiveKind::R8,
data: CilPrimitiveData::R8(v),
},
ParamDefault::String(v) => CilPrimitive {
kind: CilPrimitiveKind::String,
data: CilPrimitiveData::String(v),
},
ParamDefault::Null => CilPrimitive {
kind: CilPrimitiveKind::Class,
data: CilPrimitiveData::None,
},
};
let _ = param.default.set(primitive);
}
if let Some(marshal) = self.marshal_info {
let _ = param.marshal.set(marshal);
}
if let Some(flavor) = self.type_flavor {
let registry = type_registry
.cloned()
.unwrap_or_else(get_test_type_registry);
let param_type = match flavor {
CilFlavor::Boolean => registry.get_primitive(CilPrimitiveKind::Boolean).unwrap(),
CilFlavor::Char => registry.get_primitive(CilPrimitiveKind::Char).unwrap(),
CilFlavor::I1 => registry.get_primitive(CilPrimitiveKind::I1).unwrap(),
CilFlavor::U1 => registry.get_primitive(CilPrimitiveKind::U1).unwrap(),
CilFlavor::I2 => registry.get_primitive(CilPrimitiveKind::I2).unwrap(),
CilFlavor::U2 => registry.get_primitive(CilPrimitiveKind::U2).unwrap(),
CilFlavor::I4 => registry.get_primitive(CilPrimitiveKind::I4).unwrap(),
CilFlavor::U4 => registry.get_primitive(CilPrimitiveKind::U4).unwrap(),
CilFlavor::I8 => registry.get_primitive(CilPrimitiveKind::I8).unwrap(),
CilFlavor::U8 => registry.get_primitive(CilPrimitiveKind::U8).unwrap(),
CilFlavor::R4 => registry.get_primitive(CilPrimitiveKind::R4).unwrap(),
CilFlavor::R8 => registry.get_primitive(CilPrimitiveKind::R8).unwrap(),
CilFlavor::I => registry.get_primitive(CilPrimitiveKind::I).unwrap(),
CilFlavor::U => registry.get_primitive(CilPrimitiveKind::U).unwrap(),
CilFlavor::String => registry.get_primitive(CilPrimitiveKind::String).unwrap(),
CilFlavor::Object => registry.get_primitive(CilPrimitiveKind::Object).unwrap(),
CilFlavor::Void => registry.get_primitive(CilPrimitiveKind::Void).unwrap(),
CilFlavor::Class => {
registry
.get_or_create_type(&CompleteTypeSpec {
token_init: None,
flavor: CilFlavor::Class,
namespace: "System".to_string(),
name: "Type".to_string(),
source: TypeSource::Unknown,
generic_args: None,
base_type: None,
flags: None,
})
.unwrap()
}
CilFlavor::ValueType => {
let enum_base = registry
.get_or_create_type(&CompleteTypeSpec {
token_init: None,
flavor: CilFlavor::Class,
namespace: "System".to_string(),
name: "Enum".to_string(),
source: TypeSource::Unknown,
generic_args: None,
base_type: None,
flags: None,
})
.unwrap();
let int32_type = registry.get_primitive(CilPrimitiveKind::I4).unwrap();
let test_enum = registry
.get_or_create_type(&CompleteTypeSpec {
token_init: None,
flavor: CilFlavor::ValueType,
namespace: "System".to_string(),
name: "TestEnum".to_string(),
source: TypeSource::Unknown,
generic_args: None,
base_type: Some(enum_base),
flags: None,
})
.unwrap();
let value_field = FieldBuilder::new("value__", int32_type).build();
test_enum.fields.push(value_field);
test_enum
}
other_flavor => registry
.get_or_create_type(&CompleteTypeSpec {
token_init: None,
flavor: other_flavor,
namespace: "System".to_string(),
name: "TestType".to_string(),
source: TypeSource::Unknown,
generic_args: None,
base_type: None,
flags: None,
})
.unwrap(),
};
let _ = param.base.set(CilTypeRef::from(param_type));
}
param
}
}
impl Default for ParamBuilder {
fn default() -> Self {
Self::new(1, Some("param".to_string()))
}
}
pub struct ParamListBuilder {
params: Vec<ParamBuilder>,
include_return_param: bool,
}
impl ParamListBuilder {
pub fn new() -> Self {
Self {
params: Vec::new(),
include_return_param: false,
}
}
pub fn with_return_param(mut self) -> Self {
self.include_return_param = true;
self
}
pub fn add_param(mut self, param: ParamBuilder) -> Self {
self.params.push(param);
self
}
pub fn add_input_param(mut self, name: &str) -> Self {
let sequence = self.params.len() as u32 + 1;
self.params.push(ParamBuilder::input_param(sequence, name));
self
}
pub fn add_output_param(mut self, name: &str) -> Self {
let sequence = self.params.len() as u32 + 1;
self.params.push(ParamBuilder::output_param(sequence, name));
self
}
pub fn build(self, type_registry: Option<&Arc<TypeRegistry>>) -> Arc<boxcar::Vec<ParamRc>> {
let params = Arc::new(boxcar::Vec::new());
if self.include_return_param {
params.push(ParamBuilder::return_value().build(type_registry));
}
for param_builder in self.params {
params.push(param_builder.build(type_registry));
}
params
}
}
impl Default for ParamListBuilder {
fn default() -> Self {
Self::new()
}
}