use crate::{AnalysedTypeWithUnit, ComponentDependencyKey, ParsedFunctionSite, VariableId};
use bincode::{Decode, Encode};
use golem_wasm_ast::analysis::AnalysedType;
use golem_wasm_rpc::ValueAndType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
pub enum RibIR {
PushLit(ValueAndType),
AssignVar(VariableId),
LoadVar(VariableId),
CreateAndPushRecord(AnalysedType),
UpdateRecord(String),
PushList(AnalysedType, usize),
PushTuple(AnalysedType, usize),
PushSome(AnalysedType),
PushNone(Option<AnalysedType>), PushOkResult(AnalysedType),
PushErrResult(AnalysedType),
PushFlag(ValueAndType), SelectField(String),
SelectIndex(usize), SelectIndexV1,
EqualTo,
GreaterThan,
And,
Or,
LessThan,
GreaterThanOrEqualTo,
LessThanOrEqualTo,
IsEmpty,
JumpIfFalse(InstructionId),
Jump(InstructionId),
Label(InstructionId),
Deconstruct,
CreateFunctionName(ParsedFunctionSite, FunctionReferenceType),
InvokeFunction(
ComponentDependencyKey,
InstanceVariable,
usize,
AnalysedTypeWithUnit,
),
PushVariant(String, AnalysedType), PushEnum(String, AnalysedType),
Throw(String),
GetTag,
Concat(usize),
Plus(AnalysedType),
Minus(AnalysedType),
Divide(AnalysedType),
Multiply(AnalysedType),
Negate,
ToIterator,
CreateSink(AnalysedType),
AdvanceIterator,
PushToSink,
SinkToList,
Length,
GenerateWorkerName(Option<VariableId>),
}
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
pub enum InstanceVariable {
WitResource(VariableId),
WitWorker(VariableId),
}
impl RibIR {
pub fn get_instruction_id(&self) -> Option<InstructionId> {
match self {
RibIR::Label(id) => Some(id.clone()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
pub enum FunctionReferenceType {
Function { function: String },
RawResourceConstructor { resource: String },
RawResourceDrop { resource: String },
RawResourceMethod { resource: String, method: String },
RawResourceStaticMethod { resource: String, method: String },
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize, Encode, Decode)]
pub struct InstructionId {
pub index: usize,
}
impl InstructionId {
pub fn new(index: usize) -> Self {
InstructionId { index }
}
pub fn init() -> Self {
InstructionId { index: 0 }
}
pub fn increment(&self) -> InstructionId {
InstructionId {
index: self.index + 1,
}
}
pub fn increment_mut(&mut self) -> InstructionId {
self.index += 1;
self.clone()
}
}
#[cfg(feature = "protobuf")]
mod protobuf {
use crate::{
AnalysedTypeWithUnit, ComponentDependencyKey, FunctionReferenceType, InstanceVariable,
InstructionId, ParsedFunctionSite, RibIR, VariableId,
};
use golem_api_grpc::proto::golem::rib::rib_ir::Instruction;
use golem_api_grpc::proto::golem::rib::{
And, ConcatInstruction, CreateFunctionNameInstruction, EqualTo, GetTag, GreaterThan,
GreaterThanOrEqualTo, InvokeFunctionInstruction, IsEmpty, JumpInstruction, LessThan,
LessThanOrEqualTo, Negate, Or, PushListInstruction, PushNoneInstruction,
PushTupleInstruction, RibIr as ProtoRibIR, WitResource,
};
use golem_wasm_ast::analysis::{AnalysedType, TypeStr};
impl TryFrom<golem_api_grpc::proto::golem::rib::FunctionReferenceType> for FunctionReferenceType {
type Error = String;
fn try_from(
value: golem_api_grpc::proto::golem::rib::FunctionReferenceType,
) -> Result<Self, Self::Error> {
let value = value.r#type.ok_or("Missing type".to_string())?;
let function_reference_type = match value {
golem_api_grpc::proto::golem::rib::function_reference_type::Type::Function(name) => FunctionReferenceType::Function {
function: name.name
},
golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceConstructor(name) =>
FunctionReferenceType::RawResourceConstructor {
resource: name.resource_name
},
golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceDrop(name) => FunctionReferenceType::RawResourceDrop {
resource: name.resource_name
},
golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceMethod(raw_resource_method) => {
let resource = raw_resource_method.resource_name;
let method = raw_resource_method.method_name;
FunctionReferenceType::RawResourceMethod { resource, method }
}
golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceStaticMethod(raw_resource_static_method) => {
let resource = raw_resource_static_method.resource_name;
let method = raw_resource_static_method.method_name;
FunctionReferenceType::RawResourceStaticMethod { resource, method }
}
};
Ok(function_reference_type)
}
}
impl From<FunctionReferenceType> for golem_api_grpc::proto::golem::rib::FunctionReferenceType {
fn from(value: FunctionReferenceType) -> Self {
match value {
FunctionReferenceType::Function { function } => golem_api_grpc::proto::golem::rib::FunctionReferenceType {
r#type: Some(golem_api_grpc::proto::golem::rib::function_reference_type::Type::Function(golem_api_grpc::proto::golem::rib::Function {
name: function
}))
},
FunctionReferenceType::RawResourceConstructor { resource } => golem_api_grpc::proto::golem::rib::FunctionReferenceType {
r#type: Some(golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceConstructor(golem_api_grpc::proto::golem::rib::RawResourceConstructor {
resource_name: resource
}))
},
FunctionReferenceType::RawResourceDrop { resource } => golem_api_grpc::proto::golem::rib::FunctionReferenceType {
r#type: Some(golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceDrop(golem_api_grpc::proto::golem::rib::RawResourceDrop {
resource_name: resource
}))
},
FunctionReferenceType::RawResourceMethod { resource, method } => golem_api_grpc::proto::golem::rib::FunctionReferenceType {
r#type: Some(golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceMethod(golem_api_grpc::proto::golem::rib::RawResourceMethod {
resource_name: resource,
method_name: method,
}))
},
FunctionReferenceType::RawResourceStaticMethod { resource, method } => golem_api_grpc::proto::golem::rib::FunctionReferenceType {
r#type: Some(golem_api_grpc::proto::golem::rib::function_reference_type::Type::RawResourceStaticMethod(golem_api_grpc::proto::golem::rib::RawResourceStaticMethod {
resource_name: resource,
method_name: method,
}))
},
}
}
}
impl TryFrom<ProtoRibIR> for RibIR {
type Error = String;
fn try_from(value: ProtoRibIR) -> Result<Self, Self::Error> {
let instruction = value
.instruction
.ok_or_else(|| "Missing instruction".to_string())?;
match instruction {
Instruction::GenerateWorkerName(generate_worker_name) => {
let variable_id = generate_worker_name
.variable_id
.map(VariableId::try_from)
.transpose()?;
Ok(RibIR::GenerateWorkerName(variable_id))
}
Instruction::PushLit(value) => Ok(RibIR::PushLit(
value
.try_into()
.map_err(|_| "Failed to convert PushLit".to_string())?,
)),
Instruction::AssignVar(value) => Ok(RibIR::AssignVar(
value
.try_into()
.map_err(|_| "Failed to convert AssignVar".to_string())?,
)),
Instruction::LoadVar(value) => Ok(RibIR::LoadVar(
value
.try_into()
.map_err(|_| "Failed to convert LoadVar".to_string())?,
)),
Instruction::CreateAndPushRecord(value) => {
Ok(RibIR::CreateAndPushRecord((&value).try_into().map_err(
|_| "Failed to convert CreateAndPushRecord".to_string(),
)?))
}
Instruction::Plus(value) => {
Ok(RibIR::Plus((&value).try_into().map_err(|_| {
"Failed to convert CreateAndPushRecord".to_string()
})?))
}
Instruction::Multiply(value) => {
Ok(RibIR::Multiply((&value).try_into().map_err(|_| {
"Failed to convert CreateAndPushRecord".to_string()
})?))
}
Instruction::Minus(value) => {
Ok(RibIR::Minus((&value).try_into().map_err(|_| {
"Failed to convert CreateAndPushRecord".to_string()
})?))
}
Instruction::Divide(value) => {
Ok(RibIR::Divide((&value).try_into().map_err(|_| {
"Failed to convert CreateAndPushRecord".to_string()
})?))
}
Instruction::UpdateRecord(value) => Ok(RibIR::UpdateRecord(value)),
Instruction::PushList(value) => Ok(RibIR::PushList(
value
.list_type
.ok_or("List type not present".to_string())
.and_then(|t| {
(&t).try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())
})?,
value.list_size as usize,
)),
Instruction::CreateSome(value) => Ok(RibIR::PushSome(
(&value)
.try_into()
.map_err(|_| "Failed to convert CreateSome".to_string())?,
)),
Instruction::CreateNone(value) => match value.none_type {
Some(v) => {
let optional_type = (&v)
.try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string());
Ok(RibIR::PushNone(Some(optional_type?)))
}
None => Ok(RibIR::PushNone(None)),
},
Instruction::CreateOkResult(value) => {
Ok(RibIR::PushOkResult((&value).try_into().map_err(|_| {
"Failed to convert CreateOkResult".to_string()
})?))
}
Instruction::CreateErrResult(value) => {
Ok(RibIR::PushErrResult((&value).try_into().map_err(|_| {
"Failed to convert CreateErrResult".to_string()
})?))
}
Instruction::Length(_) => Ok(RibIR::Length),
Instruction::SelectField(value) => Ok(RibIR::SelectField(value)),
Instruction::SelectIndex(value) => Ok(RibIR::SelectIndex(value as usize)),
Instruction::SelectIndexV1(_) => Ok(RibIR::SelectIndexV1),
Instruction::EqualTo(_) => Ok(RibIR::EqualTo),
Instruction::GreaterThan(_) => Ok(RibIR::GreaterThan),
Instruction::LessThan(_) => Ok(RibIR::LessThan),
Instruction::GreaterThanOrEqualTo(_) => Ok(RibIR::GreaterThanOrEqualTo),
Instruction::LessThanOrEqualTo(_) => Ok(RibIR::LessThanOrEqualTo),
Instruction::And(_) => Ok(RibIR::And),
Instruction::IsEmpty(_) => Ok(RibIR::IsEmpty),
Instruction::Or(_) => Ok(RibIR::Or),
Instruction::JumpIfFalse(value) => Ok(RibIR::JumpIfFalse(InstructionId::new(
value.instruction_id as usize,
))),
Instruction::Jump(value) => Ok(RibIR::Jump(InstructionId::new(
value.instruction_id as usize,
))),
Instruction::Label(value) => Ok(RibIR::Label(InstructionId::new(
value.instruction_id as usize,
))),
Instruction::Deconstruct(_) => Ok(RibIR::Deconstruct),
Instruction::InvokeFunction(invoke_function_instruction) => {
let return_type = match invoke_function_instruction.return_type {
Some(return_type) => {
let analysed_type = (&return_type)
.try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())?;
AnalysedTypeWithUnit::Type(analysed_type)
}
None => AnalysedTypeWithUnit::Unit,
};
let instance_variable: InstanceVariable = invoke_function_instruction
.instance_variable
.ok_or("Missing instance_variable".to_string())
.and_then(|iv| {
match iv.kind.ok_or("Missing instance_variable kind".to_string())? {
golem_api_grpc::proto::golem::rib::instance_variable::Kind::Resource(wit_resource) => {
let variable_id = wit_resource.variable_id.ok_or(
"Missing variable_id in WitResource".to_string(),
)?.try_into()
.map_err(|_| "Failed to convert VariableId".to_string())?;
Ok(InstanceVariable::WitResource(variable_id))
}
golem_api_grpc::proto::golem::rib::instance_variable::Kind::Worker(wit_worker) => {
let variable_id = wit_worker.variable_id.ok_or(
"Missing variable_id in WitWorker".to_string(),
)?.try_into()
.map_err(|_| "Failed to convert VariableId".to_string())?;
Ok(InstanceVariable::WitWorker(variable_id))
}
}
})?;
let component_dependency_key_proto = invoke_function_instruction
.component
.ok_or("Missing component_dependency_key".to_string())?;
let component_dependency_key =
ComponentDependencyKey::try_from(component_dependency_key_proto)
.map_err(|_| "Failed to convert ComponentDependencyKey".to_string())?;
Ok(RibIR::InvokeFunction(
component_dependency_key,
instance_variable,
invoke_function_instruction.argument_count as usize,
return_type,
))
}
Instruction::VariantConstruction(variant_construction) => {
let variant_type = variant_construction
.return_type
.ok_or("Missing return_type for variant construction".to_string())?;
let analysed_variant_type = (&variant_type)
.try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())?;
Ok(RibIR::PushVariant(
variant_construction.variant_name,
analysed_variant_type,
))
}
Instruction::EnumConstruction(enum_construction) => {
let enum_type = enum_construction
.return_type
.ok_or("Missing return_type for enum construction".to_string())?;
let analysed_enum_type = (&enum_type)
.try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())?;
Ok(RibIR::PushEnum(
enum_construction.enum_name,
analysed_enum_type,
))
}
Instruction::Throw(value) => Ok(RibIR::Throw(value)),
Instruction::PushFlag(flag) => Ok(RibIR::PushFlag(
flag.try_into()
.map_err(|_| "Failed to convert PushFlag".to_string())?,
)),
Instruction::GetTag(_) => Ok(RibIR::GetTag),
Instruction::PushTuple(tuple_instruction) => {
let tuple_type = tuple_instruction
.tuple_type
.ok_or("Missing tuple_type".to_string())
.and_then(|t| {
(&t).try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())
})?;
Ok(RibIR::PushTuple(
tuple_type,
tuple_instruction.tuple_size as usize,
))
}
Instruction::Negate(_) => Ok(RibIR::Negate),
Instruction::Concat(concat_instruction) => {
Ok(RibIR::Concat(concat_instruction.arg_size as usize))
}
Instruction::CreateFunctionName(instruction) => {
let parsed_site = instruction.site.ok_or("Missing site".to_string())?;
let parsed_function_site = ParsedFunctionSite::try_from(parsed_site)?;
let reference_type = instruction
.function_reference_details
.ok_or("Missing reference_type".to_string())?;
let function_reference_type = reference_type.try_into()?;
Ok(RibIR::CreateFunctionName(
parsed_function_site,
function_reference_type,
))
}
Instruction::ListToIterator(_) => Ok(RibIR::ToIterator),
Instruction::CreateSink(create_sink) => {
let result = create_sink
.list_type
.ok_or("Sink list type not present".to_string())
.and_then(|t| {
(&t).try_into()
.map_err(|_| "Failed to convert AnalysedType".to_string())
})?;
Ok(RibIR::CreateSink(result))
}
Instruction::AdvanceIterator(_) => Ok(RibIR::AdvanceIterator),
Instruction::SinkToList(_) => Ok(RibIR::SinkToList),
Instruction::PushToSink(_) => Ok(RibIR::PushToSink),
}
}
}
impl TryFrom<RibIR> for ProtoRibIR {
type Error = String;
fn try_from(value: RibIR) -> Result<Self, Self::Error> {
let instruction = match value {
RibIR::GenerateWorkerName(variable_id) => {
let variable_id_proto = variable_id.map(|v| v.into());
Instruction::GenerateWorkerName(
golem_api_grpc::proto::golem::rib::GenerateWorkerName {
variable_id: variable_id_proto,
},
)
}
RibIR::PushLit(value) => Instruction::PushLit(value.into()),
RibIR::And => Instruction::And(And {}),
RibIR::IsEmpty => Instruction::IsEmpty(IsEmpty {}),
RibIR::Or => Instruction::Or(Or {}),
RibIR::AssignVar(value) => Instruction::AssignVar(value.into()),
RibIR::LoadVar(value) => Instruction::LoadVar(value.into()),
RibIR::CreateAndPushRecord(value) => {
Instruction::CreateAndPushRecord((&value).into())
}
RibIR::Plus(value) => Instruction::Plus((&value).into()),
RibIR::Minus(value) => Instruction::Minus((&value).into()),
RibIR::Multiply(value) => Instruction::Multiply((&value).into()),
RibIR::Divide(value) => Instruction::Divide((&value).into()),
RibIR::UpdateRecord(value) => Instruction::UpdateRecord(value),
RibIR::PushList(value, arg_size) => Instruction::PushList(PushListInstruction {
list_type: Some((&value).into()),
list_size: arg_size as u64,
}),
RibIR::PushSome(value) => Instruction::CreateSome((&value).into()),
RibIR::PushNone(value) => {
let push_none_instruction = PushNoneInstruction {
none_type: value.map(|t| (&t).into()),
};
Instruction::CreateNone(push_none_instruction)
}
RibIR::PushOkResult(value) => Instruction::CreateOkResult((&value).into()),
RibIR::PushErrResult(value) => Instruction::CreateErrResult((&value).into()),
RibIR::SelectField(value) => Instruction::SelectField(value),
RibIR::SelectIndex(value) => Instruction::SelectIndex(value as u64),
RibIR::EqualTo => Instruction::EqualTo(EqualTo {}),
RibIR::GreaterThan => Instruction::GreaterThan(GreaterThan {}),
RibIR::LessThan => Instruction::LessThan(LessThan {}),
RibIR::Length => Instruction::Length(golem_api_grpc::proto::golem::rib::Length {}),
RibIR::SelectIndexV1 => {
Instruction::SelectIndexV1(golem_api_grpc::proto::golem::rib::SelectIndexV1 {})
}
RibIR::GreaterThanOrEqualTo => {
Instruction::GreaterThanOrEqualTo(GreaterThanOrEqualTo {})
}
RibIR::LessThanOrEqualTo => Instruction::LessThanOrEqualTo(LessThanOrEqualTo {}),
RibIR::JumpIfFalse(value) => Instruction::JumpIfFalse(JumpInstruction {
instruction_id: value.index as u64,
}),
RibIR::Jump(value) => Instruction::Jump(JumpInstruction {
instruction_id: value.index as u64,
}),
RibIR::Label(value) => Instruction::Label(JumpInstruction {
instruction_id: value.index as u64,
}),
RibIR::Deconstruct => {
Instruction::Deconstruct((&AnalysedType::Str(TypeStr)).into())
} RibIR::InvokeFunction(
component_dependency_key,
worker_name_presence,
arg_count,
return_type,
) => {
let typ = match return_type {
AnalysedTypeWithUnit::Unit => None,
AnalysedTypeWithUnit::Type(analysed_type) => {
let typ =
golem_wasm_ast::analysis::protobuf::Type::from(&analysed_type);
Some(typ)
}
};
let instance_variable = match worker_name_presence {
InstanceVariable::WitResource(variable_id) => {
golem_api_grpc::proto::golem::rib::InstanceVariable {
kind: Some(
golem_api_grpc::proto::golem::rib::instance_variable::Kind::Resource(
WitResource {
variable_id: Some(variable_id.into()),
},
),
),
}
}
InstanceVariable::WitWorker(variable_id) => {
golem_api_grpc::proto::golem::rib::InstanceVariable {
kind: Some(
golem_api_grpc::proto::golem::rib::instance_variable::Kind::Worker(
golem_api_grpc::proto::golem::rib::WitWorker {
variable_id: Some(variable_id.into()),
},
),
),
}
}
};
let component_dependency_key =
golem_api_grpc::proto::golem::rib::ComponentDependencyKey::from(
component_dependency_key,
);
Instruction::InvokeFunction(InvokeFunctionInstruction {
component: Some(component_dependency_key),
argument_count: arg_count as u64,
return_type: typ,
instance_variable: Some(instance_variable),
})
}
RibIR::PushVariant(name, return_type) => {
let typ = golem_wasm_ast::analysis::protobuf::Type::from(&return_type);
Instruction::VariantConstruction(
golem_api_grpc::proto::golem::rib::VariantConstructionInstruction {
variant_name: name,
return_type: Some(typ),
},
)
}
RibIR::PushEnum(name, return_type) => {
let typ = golem_wasm_ast::analysis::protobuf::Type::from(&return_type);
Instruction::EnumConstruction(
golem_api_grpc::proto::golem::rib::EnumConstructionInstruction {
enum_name: name,
return_type: Some(typ),
},
)
}
RibIR::Throw(msg) => Instruction::Throw(msg),
RibIR::PushFlag(flag) => Instruction::PushFlag(flag.into()),
RibIR::GetTag => Instruction::GetTag(GetTag {}),
RibIR::PushTuple(analysed_type, size) => {
let typ = golem_wasm_ast::analysis::protobuf::Type::from(&analysed_type);
Instruction::PushTuple(PushTupleInstruction {
tuple_type: Some(typ),
tuple_size: size as u64,
})
}
RibIR::Concat(concat) => Instruction::Concat(ConcatInstruction {
arg_size: concat as u64,
}),
RibIR::Negate => Instruction::Negate(Negate {}),
RibIR::CreateFunctionName(site, reference_type) => {
Instruction::CreateFunctionName(CreateFunctionNameInstruction {
site: Some(site.into()),
function_reference_details: Some(reference_type.into()),
})
}
RibIR::ToIterator => Instruction::ListToIterator(
golem_api_grpc::proto::golem::rib::ListToIterator {},
),
RibIR::CreateSink(analysed_type) => {
Instruction::CreateSink(golem_api_grpc::proto::golem::rib::CreateSink {
list_type: Some((&analysed_type).into()),
})
}
RibIR::AdvanceIterator => Instruction::AdvanceIterator(
golem_api_grpc::proto::golem::rib::AdvanceIterator {},
),
RibIR::PushToSink => {
Instruction::PushToSink(golem_api_grpc::proto::golem::rib::PushToSink {})
}
RibIR::SinkToList => {
Instruction::SinkToList(golem_api_grpc::proto::golem::rib::SinkToList {})
}
};
Ok(ProtoRibIR {
instruction: Some(instruction),
})
}
}
}