use super::*;
use boltffi_ffi_rules::callable::ExecutionKind;
impl<'c> Lowerer<'c> {
pub fn to_abi_contract(&self) -> AbiContract {
let function_calls = self
.contract
.functions
.iter()
.map(|func| self.abi_call_for_function(func));
let class_calls = self.contract.catalog.all_classes().flat_map(|class| {
let ctor_calls = class
.constructors
.iter()
.enumerate()
.map(|(index, ctor)| self.abi_call_for_constructor(class, ctor, index));
let method_calls = class
.methods
.iter()
.map(|method| self.abi_call_for_method(class, method));
ctor_calls.chain(method_calls)
});
let value_type_calls = self
.contract
.catalog
.all_records()
.filter(|r| r.has_methods())
.map(|r| r as &dyn MethodHost)
.chain(
self.contract
.catalog
.all_enums()
.filter(|e| e.has_methods())
.map(|e| e as &dyn MethodHost),
)
.flat_map(|host| {
let ctor_calls = host.constructors().iter().enumerate().map(|(index, ctor)| {
self.abi_call_for_value_type_constructor(host, ctor, index)
});
let method_calls = host
.methods()
.iter()
.filter(|method| method.execution_kind() == ExecutionKind::Sync)
.map(|method| self.abi_call_for_value_type_method(host, method));
ctor_calls.chain(method_calls)
});
let calls = function_calls
.chain(class_calls)
.chain(value_type_calls)
.collect();
let callbacks = self
.contract
.catalog
.all_callbacks()
.map(|callback| self.abi_callback_invocation(callback))
.collect();
let records = self
.contract
.catalog
.all_records()
.map(|record| self.abi_record(record))
.collect();
let enums = self
.contract
.catalog
.all_enums()
.map(|enumeration| self.abi_enum(enumeration))
.collect();
let streams = self
.contract
.catalog
.all_classes()
.flat_map(|class| {
class
.streams
.iter()
.map(|stream| self.abi_stream(&class.id, stream))
})
.collect();
AbiContract {
package: self.contract.package.clone(),
calls,
callbacks,
streams,
records,
enums,
free_buf: naming::free_buf(),
atomic_cas: naming::atomic_u8_cas(),
}
}
pub(super) fn abi_call_for_function(&self, func: &FunctionDef) -> AbiCall {
let plan = self.lower_function(func);
let symbol = self.call_symbol(&plan);
let params = self.abi_params_from_plan(&plan.params);
let (mode, returns, error) = self.abi_mode_returns_error_for_function(func, &plan.kind);
AbiCall {
id: CallId::Function(func.id.clone()),
symbol,
mode,
params,
returns,
error,
}
}
pub(super) fn abi_call_for_method(&self, class: &ClassDef, method: &MethodDef) -> AbiCall {
let plan = self.lower_method(class, method);
let symbol = self.call_symbol(&plan);
let params = self.abi_params_from_plan(&plan.params);
let (mode, returns, error) =
self.abi_mode_returns_error_for_method(class, method, &plan.kind);
AbiCall {
id: CallId::Method {
class_id: class.id.clone(),
method_id: method.id.clone(),
},
symbol,
mode,
params,
returns,
error,
}
}
pub(super) fn abi_call_for_constructor(
&self,
class: &ClassDef,
ctor: &ConstructorDef,
index: usize,
) -> AbiCall {
let plan = self.lower_constructor(class, ctor);
let symbol = self.call_symbol(&plan);
let params = self.abi_params_from_plan(&plan.params);
let (returns, error) = self.return_shape_and_error(match &plan.kind {
CallPlanKind::Sync { returns } => returns,
CallPlanKind::Async { .. } => panic!("constructors cannot be async"),
});
AbiCall {
id: CallId::Constructor {
class_id: class.id.clone(),
index,
},
symbol,
mode: CallMode::Sync,
params,
returns,
error,
}
}
pub(super) fn abi_call_for_value_type_method(
&self,
host: &dyn MethodHost,
method: &MethodDef,
) -> AbiCall {
let plan = self.lower_value_type_method(host, method);
let symbol = self.call_symbol(&plan);
let params = self.abi_params_from_plan(&plan.params);
let (returns, error) = self.return_shape_and_error(match &plan.kind {
CallPlanKind::Sync { returns } => returns,
CallPlanKind::Async { .. } => unreachable!("value type methods are always sync"),
});
AbiCall {
id: host.method_call_id(&method.id),
symbol,
mode: CallMode::Sync,
params,
returns,
error,
}
}
pub(super) fn abi_call_for_value_type_constructor(
&self,
host: &dyn MethodHost,
ctor: &ConstructorDef,
index: usize,
) -> AbiCall {
let plan = self.lower_value_type_constructor(host, ctor);
let symbol = self.call_symbol(&plan);
let params = self.abi_params_from_plan(&plan.params);
let (returns, error) = self.return_shape_and_error(match &plan.kind {
CallPlanKind::Sync { returns } => returns,
CallPlanKind::Async { .. } => unreachable!("value type constructors are always sync"),
});
AbiCall {
id: host.constructor_call_id(index),
symbol,
mode: CallMode::Sync,
params,
returns,
error,
}
}
pub(super) fn abi_callback_invocation(
&self,
callback: &CallbackTraitDef,
) -> AbiCallbackInvocation {
let methods = callback
.methods
.iter()
.map(|method| {
let params = self.abi_callback_params(callback, method).collect();
let (returns, error) = self.callback_return_shape_and_error(&method.returns);
AbiCallbackMethod {
id: method.id.clone(),
vtable_field: naming::vtable_field_name(method.id.as_str()),
execution_kind: method.execution_kind(),
params,
returns,
error,
}
})
.collect();
AbiCallbackInvocation {
callback_id: callback.id.clone(),
vtable_type: naming::callback_vtable_name(callback.id.as_str()),
register_fn: naming::callback_register_fn(callback.id.as_str()),
create_fn: naming::callback_create_fn(callback.id.as_str()),
methods,
}
}
pub(super) fn abi_stream(&self, class_id: &ClassId, stream: &StreamDef) -> AbiStream {
let class_name = class_id.as_str();
let stream_name = stream.id.as_str();
let item_codec = self.build_codec(&stream.item_type);
let decode_ops = self.expand_decode(&item_codec);
let item_transport = self.classify_type(&stream.item_type);
let item_size = match &item_transport {
Transport::Scalar(origin) => Some(match origin.primitive() {
PrimitiveType::Bool | PrimitiveType::I8 | PrimitiveType::U8 => 1,
PrimitiveType::I16 | PrimitiveType::U16 => 2,
PrimitiveType::I32 | PrimitiveType::U32 | PrimitiveType::F32 => 4,
PrimitiveType::I64
| PrimitiveType::U64
| PrimitiveType::ISize
| PrimitiveType::USize
| PrimitiveType::F64 => 8,
}),
Transport::Composite(layout) => Some(layout.total_size),
_ => None,
};
AbiStream {
class_id: class_id.clone(),
stream_id: stream.id.clone(),
mode: stream.mode,
item: StreamItemTransport::WireEncoded { decode_ops },
item_transport,
item_size,
subscribe: naming::stream_ffi_subscribe(class_name, stream_name),
poll: naming::stream_ffi_poll(class_name, stream_name),
pop_batch: naming::stream_ffi_pop_batch(class_name, stream_name),
wait: naming::stream_ffi_wait(class_name, stream_name),
unsubscribe: naming::stream_ffi_unsubscribe(class_name, stream_name),
free: naming::stream_ffi_free(class_name, stream_name),
}
}
pub(super) fn abi_record(&self, record: &RecordDef) -> AbiRecord {
let codec = self.build_codec(&TypeExpr::Record(record.id.clone()));
let decode_ops = self.expand_decode(&codec);
let encode_ops = self.expand_encode(&codec, ValueExpr::Instance);
let (is_blittable, size) = match codec {
CodecPlan::Record {
layout: RecordLayout::Blittable { size, .. },
..
} => (true, Some(size)),
_ => (false, None),
};
AbiRecord {
id: record.id.clone(),
decode_ops,
encode_ops,
is_blittable,
size,
}
}
pub(super) fn abi_enum(&self, enumeration: &EnumDef) -> AbiEnum {
let codec = self.build_codec(&TypeExpr::Enum(enumeration.id.clone()));
let decode_ops = self.expand_decode(&codec);
let encode_ops = self.expand_encode(&codec, ValueExpr::Instance);
let (is_c_style, codec_tag_strategy, variants) = match codec {
CodecPlan::Enum {
layout: EnumLayout::CStyle { tag_strategy, .. },
..
} => (
true,
tag_strategy,
match &enumeration.repr {
EnumRepr::CStyle { variants, .. } => variants
.iter()
.map(|variant| AbiEnumVariant {
name: variant.name.clone(),
discriminant: variant.discriminant,
payload: AbiEnumPayload::Unit,
})
.collect(),
_ => vec![],
},
),
CodecPlan::Enum {
layout:
EnumLayout::Data {
tag_strategy,
variants,
..
},
..
} => (
false,
tag_strategy,
match &enumeration.repr {
EnumRepr::Data {
variants: data_variants,
..
} => {
let layout_fields = variants
.iter()
.map(|variant| {
let fields = match &variant.payload {
VariantPayloadLayout::Unit => Vec::new(),
VariantPayloadLayout::Fields(fields) => fields
.iter()
.map(|field| self.abi_enum_field(field))
.collect(),
};
(variant.name.clone(), fields)
})
.collect::<HashMap<_, _>>();
data_variants
.iter()
.map(|variant| {
let fields = layout_fields
.get(&variant.name)
.cloned()
.unwrap_or_default();
let payload = match &variant.payload {
VariantPayload::Unit => AbiEnumPayload::Unit,
VariantPayload::Tuple(_) => AbiEnumPayload::Tuple(fields),
VariantPayload::Struct(_) => AbiEnumPayload::Struct(fields),
};
AbiEnumVariant {
name: variant.name.clone(),
discriminant: variant.discriminant,
payload,
}
})
.collect()
}
_ => Vec::new(),
},
),
_ => (false, EnumTagStrategy::OrdinalIndex, vec![]),
};
AbiEnum {
id: enumeration.id.clone(),
decode_ops,
encode_ops,
is_c_style,
codec_tag_strategy,
variants,
}
}
pub(super) fn abi_enum_field(&self, field: &EncodedField) -> AbiEnumField {
let decode = self.expand_decode(&field.codec);
let encode = self.expand_encode(
&field.codec,
ValueExpr::Named(field.name.as_str().to_string()),
);
AbiEnumField {
name: field.name.clone(),
type_expr: TypeExpr::from(&field.codec),
decode,
encode,
}
}
pub(super) fn abi_mode_returns_error_for_function(
&self,
func: &FunctionDef,
kind: &CallPlanKind,
) -> (CallMode, ReturnShape, ErrorTransport) {
match kind {
CallPlanKind::Sync { returns } => {
let (ret, error) = self.return_shape_and_error(returns);
(CallMode::Sync, ret, error)
}
CallPlanKind::Async { async_plan } => {
let mode =
CallMode::Async(Box::new(self.async_call_for_function(func, async_plan)));
let ret = ReturnShape {
contract: ReturnContract::infallible(ValueReturnStrategy::Scalar(
ScalarReturnStrategy::PrimitiveValue,
)),
transport: Some(Transport::Scalar(ScalarOrigin::Primitive(
PrimitiveType::USize,
))),
decode_ops: None,
encode_ops: None,
};
(mode, ret, ErrorTransport::None)
}
}
}
pub(super) fn abi_mode_returns_error_for_method(
&self,
class: &ClassDef,
method: &MethodDef,
kind: &CallPlanKind,
) -> (CallMode, ReturnShape, ErrorTransport) {
match kind {
CallPlanKind::Sync { returns } => {
let (ret, error) = self.return_shape_and_error(returns);
(CallMode::Sync, ret, error)
}
CallPlanKind::Async { async_plan } => {
let mode = CallMode::Async(Box::new(
self.async_call_for_method(class, method, async_plan),
));
let ret = ReturnShape {
contract: ReturnContract::infallible(ValueReturnStrategy::Scalar(
ScalarReturnStrategy::PrimitiveValue,
)),
transport: Some(Transport::Scalar(ScalarOrigin::Primitive(
PrimitiveType::USize,
))),
decode_ops: None,
encode_ops: None,
};
(mode, ret, ErrorTransport::None)
}
}
}
pub(super) fn async_call_for_function(
&self,
func: &FunctionDef,
plan: &AsyncPlan,
) -> AsyncCall {
let (result, _) = self.return_shape_and_error(&plan.result);
AsyncCall {
poll: naming::function_ffi_poll(func.id.as_str()),
complete: naming::function_ffi_complete(func.id.as_str()),
cancel: naming::function_ffi_cancel(func.id.as_str()),
free: naming::function_ffi_free(func.id.as_str()),
result: result.with_error_strategy(ErrorReturnStrategy::StatusCode),
error: ErrorTransport::StatusCode,
}
}
pub(super) fn async_call_for_method(
&self,
class: &ClassDef,
method: &MethodDef,
plan: &AsyncPlan,
) -> AsyncCall {
let (result, _) = self.return_shape_and_error(&plan.result);
AsyncCall {
poll: naming::method_ffi_poll(class.id.as_str(), method.id.as_str()),
complete: naming::method_ffi_complete(class.id.as_str(), method.id.as_str()),
cancel: naming::method_ffi_cancel(class.id.as_str(), method.id.as_str()),
free: naming::method_ffi_free(class.id.as_str(), method.id.as_str()),
result: result.with_error_strategy(ErrorReturnStrategy::StatusCode),
error: ErrorTransport::StatusCode,
}
}
pub(super) fn return_shape_and_error(
&self,
returns: &ReturnPlan,
) -> (ReturnShape, ErrorTransport) {
match returns {
ReturnPlan::Void => (ReturnShape::void(), ErrorTransport::None),
ReturnPlan::Value(v) => (self.return_shape_from_transport(v), ErrorTransport::None),
ReturnPlan::Fallible {
ok: Transport::Handle { class_id, .. },
err_codec,
} => (
ReturnShape {
contract: ReturnContract::new(
ValueReturnStrategy::ObjectHandle,
ErrorReturnStrategy::Encoded,
),
transport: Some(Transport::Handle {
class_id: class_id.clone(),
nullable: true,
}),
decode_ops: None,
encode_ops: None,
},
ErrorTransport::Encoded {
decode_ops: self.expand_decode(err_codec),
encode_ops: None,
},
),
ReturnPlan::Fallible { ok, err_codec } => {
let ok_codec = self.codec_from_transport(ok);
let result_codec = CodecPlan::Result {
ok: Box::new(ok_codec),
err: Box::new(err_codec.clone()),
};
let decode_ops = self.expand_decode(&result_codec);
let encode_ops = self.expand_encode(&result_codec, ValueExpr::Var("value".into()));
let wire_transport = Transport::Span(SpanContent::Encoded(result_codec));
(
ReturnShape::from_transport_with_ops(wire_transport, decode_ops, encode_ops)
.with_error_strategy(ErrorReturnStrategy::Encoded),
ErrorTransport::Encoded {
decode_ops: self.expand_decode(err_codec),
encode_ops: None,
},
)
}
}
}
pub(super) fn return_shape_from_transport(&self, value: &Transport) -> ReturnShape {
match value {
Transport::Scalar(origin) => ReturnShape {
contract: ReturnContract::infallible(value.value_return_strategy()),
transport: Some(Transport::Scalar(origin.clone())),
decode_ops: None,
encode_ops: None,
},
Transport::Composite(layout) => {
let codec = self.build_codec(&TypeExpr::Record(layout.record_id.clone()));
let decode_ops = self.expand_decode(&codec);
let encode_ops = self.expand_encode(&codec, ValueExpr::Var("value".into()));
ReturnShape {
contract: ReturnContract::infallible(value.value_return_strategy()),
transport: Some(value.clone()),
decode_ops: Some(decode_ops),
encode_ops: Some(encode_ops),
}
}
Transport::Span(SpanContent::Composite(_) | SpanContent::Scalar(_)) => ReturnShape {
contract: ReturnContract::infallible(value.value_return_strategy()),
transport: Some(value.clone()),
decode_ops: None,
encode_ops: None,
},
Transport::Span(content) => {
if let Some(composite_transport) = self.try_promote_to_composite_span(content) {
return ReturnShape {
contract: ReturnContract::infallible(
composite_transport.value_return_strategy(),
),
transport: Some(composite_transport),
decode_ops: None,
encode_ops: None,
};
}
let codec = self.codec_from_span_content(content);
let decode_ops = self.expand_decode(&codec);
let encode_ops = self.expand_encode(&codec, ValueExpr::Var("value".into()));
ReturnShape::from_transport_with_ops(value.clone(), decode_ops, encode_ops)
}
transport @ (Transport::Handle { .. } | Transport::Callback { .. }) => ReturnShape {
contract: ReturnContract::infallible(transport.value_return_strategy()),
transport: Some(transport.clone()),
decode_ops: None,
encode_ops: None,
},
}
}
pub(super) fn codec_from_transport(&self, value: &Transport) -> CodecPlan {
match value {
Transport::Scalar(origin) => CodecPlan::Primitive(origin.primitive()),
Transport::Composite(layout) => {
self.build_codec(&TypeExpr::Record(layout.record_id.clone()))
}
Transport::Span(content) => self.codec_from_span_content(content),
Transport::Handle { .. } | Transport::Callback { .. } => {
panic!("Handle and Callback types cannot be wire-encoded")
}
}
}
pub(super) fn try_promote_to_composite_span(&self, content: &SpanContent) -> Option<Transport> {
let SpanContent::Encoded(CodecPlan::Vec { element, .. }) = content else {
return None;
};
let CodecPlan::Record { id, .. } = element.as_ref() else {
return None;
};
match self.classify_record(id) {
Transport::Composite(layout) => Some(Transport::Span(SpanContent::Composite(layout))),
_ => None,
}
}
pub(super) fn codec_from_span_content(&self, content: &SpanContent) -> CodecPlan {
match content {
SpanContent::Scalar(origin) => {
let p = origin.primitive();
CodecPlan::Vec {
element: Box::new(CodecPlan::Primitive(p)),
layout: VecLayout::Blittable {
element_size: p.wire_size_bytes(),
},
}
}
SpanContent::Composite(layout) => {
let element_codec = self.build_codec(&TypeExpr::Record(layout.record_id.clone()));
CodecPlan::Vec {
element: Box::new(element_codec),
layout: VecLayout::Blittable {
element_size: layout.total_size,
},
}
}
SpanContent::Utf8 => CodecPlan::String,
SpanContent::Encoded(codec) => codec.clone(),
}
}
pub(super) fn abi_params_from_plan(&self, params: &[ParamPlan]) -> Vec<AbiParam> {
params
.iter()
.flat_map(|param| self.abi_param_from_plan(param))
.collect()
}
pub(super) fn abi_param_from_plan(&self, param: &ParamPlan) -> Vec<AbiParam> {
let len_name = ParamName::new(format!("{}_len", param.name.as_str()));
let make_span_params = |transport: Transport,
contract: ParamContract,
mutability: Mutability,
decode_ops: Option<ReadSeq>,
encode_ops: Option<WriteSeq>|
-> Vec<AbiParam> {
let ptr_element = match &transport {
Transport::Span(SpanContent::Scalar(origin)) => origin.primitive(),
_ => PrimitiveType::U8,
};
vec![
AbiParam {
name: param.name.clone(),
abi_type: AbiType::Pointer(ptr_element),
role: ParamRole::Input {
contract,
transport,
mutability,
len_param: Some(len_name.clone()),
decode_ops,
encode_ops,
},
},
AbiParam {
name: len_name.clone(),
abi_type: AbiType::USize,
role: ParamRole::SyntheticLen {
for_param: param.name.clone(),
},
},
]
};
match ¶m.transport {
Transport::Scalar(origin) => vec![AbiParam {
name: param.name.clone(),
abi_type: AbiType::from(origin.primitive()),
role: ParamRole::Input {
contract: param.contract,
transport: param.transport.clone(),
mutability: param.mutability,
len_param: None,
decode_ops: None,
encode_ops: None,
},
}],
Transport::Composite(layout) => {
let codec = self.build_codec(&TypeExpr::Record(layout.record_id.clone()));
let decode_ops = self.expand_decode(&codec);
let encode_ops =
self.expand_encode(&codec, ValueExpr::Named(param.name.as_str().to_string()));
vec![AbiParam {
name: param.name.clone(),
abi_type: AbiType::Struct(layout.record_id.clone()),
role: ParamRole::Input {
contract: param.contract,
transport: Transport::Composite(layout.clone()),
mutability: param.mutability,
len_param: None,
decode_ops: Some(decode_ops),
encode_ops: Some(encode_ops),
},
}]
}
span @ Transport::Span(content) => match content {
SpanContent::Scalar(_) | SpanContent::Utf8 => {
make_span_params(span.clone(), param.contract, param.mutability, None, None)
}
SpanContent::Composite(_) => {
let codec = self.codec_from_span_content(content);
let decode_ops = self.expand_decode(&codec);
let encode_ops = self
.expand_encode(&codec, ValueExpr::Named(param.name.as_str().to_string()));
make_span_params(
span.clone(),
param.contract,
param.mutability,
Some(decode_ops),
Some(encode_ops),
)
}
SpanContent::Encoded(codec) => {
let decode_ops = self.expand_decode(codec);
let encode_ops = self
.expand_encode(codec, ValueExpr::Named(param.name.as_str().to_string()));
make_span_params(
span.clone(),
param.contract,
param.mutability,
Some(decode_ops),
Some(encode_ops),
)
}
},
Transport::Handle { class_id, .. } => vec![AbiParam {
name: param.name.clone(),
abi_type: AbiType::Handle(class_id.clone()),
role: ParamRole::Input {
contract: param.contract,
transport: param.transport.clone(),
mutability: param.mutability,
len_param: None,
decode_ops: None,
encode_ops: None,
},
}],
Transport::Callback {
style: CallbackStyle::BoxedDyn,
..
} => vec![AbiParam {
name: param.name.clone(),
abi_type: AbiType::CallbackHandle,
role: ParamRole::Input {
contract: param.contract,
transport: param.transport.clone(),
mutability: param.mutability,
len_param: None,
decode_ops: None,
encode_ops: None,
},
}],
Transport::Callback {
style: CallbackStyle::ImplTrait,
callback_id,
..
} => {
let ud_name = ParamName::new(format!("{}_ud", param.name.as_str()));
let (fn_params, fn_return_type) =
self.inline_callback_fn_abi_signature(callback_id);
vec![
AbiParam {
name: param.name.clone(),
abi_type: AbiType::InlineCallbackFn {
params: fn_params,
return_type: Box::new(fn_return_type),
},
role: ParamRole::Input {
contract: param.contract,
transport: param.transport.clone(),
mutability: param.mutability,
len_param: Some(ud_name.clone()),
decode_ops: None,
encode_ops: None,
},
},
AbiParam {
name: ud_name,
abi_type: AbiType::Pointer(PrimitiveType::U8),
role: ParamRole::CallbackContext {
for_param: param.name.clone(),
},
},
]
}
}
}
pub(super) fn inline_callback_fn_abi_signature(
&self,
callback_id: &CallbackId,
) -> (Vec<AbiType>, AbiType) {
let callback_def = self
.contract
.catalog
.resolve_callback(callback_id)
.unwrap_or_else(|| panic!("callback {} not found", callback_id.as_str()));
let mut abi_params = vec![];
for method in &callback_def.methods {
for param in &method.params {
let transport = self.classify_type(¶m.type_expr);
match &transport {
Transport::Scalar(origin) => {
abi_params.push(AbiType::from(origin.primitive()));
}
_ => {
abi_params.push(AbiType::Pointer(PrimitiveType::U8));
abi_params.push(AbiType::USize);
}
}
}
}
let return_type = callback_def
.methods
.first()
.and_then(|method| match &method.returns {
ReturnDef::Void => None,
ReturnDef::Value(ty) => {
let transport = self.classify_type(ty);
match &transport {
Transport::Scalar(origin) => Some(AbiType::from(origin.primitive())),
Transport::Composite(layout) => {
Some(AbiType::Struct(layout.record_id.clone()))
}
_ => Some(AbiType::OwnedBuffer),
}
}
ReturnDef::Result { .. } => Some(AbiType::OwnedBuffer),
})
.unwrap_or(AbiType::Void);
(abi_params, return_type)
}
pub(super) fn callback_return_shape_and_error(
&self,
returns: &ReturnDef,
) -> (ReturnShape, ErrorTransport) {
match returns {
ReturnDef::Void => (ReturnShape::void(), ErrorTransport::None),
ReturnDef::Value(ty) => {
let transport = self.classify_type(ty);
let shape = match &transport {
Transport::Scalar(_)
| Transport::Composite(_)
| Transport::Handle { .. }
| Transport::Callback { .. } => self.return_shape_from_transport(&transport),
_ => {
let codec = self.build_codec(ty);
let decode_ops = self.expand_decode(&codec);
let encode_ops = self.expand_encode(&codec, ValueExpr::Var("value".into()));
let wire_transport = Transport::Span(SpanContent::Encoded(codec));
ReturnShape::from_transport_with_ops(wire_transport, decode_ops, encode_ops)
}
};
(shape, ErrorTransport::None)
}
ReturnDef::Result { ok, err } => {
let ok_codec = self.build_codec(ok);
let err_codec = self.build_codec(err);
let result_codec = CodecPlan::Result {
ok: Box::new(ok_codec),
err: Box::new(err_codec.clone()),
};
let decode_ops = self.expand_decode(&result_codec);
let encode_ops = self.expand_encode(&result_codec, ValueExpr::Var("result".into()));
let wire_transport = Transport::Span(SpanContent::Encoded(result_codec));
(
ReturnShape::from_transport_with_ops(wire_transport, decode_ops, encode_ops)
.with_error_strategy(ErrorReturnStrategy::Encoded),
ErrorTransport::Encoded {
decode_ops: self.expand_decode(&err_codec),
encode_ops: Some(
self.expand_encode(&err_codec, ValueExpr::Var("error".into())),
),
},
)
}
}
}
pub(super) fn abi_callback_params<'a>(
&'a self,
callback: &'a CallbackTraitDef,
method: &'a CallbackMethodDef,
) -> impl Iterator<Item = AbiParam> + 'a {
let handle_param = AbiParam {
name: ParamName::new("handle"),
abi_type: AbiType::Pointer(PrimitiveType::U8),
role: ParamRole::Input {
contract: ParamContract::new(
ParamValueStrategy::CallbackHandle {
nullable: false,
style: CallbackParamStyle::BoxedDyn,
},
ParamPassingStrategy::ByValue,
),
transport: Transport::Callback {
callback_id: callback.id.clone(),
nullable: false,
style: CallbackStyle::BoxedDyn,
},
mutability: Mutability::Shared,
len_param: None,
decode_ops: None,
encode_ops: None,
},
};
let method_params = method
.params
.iter()
.map(|param| self.lower_callback_param(param))
.flat_map(|param| self.abi_callback_param_from_plan(param));
let out_params = self.abi_callback_out_params(&method.returns, method.execution_kind());
std::iter::once(handle_param)
.chain(method_params)
.chain(out_params)
}
pub(super) fn abi_callback_param_from_plan(
&self,
param: AbiCallbackParamPlan,
) -> Vec<AbiParam> {
let len_name = ParamName::new(format!("{}_len", param.name.as_str()));
match param.strategy {
AbiCallbackParamStrategy::Scalar(origin) => vec![AbiParam {
name: param.name,
abi_type: AbiType::from(origin.primitive()),
role: ParamRole::Input {
contract: ParamContract::new(
ParamValueStrategy::Scalar(ScalarParamStrategy::PrimitiveValue),
ParamPassingStrategy::ByValue,
),
transport: Transport::Scalar(origin),
mutability: Mutability::Shared,
len_param: None,
decode_ops: None,
encode_ops: None,
},
}],
AbiCallbackParamStrategy::Direct(layout) => {
let codec = self.build_codec(&TypeExpr::Record(layout.record_id.clone()));
let decode_ops = self.expand_decode(&codec);
let encode_ops =
self.expand_encode(&codec, ValueExpr::Named(param.name.as_str().to_string()));
vec![AbiParam {
name: param.name.clone(),
abi_type: AbiType::Struct(layout.record_id.clone()),
role: ParamRole::Input {
contract: ParamContract::new(
ParamValueStrategy::CompositeValue,
ParamPassingStrategy::ByValue,
),
transport: Transport::Composite(layout),
mutability: Mutability::Shared,
len_param: None,
decode_ops: Some(decode_ops),
encode_ops: Some(encode_ops),
},
}]
}
AbiCallbackParamStrategy::Encoded { codec } => {
let decode_ops = self.expand_decode(&codec);
let encode_ops =
self.expand_encode(&codec, ValueExpr::Named(param.name.as_str().to_string()));
vec![
AbiParam {
name: param.name.clone(),
abi_type: AbiType::Pointer(PrimitiveType::U8),
role: ParamRole::Input {
contract: ParamContract::new(
ParamValueStrategy::WireEncoded(WireParamStrategy::SingleValue),
ParamPassingStrategy::ByValue,
),
transport: Transport::Span(SpanContent::Encoded(codec)),
mutability: Mutability::Shared,
len_param: Some(len_name.clone()),
decode_ops: Some(decode_ops),
encode_ops: Some(encode_ops),
},
},
AbiParam {
name: len_name,
abi_type: AbiType::USize,
role: ParamRole::SyntheticLen {
for_param: param.name,
},
},
]
}
}
}
pub(super) fn abi_callback_out_params(
&self,
returns: &ReturnDef,
execution_kind: ExecutionKind,
) -> Vec<AbiParam> {
let has_return =
!matches!(returns, ReturnDef::Void) && execution_kind == ExecutionKind::Sync;
let out_ptr_name = ParamName::new("out_ptr");
let out_len_name = ParamName::new("out_len");
if !has_return {
return Vec::new();
}
let (ret, _) = self.callback_return_shape_and_error(returns);
match &ret.transport {
Some(Transport::Scalar(origin)) => vec![AbiParam {
name: out_ptr_name,
abi_type: AbiType::from(origin.primitive()),
role: ParamRole::OutDirect,
}],
Some(Transport::Handle { .. } | Transport::Callback { .. }) | None => {
vec![AbiParam {
name: out_ptr_name,
abi_type: AbiType::Pointer(PrimitiveType::U8),
role: ParamRole::OutDirect,
}]
}
Some(_) => {
vec![
AbiParam {
name: out_ptr_name.clone(),
abi_type: AbiType::Pointer(PrimitiveType::U8),
role: ParamRole::OutDirect,
},
AbiParam {
name: out_len_name,
abi_type: AbiType::USize,
role: ParamRole::OutLen {
for_param: out_ptr_name,
},
},
]
}
}
}
}