use crate::contract::{self, Consumed};
use crate::corba_idl::corba_scoped_name;
use crate::encode::ProbeError;
use crate::target_keywords::cxx;
use serde_json::Value;
use std::borrow::Cow;
use std::fmt::Write as _;
use zerodds_ast::{Ast, CharWidth, PrimitiveKind, TypeKind, TypeNode};
use zerodds_ast_core::Id;
fn cpp_id(name: &str) -> Cow<'_, str> {
match name.strip_prefix('_') {
Some(bare) => Cow::Owned(cxx(bare).into_owned()),
None => cxx(name),
}
}
enum Place {
Lvalue(String),
Modifier {
object: String,
name: String,
},
}
impl Place {
fn assign(&self, literal: &str) -> String {
match self {
Self::Lvalue(expression) => format!("{expression} = {literal};"),
Self::Modifier { object, name } => format!("{object}.{name}({literal});"),
}
}
fn reference(&self) -> String {
match self {
Self::Lvalue(expression) => expression.clone(),
Self::Modifier { object, name } => format!("{object}.{name}()"),
}
}
}
#[derive(Debug, Default)]
struct Cpp {
declarations: String,
statements: String,
next: usize,
seen: Consumed,
}
impl Cpp {
fn into_source(self) -> String {
let mut out = self.declarations;
out.push_str(&self.statements);
out
}
fn temporary(&mut self, stem: &str) -> String {
self.next += 1;
format!("zd_{stem}_{}", self.next)
}
}
pub fn fill_corba_cpp(
ast: &Ast,
type_: Id<TypeNode>,
values: &Value,
) -> Result<String, ProbeError> {
fill_corba_cpp_recording(ast, type_, values, &mut Consumed::new())
}
pub fn fill_corba_cpp_recording(
ast: &Ast,
type_: Id<TypeNode>,
values: &Value,
seen: &mut Consumed,
) -> Result<String, ProbeError> {
contract::reject_alias_cycle(ast)?;
let mut out = Cpp::default();
let root = contract::root_path(ast, type_);
let result = fill_at(
ast,
type_,
values,
&Place::Lvalue(String::from("sample")),
&mut out,
&root,
);
*seen = core::mem::take(&mut out.seen);
result?;
Ok(out.into_source())
}
fn accessor_is_a_reference(ast: &Ast, type_: Id<TypeNode>) -> bool {
matches!(
ast.type_(contract::resolve_alias(ast, type_))
.map(|node| node.kind.clone()),
Ok(TypeKind::Structure { .. }
| TypeKind::Valuetype { .. }
| TypeKind::Union { .. }
| TypeKind::Sequence { .. }
| TypeKind::Array { .. })
)
}
fn array_after_aliases(ast: &Ast, type_: Id<TypeNode>) -> Option<TypeKind> {
let kind = ast
.type_(contract::resolve_alias(ast, type_))
.map(|node| node.kind.clone())
.ok()?;
matches!(kind, TypeKind::Array { .. }).then_some(kind)
}
#[allow(clippy::too_many_lines)]
fn fill_at(
ast: &Ast,
type_: Id<TypeNode>,
value: &Value,
place: &Place,
out: &mut Cpp,
path: &str,
) -> Result<(), ProbeError> {
let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
detail: format!("{error}"),
})?;
match &node.kind {
TypeKind::Primitive(kind) => {
let literal = primitive_literal(*kind, value, path)?;
out.seen.scalar(path);
let _ = writeln!(out.statements, "{}", place.assign(&literal));
Ok(())
}
TypeKind::String {
width: CharWidth::Char,
..
} => {
let text = contract::text(value, path)?;
out.seen.scalar(path);
let _ = writeln!(
out.statements,
"{}",
place.assign(&format!("CORBA::string_dup({})", cpp_string(text)))
);
Ok(())
}
TypeKind::String {
width: CharWidth::WChar,
..
} => {
let text = contract::text(value, path)?;
out.seen.scalar(path);
let buffer = out.temporary("wstr");
let mut units = String::new();
for unit in text.encode_utf16() {
let _ = write!(units, "0x{unit:04x}, ");
}
let _ = writeln!(
out.declarations,
"static CORBA::WChar {buffer}[] = {{ {units}0x0000 }};"
);
let _ = writeln!(
out.statements,
"{}",
place.assign(&format!("CORBA::wstring_dup({buffer})"))
);
Ok(())
}
TypeKind::Fixed { .. } => {
let text = contract::text(value, path)?;
out.seen.scalar(path);
let _ = writeln!(
out.statements,
"{}",
place.assign(&format!("CORBA::Fixed({})", cpp_string(text)))
);
Ok(())
}
TypeKind::Enumeration { .. } => {
let (name, _) = contract::enumerator(ast, type_, value, path)?;
out.seen.scalar(path);
let _ = writeln!(
out.statements,
"{}",
place.assign(&enumerator_name(ast, type_, &name))
);
Ok(())
}
TypeKind::Alias { aliased } => {
let target = aliased.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the typedef target is unresolved"),
})?;
fill_at(ast, target, value, place, out, path)
}
TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
let object = contract::object(value, path)?;
let members = contract::members(ast, type_);
let declared: Vec<String> = members.iter().map(|member| member.name.clone()).collect();
contract::reject_unknown_keys(object, &declared, path)?;
let target = place.reference();
for member in &members {
let Some(member_value) = contract::member_value(object, member, path)? else {
continue;
};
fill_at(
ast,
member.type_,
member_value,
&Place::Lvalue(format!("{target}.{}", cpp_id(&member.name))),
out,
&contract::join(path, &member.name),
)?;
}
Ok(())
}
TypeKind::Union { discriminator, .. } => {
let object = contract::object(value, path)?;
let discriminant = contract::discriminator(object, path)?;
let target = place.reference();
for (name, member_value) in object {
if name == "_d" {
continue;
}
let Some(member_type) = union_member(ast, type_, name) else {
return Err(ProbeError::Unknown {
path: contract::join(path, name),
declared: union_member_names(ast, type_),
});
};
contract::reject_null(member_value, &contract::join(path, name))?;
if let Some(TypeKind::Array { element, dims }) =
array_after_aliases(ast, member_type)
{
let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the array element is unresolved"),
})?;
let counts =
contract::array_dimensions(ast, &dims, &contract::join(path, name))?;
let temporary = out.temporary("case");
let accessor = cpp_id(name);
let _ = writeln!(
out.statements,
"std::remove_const<std::remove_pointer<decltype({target}.{accessor}())>::type>::type {temporary}[{}];",
counts[0]
);
fill_array(
ast,
element,
member_value,
&temporary,
out,
&contract::join(path, name),
&counts,
)?;
let _ = writeln!(out.statements, "{target}.{accessor}({temporary});");
} else if accessor_is_a_reference(ast, member_type) {
let temporary = out.temporary("case");
let accessor = cpp_id(name);
let _ = writeln!(
out.statements,
"std::remove_reference<decltype({target}.{accessor}())>::type {temporary};"
);
fill_at(
ast,
member_type,
member_value,
&Place::Lvalue(temporary.clone()),
out,
&contract::join(path, name),
)?;
let _ = writeln!(out.statements, "{target}.{accessor}({temporary});");
} else {
fill_at(
ast,
member_type,
member_value,
&Place::Modifier {
object: target.clone(),
name: cpp_id(name).into_owned(),
},
out,
&contract::join(path, name),
)?;
}
}
let discriminator = discriminator
.resolved()
.ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the discriminator is unresolved"),
})?;
fill_at(
ast,
discriminator,
discriminant,
&Place::Modifier {
object: target,
name: String::from("_d"),
},
out,
&contract::join(path, "_d"),
)
}
TypeKind::Array { element, dims } => {
let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the array element is unresolved"),
})?;
let counts = contract::array_dimensions(ast, dims, path)?;
fill_array(ast, element, value, &place.reference(), out, path, &counts)
}
TypeKind::Sequence { element, bound } => {
let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the sequence element is unresolved"),
})?;
let items = contract::bounded_items(ast, value, *bound, "elements", path)?;
let target = place.reference();
let _ = writeln!(out.statements, "{target}.length({});", items.len());
for (index, item) in items.iter().enumerate() {
fill_at(
ast,
element,
item,
&Place::Lvalue(format!("{target}[{index}]")),
out,
&format!("{path}[{index}]"),
)?;
}
Ok(())
}
TypeKind::Bitmask { .. } => Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a bitmask, which is XTypes 1.3 §7.3.1.2.1.4 and not CORBA IDL",
}),
TypeKind::Bitset { .. } => Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a bitset, which is XTypes 1.3 §7.3.1.2.1.3 and not CORBA IDL",
}),
TypeKind::Map {
key,
value: entry,
bound,
} => {
let key_type = key.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the map key type is unresolved"),
})?;
let value_type = entry.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the map value type is unresolved"),
})?;
let entries = contract::bounded_items(ast, value, *bound, "entries", path)?;
let target = place.reference();
for (index, pair) in entries.iter().enumerate() {
let entry_path = format!("{path}[{index}]");
let (entry_key, entry_value) = contract::map_entry(pair, &entry_path)?;
let key_path = contract::join(&entry_path, "key");
let key_literal = map_key_literal(ast, key_type, entry_key, &key_path, out)?;
out.seen.scalar(&key_path);
fill_at(
ast,
value_type,
entry_value,
&Place::Lvalue(format!("{target}[{key_literal}]")),
out,
&contract::join(&entry_path, "value"),
)?;
}
Ok(())
}
other => Err(ProbeError::Unfilled {
path: path.to_string(),
kind: other.as_str(),
}),
}
}
fn map_key_literal(
ast: &Ast,
key_type: Id<TypeNode>,
value: &Value,
path: &str,
out: &mut Cpp,
) -> Result<String, ProbeError> {
let resolved = contract::resolve_alias(ast, key_type);
let node = ast.type_(resolved).map_err(|error| ProbeError::NoLayout {
detail: format!("{error}"),
})?;
match &node.kind {
TypeKind::Primitive(kind) => primitive_literal(*kind, value, path),
TypeKind::String {
width: CharWidth::Char,
..
} => {
let text = contract::text(value, path)?;
Ok(cpp_string(text))
}
TypeKind::String {
width: CharWidth::WChar,
..
} => {
let text = contract::text(value, path)?;
let buffer = out.temporary("wkey");
let mut units = String::new();
for unit in text.encode_utf16() {
let _ = write!(units, "0x{unit:04x}, ");
}
let _ = writeln!(
out.declarations,
"static CORBA::WChar {buffer}[] = {{ {units}0x0000 }};"
);
Ok(buffer)
}
TypeKind::Enumeration { .. } => {
let (name, _) = contract::enumerator(ast, resolved, value, path)?;
Ok(enumerator_name(ast, resolved, &name))
}
other => Err(ProbeError::Unfilled {
path: path.to_string(),
kind: other.as_str(),
}),
}
}
#[allow(clippy::too_many_arguments)]
fn fill_array(
ast: &Ast,
element: Id<TypeNode>,
value: &Value,
lvalue: &str,
out: &mut Cpp,
path: &str,
counts: &[usize],
) -> Result<(), ProbeError> {
let Some((&count, rest)) = counts.split_first() else {
return fill_at(
ast,
element,
value,
&Place::Lvalue(lvalue.to_string()),
out,
path,
);
};
let items = contract::array(value, path)?;
if items.len() != count {
return Err(ProbeError::Mismatch {
path: path.to_string(),
expected: format!("exactly {count} elements"),
found: format!("{}", items.len()),
});
}
for (index, item) in items.iter().enumerate() {
fill_array(
ast,
element,
item,
&format!("{lvalue}[{index}]"),
out,
&format!("{path}[{index}]"),
rest,
)?;
}
Ok(())
}
#[derive(Debug, Default)]
struct Dump {
body: String,
depth: usize,
next: usize,
rec: Vec<Id<TypeNode>>,
}
impl Dump {
fn line(&mut self, text: &str) {
for _ in 0..self.depth {
self.body.push_str(" ");
}
self.body.push_str(text);
self.body.push('\n');
}
fn open(&mut self, text: &str) {
self.line(text);
self.depth += 1;
}
fn close(&mut self, text: &str) {
self.depth = self.depth.saturating_sub(1);
self.line(text);
}
fn raw(&mut self, literal: &str) {
let escaped = literal.replace('\\', "\\\\").replace('"', "\\\"");
self.line(&format!("zdj_raw(\"{escaped}\");"));
}
fn temporary(&mut self, stem: &str) -> String {
self.next += 1;
format!("zd_{stem}_{}", self.next)
}
}
pub fn dump_corba_cpp(ast: &Ast, type_: Id<TypeNode>) -> Result<String, ProbeError> {
contract::reject_alias_cycle(ast)?;
let mut out = Dump {
rec: crate::recursion::recursive_aggregates(ast, type_),
..Dump::default()
};
if !out.rec.is_empty() {
out.open("struct zd_rec {");
for position in 0..out.rec.len() {
let id = out.rec[position];
let name = corba_cpp_type(ast, id)?;
out.open(&format!(
"static void dump_{position}(const {name} &zdv) {{"
));
dump_aggregate(ast, id, "zdv", &mut out, &format!("<{name}>"))?;
out.close("}");
}
out.close("};");
}
dump_at(ast, type_, "sample", &mut out, "")?;
out.line("zdj_raw(\"\\n\");");
Ok(out.body)
}
fn corba_cpp_type(ast: &Ast, type_: Id<TypeNode>) -> Result<String, ProbeError> {
let segments = corba_scoped_name(ast, type_).ok_or_else(|| ProbeError::NoLayout {
detail: String::from("a type on a cycle has no scoped name"),
})?;
let mut out = String::new();
for segment in &segments {
out.push_str("::");
out.push_str(&cpp_id(segment));
}
Ok(out)
}
#[allow(clippy::too_many_lines)]
fn dump_at(
ast: &Ast,
type_: Id<TypeNode>,
expr: &str,
out: &mut Dump,
path: &str,
) -> Result<(), ProbeError> {
let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
detail: format!("{error}"),
})?;
match &node.kind {
TypeKind::Primitive(kind) => {
out.line(&primitive_print(*kind, expr, path)?);
Ok(())
}
TypeKind::String {
width: CharWidth::Char,
..
} => {
out.line(&format!("zdj_string((const char *)({expr}));"));
Ok(())
}
TypeKind::String {
width: CharWidth::WChar,
..
} => {
out.line(&format!(
"zdj_wstring_corba((const CORBA::WChar *)({expr}));"
));
Ok(())
}
TypeKind::Fixed { .. } => {
out.line(&format!("zdj_fixed_corba({expr});"));
Ok(())
}
TypeKind::Enumeration { .. } => {
dump_enumeration(ast, type_, expr, out);
Ok(())
}
TypeKind::Alias { aliased } => {
let target = aliased.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the typedef target is unresolved"),
})?;
dump_at(ast, target, expr, out, path)
}
TypeKind::Structure { .. } | TypeKind::Valuetype { .. } | TypeKind::Union { .. } => {
if let Some(position) = out.rec.iter().position(|id| *id == type_) {
out.line(&format!("zd_rec::dump_{position}({expr});"));
return Ok(());
}
dump_aggregate(ast, type_, expr, out, path)
}
TypeKind::Array { element, dims } => {
let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the array element is unresolved"),
})?;
let counts = contract::array_dimensions(ast, dims, path)?;
dump_array(ast, element, expr, out, path, &counts)
}
TypeKind::Sequence { element, .. } => {
let element = element.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the sequence element is unresolved"),
})?;
let count = out.temporary("n");
let index = out.temporary("i");
out.open("{");
out.line(&format!("CORBA::ULong {count} = {expr}.length();"));
out.raw("[");
out.open(&format!(
"for (CORBA::ULong {index} = 0; {index} < {count}; {index}++) {{"
));
out.line(&format!("if ({index} > 0) {{ zdj_raw(\",\"); }}"));
dump_at(
ast,
element,
&format!("{expr}[{index}]"),
out,
&format!("{path}[]"),
)?;
out.close("}");
out.raw("]");
out.close("}");
Ok(())
}
TypeKind::Map {
key, value: entry, ..
} => {
let key_type = key.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the map key type is unresolved"),
})?;
let value_type = entry.resolved().ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the map value type is unresolved"),
})?;
let first = out.temporary("first");
let pair = out.temporary("entry");
out.open("{");
out.line(&format!("int {first} = 1;"));
out.raw("[");
out.open(&format!("for (const auto &{pair} : {expr}) {{"));
out.line(&format!("if (!{first}) {{ zdj_raw(\",\"); }} {first} = 0;"));
out.raw("[");
dump_at(
ast,
key_type,
&format!("{pair}.first"),
out,
&format!("{path}[].key"),
)?;
out.raw(",");
dump_at(
ast,
value_type,
&format!("{pair}.second"),
out,
&format!("{path}[].value"),
)?;
out.raw("]");
out.close("}");
out.raw("]");
out.close("}");
Ok(())
}
TypeKind::Bitmask { .. } => Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a bitmask, which is XTypes 1.3 §7.3.1.2.1.4 and not CORBA IDL",
}),
TypeKind::Bitset { .. } => Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a bitset, which is XTypes 1.3 §7.3.1.2.1.3 and not CORBA IDL",
}),
other => Err(ProbeError::Unfilled {
path: path.to_string(),
kind: other.as_str(),
}),
}
}
#[allow(clippy::too_many_lines)]
fn dump_aggregate(
ast: &Ast,
type_: Id<TypeNode>,
expr: &str,
out: &mut Dump,
path: &str,
) -> Result<(), ProbeError> {
let node = ast.type_(type_).map_err(|error| ProbeError::NoLayout {
detail: format!("{error}"),
})?;
match &node.kind {
TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
out.open("{");
out.line("int zd_first = 1;");
out.raw("{");
for member in contract::members(ast, type_) {
out.line(&format!("zdj_key(&zd_first, \"{}\");", member.name));
dump_at(
ast,
member.type_,
&format!("{expr}.{}", cpp_id(&member.name)),
out,
&contract::join(path, &member.name),
)?;
}
out.raw("}");
out.close("}");
Ok(())
}
TypeKind::Union { discriminator, .. } => {
let discriminator = discriminator
.resolved()
.ok_or_else(|| ProbeError::NoLayout {
detail: format!("{path}: the discriminator is unresolved"),
})?;
out.open("{");
out.line("int zd_first = 1;");
out.raw("{");
out.line("zdj_key(&zd_first, \"_d\");");
dump_at(
ast,
discriminator,
&format!("{expr}._d()"),
out,
&format!("{path}._d"),
)?;
let (width, suffix) = contract::discriminator_c_width(ast, discriminator);
out.open(&format!("switch (({width})({expr}._d())) {{"));
for id in ast.cases_of(type_) {
let Ok(case) = ast.case(*id) else { continue };
let Ok(member) = ast.member(case.member) else {
continue;
};
let Some(member_type) = member.type_.resolved() else {
continue;
};
for label in &case.labels {
match label.expr() {
Some(expression) => match ast.eval_integral(expression) {
Ok(value) => out.line(&format!("case {value}{suffix}:")),
Err(error) => {
return Err(ProbeError::NoLayout {
detail: format!(
"{path}: a case label does not evaluate: {error}"
),
});
}
},
None => out.line("default:"),
}
}
out.depth += 1;
out.line(&format!("zdj_key(&zd_first, \"{}\");", member.name));
dump_at(
ast,
member_type,
&format!("{expr}.{}()", cpp_id(&member.name)),
out,
&contract::join(path, &member.name),
)?;
out.line("break;");
out.depth = out.depth.saturating_sub(1);
}
out.close("}");
out.raw("}");
out.close("}");
Ok(())
}
other => Err(ProbeError::NoLayout {
detail: format!("{path}: not an aggregate: {}", other.as_str()),
}),
}
}
fn dump_array(
ast: &Ast,
element: Id<TypeNode>,
expr: &str,
out: &mut Dump,
path: &str,
counts: &[usize],
) -> Result<(), ProbeError> {
let Some((&count, rest)) = counts.split_first() else {
return dump_at(ast, element, expr, out, path);
};
out.raw("[");
for index in 0..count {
if index > 0 {
out.raw(",");
}
dump_array(
ast,
element,
&format!("{expr}[{index}]"),
out,
&format!("{path}[{index}]"),
rest,
)?;
}
out.raw("]");
Ok(())
}
fn dump_enumeration(ast: &Ast, type_: Id<TypeNode>, expr: &str, out: &mut Dump) {
out.open(&format!("switch ((long long)({expr})) {{"));
for id in ast.enumerators_of(type_) {
let Ok(node) = ast.enumerator(*id) else {
continue;
};
let scoped = enumerator_name(ast, type_, &node.name);
out.line(&format!("case (long long){scoped}:"));
out.line(&format!(" zdj_raw(\"\\\"{}\\\"\"); break;", node.name));
}
out.line(&format!(
"default: zdj_enum_unknown((long long)({expr})); break;"
));
out.close("}");
}
fn enumerator_name(ast: &Ast, type_: Id<TypeNode>, enumerator: &str) -> String {
let Some(scoped) = corba_scoped_name(ast, type_) else {
return format!("::{}", cpp_id(enumerator));
};
let Some((_, scope)) = scoped.split_last() else {
return format!("::{}", cpp_id(enumerator));
};
let mut out = String::from("::");
for segment in scope {
out.push_str(&cpp_id(segment));
out.push_str("::");
}
out.push_str(&cpp_id(enumerator));
out
}
fn primitive_literal(kind: PrimitiveKind, value: &Value, path: &str) -> Result<String, ProbeError> {
Ok(match kind {
PrimitiveKind::Boolean => {
if contract::boolean(value, path)? {
"1".to_string()
} else {
"0".to_string()
}
}
PrimitiveKind::Char => {
let code = contract::character_for(kind, value, path)?;
format!("(CORBA::Char)0x{:02x}", code as u32)
}
PrimitiveKind::WChar => {
let code = contract::character_for(kind, value, path)?;
format!("(CORBA::WChar)0x{:04x}", code as u32)
}
PrimitiveKind::Octet | PrimitiveKind::UInt8 | PrimitiveKind::Int8 => {
let integer = contract::integer_for(kind, value, path)?;
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let octet = (integer as i64) as u8;
format!("(CORBA::Octet)0x{octet:02x}")
}
PrimitiveKind::Float32 => format!("{:.17e}F", contract::float(value, path)?),
PrimitiveKind::Float64 => format!("{:.17e}", contract::float(value, path)?),
PrimitiveKind::Int64 => {
let n = contract::integer_for(kind, value, path)?;
if n == i128::from(i64::MIN) {
"(-9223372036854775807LL - 1)".to_string()
} else {
format!("{n}LL")
}
}
PrimitiveKind::UInt64 => format!("{}ULL", contract::integer_for(kind, value, path)?),
PrimitiveKind::Float128 => {
return Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a long double, whose C++ mapping is x87 extended and not §9.3.1.3's binary128",
});
}
_ => format!("{}", contract::integer_for(kind, value, path)?),
})
}
fn primitive_print(kind: PrimitiveKind, expr: &str, path: &str) -> Result<String, ProbeError> {
Ok(match kind {
PrimitiveKind::Boolean => format!("zdj_bool((int)({expr}));"),
PrimitiveKind::Char => format!("zdj_char((char)({expr}));"),
PrimitiveKind::WChar => format!("zdj_wchar((unsigned long)({expr}));"),
PrimitiveKind::Int8 => format!("zdj_i64((long long)(signed char)({expr}));"),
PrimitiveKind::Octet | PrimitiveKind::UInt8 => {
format!("zdj_u64((unsigned long long)(unsigned char)({expr}));")
}
PrimitiveKind::Int16 | PrimitiveKind::Int32 => {
format!("zdj_i64((long long)({expr}));")
}
PrimitiveKind::UInt16 | PrimitiveKind::UInt32 => {
format!("zdj_u64((unsigned long long)({expr}));")
}
PrimitiveKind::Int64 => format!("zdj_i64_string((long long)({expr}));"),
PrimitiveKind::UInt64 => format!("zdj_u64_string((unsigned long long)({expr}));"),
PrimitiveKind::Float32 => format!("zdj_f32((float)({expr}));"),
PrimitiveKind::Float64 => format!("zdj_f64((double)({expr}));"),
PrimitiveKind::Float128 => {
return Err(ProbeError::Unsupported {
path: path.to_string(),
kind: "a long double, whose C++ mapping is x87 extended and not §9.3.1.3's binary128",
});
}
})
}
fn union_member(ast: &Ast, union: Id<TypeNode>, name: &str) -> Option<Id<TypeNode>> {
contract::union_cases(ast, union)
.into_iter()
.find(|case| case.name == name)
.map(|case| case.type_)
}
fn union_member_names(ast: &Ast, union: Id<TypeNode>) -> Vec<String> {
contract::union_cases(ast, union)
.into_iter()
.map(|case| case.name)
.collect()
}
fn cpp_string(text: &str) -> String {
let mut out = String::from("\"");
for byte in text.as_bytes() {
match byte {
b'"' => out.push_str("\\\""),
b'\\' => out.push_str("\\\\"),
b'\n' => out.push_str("\\n"),
b'\r' => out.push_str("\\r"),
b'\t' => out.push_str("\\t"),
0x20..=0x7E => out.push(char::from(*byte)),
other => {
let _ = write!(out, "\\{other:03o}");
}
}
}
out.push('"');
out
}