Move header file generation logic

WIP, still need to get the length of the metadat object, but otherwise
everything should be working.
This commit is contained in:
Mark McCaskey
2020-09-03 17:45:27 -07:00
parent 3f109bf5c7
commit 86e934a755
11 changed files with 502 additions and 471 deletions

1
Cargo.lock generated
View File

@@ -2229,6 +2229,7 @@ dependencies = [
"wasmer-engine-native",
"wasmer-engine-object-file",
"wasmer-types",
"wasmer-vm",
"wasmer-wasi",
"wasmer-wasi-experimental-io-devices",
"wasmer-wast",

View File

@@ -35,6 +35,7 @@ wasmer-engine-object-file = { version = "1.0.0-alpha01.0", path = "../engine-obj
wasmer-wasi = { version = "1.0.0-alpha01.0", path = "../wasi", optional = true }
wasmer-wasi-experimental-io-devices = { version = "1.0.0-alpha01.0", path = "../wasi-experimental-io-devices", optional = true }
wasmer-wast = { version = "1.0.0-alpha01.0", path = "../../tests/lib/wast", optional = true }
wasmer-vm = { version = "1.0.0-alpha01.0", path = "../vm" }
wasmer-cache = { version = "1.0.0-alpha01.0", path = "../cache", optional = true }
wasmer-types = { version = "1.0.0-alpha01.0", path = "../wasmer-types" }
atty = "0.2"

View File

@@ -106,10 +106,11 @@ impl Compile {
#[cfg(feature = "object-file")]
if engine_type == EngineType::ObjectFile {
use wasmer_engine_object_file::ObjectFileArtifact;
let symbol_registry = module.artifact().symbol_registry();
let module_info = module.info();
let header_file_src =
crate::header_file_generation::generate_header_file(module_info, symbol_registry);
if let Some(obj_file) = module.artifact().downcast_ref::<ObjectFileArtifact>() {
let header_file_src = obj_file.generate_header_file();
let header_path = self.header_path.as_ref().cloned().unwrap_or_else(|| {
let mut hp = PathBuf::from(
self.path
@@ -133,10 +134,6 @@ impl Compile {
"✔ Header file generated successfully at `{}`.",
header_path.display(),
);
} else {
// TODO: handle error
panic!("Downcast failed!")
}
}
Ok(())
}

View File

@@ -0,0 +1,456 @@
//! A convenient little abstraction for building up C expressions and generating
//! simple C code.
/// A Type in the C language.
#[derive(Debug, Clone)]
pub enum CType {
/// C `void` type.
Void,
/// A pointer to some other type.
PointerTo {
/// Whether the pointer is `const`.
is_const: bool,
/// The type that the pointer points to.
inner: Box<CType>,
},
/// C 8 bit unsigned integer type.
U8,
/// C 16 bit unsigned integer type.
U16,
/// C 32 bit unsigned integer type.
U32,
/// C 64 bit unsigned integer type.
U64,
/// C pointer sized unsigned integer type.
USize,
/// C 8 bit signed integer type.
I8,
/// C 16 bit signed integer type.
I16,
/// C 32 bit signed integer type.
I32,
/// C 64 bit signed integer type.
I64,
/// C pointer sized signed integer type.
ISize,
/// A function or function pointer.
Function {
/// The arguments the function takes.
arguments: Vec<CType>,
/// The return value if it has one
///
/// None is equivalent to Some(Box(Ctype::Void)).
return_value: Option<Box<CType>>,
},
}
impl CType {
/// Convenience function to get a mutable void pointer type.
pub fn void_ptr() -> Self {
CType::PointerTo {
is_const: false,
inner: Box::new(CType::Void),
}
}
/// Convenience function to get a const void pointer type.
#[allow(dead_code)]
pub fn const_void_ptr() -> Self {
CType::PointerTo {
is_const: false,
inner: Box::new(CType::Void),
}
}
/// Generate the C source code for a type into the given `String`.
fn generate_c(&self, w: &mut String) {
match &self {
Self::Void => {
w.push_str("void");
}
Self::PointerTo { is_const, inner } => {
if *is_const {
w.push_str("const ");
}
inner.generate_c(w);
w.push_str("*");
}
Self::U8 => {
w.push_str("unsigned char");
}
Self::U16 => {
w.push_str("unsigned short");
}
Self::U32 => {
w.push_str("unsigned int");
}
Self::U64 => {
w.push_str("unsigned long long");
}
Self::USize => {
w.push_str("unsigned size_t");
}
Self::I8 => {
w.push_str("char");
}
Self::I16 => {
w.push_str("short");
}
Self::I32 => {
w.push_str("int");
}
Self::I64 => {
w.push_str("long long");
}
Self::ISize => {
w.push_str("size_t");
}
Self::Function {
arguments,
return_value,
} => {
// function with no, name, assume it's a function pointer
let ret: CType = return_value
.as_ref()
.map(|i: &Box<CType>| (&**i).clone())
.unwrap_or_default();
ret.generate_c(w);
w.push(' ');
w.push_str("(*)");
w.push('(');
if arguments.len() > 1 {
for arg in &arguments[..arguments.len() - 1] {
arg.generate_c(w);
w.push_str(", ");
}
arguments.last().unwrap().generate_c(w);
} else if arguments.len() == 1 {
arguments[0].generate_c(w);
}
w.push(')');
}
}
}
/// Generate the C source code for a type with a nameinto the given `String`.
fn generate_c_with_name(&self, name: &String, w: &mut String) {
match &self {
Self::PointerTo { .. }
| Self::Void
| Self::U8
| Self::U16
| Self::U32
| Self::U64
| Self::USize
| Self::I8
| Self::I16
| Self::I32
| Self::I64
| Self::ISize => {
self.generate_c(w);
w.push(' ');
w.push_str(name);
}
Self::Function {
arguments,
return_value,
} => {
let ret: CType = return_value
.as_ref()
.map(|i: &Box<CType>| (&**i).clone())
.unwrap_or_default();
ret.generate_c(w);
w.push(' ');
w.push_str(&name);
w.push('(');
if arguments.len() > 1 {
for arg in &arguments[..arguments.len() - 1] {
arg.generate_c(w);
w.push_str(", ");
}
arguments.last().unwrap().generate_c(w);
} else if arguments.len() == 1 {
arguments[0].generate_c(w);
}
w.push(')');
}
}
}
}
impl Default for CType {
fn default() -> CType {
CType::Void
}
}
/// A statement in the C programming language. This may not be exact to what an
/// AST would look like or what the C standard says about the C language, it's
/// simply a structed way to organize data for generating C code.
#[derive(Debug, Clone)]
pub enum CStatement {
/// A declaration of some kind.
Declaration {
/// The name of the thing being declared.
name: String,
/// Whether the thing being declared is an array.
// TODO: probably make this part of CType
array: bool,
/// Whether the thing being declared is `extern`.
is_extern: bool,
/// Whether the thing being declared is `const`.
is_const: bool,
/// The type of the thing being declared.
ctype: CType,
/// The definition of the thing being declared.
///
/// This is useful for initializing constant arrays, for example.
definition: Option<Box<CStatement>>,
},
/// A literal array of CStatements.
LiteralArray {
/// The contents of the array.
items: Vec<CStatement>,
},
/// A literal constant value, passed through directly as a string.
LiteralConstant {
/// The raw value acting as a constant.
value: String,
},
}
impl CStatement {
/// Generate C source code for the given CStatement.
fn generate_c(&self, w: &mut String) {
match &self {
Self::Declaration {
name,
array,
is_extern,
is_const,
ctype,
definition,
} => {
if *is_const {
w.push_str("const ");
}
if *is_extern {
w.push_str("extern ");
}
ctype.generate_c_with_name(name, w);
// TODO: array should be part of the type
if *array {
w.push_str("[]");
}
if let Some(def) = definition {
w.push_str(" = ");
def.generate_c(w);
}
w.push(';');
w.push('\n');
}
Self::LiteralArray { items } => {
w.push('{');
if !items.is_empty() {
w.push('\n');
}
for item in items {
w.push('\t');
item.generate_c(w);
w.push(',');
w.push('\n');
}
w.push('}');
}
Self::LiteralConstant { value } => {
w.push_str(&value);
}
}
}
}
/// Generate C source code from some `CStatements` into a String.
// TODO: add config section
pub fn generate_c(statements: &[CStatement]) -> String {
let mut out = String::new();
for statement in statements {
statement.generate_c(&mut out);
}
out
}
// TODO: split the bottom part into its own file
use wasmer_compiler::{Symbol, SymbolRegistry};
use wasmer_vm::ModuleInfo;
/// Generate the header file that goes with the generated object file.
pub fn generate_header_file(
module_info: &ModuleInfo,
symbol_registry: &dyn SymbolRegistry,
) -> String {
let mut c_statements = vec![];
c_statements.push(CStatement::Declaration {
name: "module_bytes_len".to_string(),
array: false,
is_extern: false,
is_const: true,
ctype: CType::U32,
definition: Some(Box::new(CStatement::LiteralConstant {
value: "0".to_string(), //todo!("get the metadata length from somewhere"), //format!("{}", self.metadata_length),
})),
});
c_statements.push(CStatement::Declaration {
name: "WASMER_METADATA".to_string(),
array: true,
is_extern: true,
is_const: true,
ctype: CType::U8,
definition: None,
});
for (function_local_index, _sig_index) in
module_info
.functions
.iter()
.filter_map(|(f_index, sig_index)| {
Some((module_info.local_func_index(f_index)?, sig_index))
})
{
let function_name =
symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index));
// TODO: figure out the signature here too
c_statements.push(CStatement::Declaration {
name: function_name.clone(),
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::Void],
return_value: None,
},
definition: None,
});
}
// function pointer array
{
let mut function_pointer_array_statements = vec![];
for (function_local_index, _sig_index) in
module_info
.functions
.iter()
.filter_map(|(f_index, sig_index)| {
Some((module_info.local_func_index(f_index)?, sig_index))
})
{
let function_name =
symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index));
// TODO: figure out the signature here too
function_pointer_array_statements.push(CStatement::LiteralConstant {
value: function_name.clone(),
});
}
c_statements.push(CStatement::Declaration {
name: "function_pointers".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: function_pointer_array_statements,
})),
});
}
for (sig_index, _func_type) in module_info.signatures.iter() {
let function_name =
symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
c_statements.push(CStatement::Declaration {
name: function_name.clone(),
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
return_value: None,
},
definition: None,
});
}
// function trampolines
{
let mut function_trampoline_statements = vec![];
for (sig_index, _vm_shared_index) in module_info.signatures.iter() {
let function_name =
symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
function_trampoline_statements.push(CStatement::LiteralConstant {
value: function_name,
});
}
c_statements.push(CStatement::Declaration {
name: "function_trampolines".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: function_trampoline_statements,
})),
});
}
for func_index in module_info
.functions
.keys()
.take(module_info.num_imported_functions)
{
let function_name =
symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
// TODO: figure out the signature here
c_statements.push(CStatement::Declaration {
name: function_name,
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
return_value: None,
},
definition: None,
});
}
// dynamic function trampoline pointer array
{
let mut dynamic_function_trampoline_statements = vec![];
for func_index in module_info
.functions
.keys()
.take(module_info.num_imported_functions)
{
let function_name =
symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
dynamic_function_trampoline_statements.push(CStatement::LiteralConstant {
value: function_name,
});
}
c_statements.push(CStatement::Declaration {
name: "dynamic_function_trampoline_pointers".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: dynamic_function_trampoline_statements,
})),
});
}
generate_c(&c_statements)
}

View File

@@ -20,6 +20,7 @@ pub mod commands;
pub mod common;
#[macro_use]
pub mod error;
pub mod header_file_generation;
#[cfg(feature = "debug")]
pub mod logging;
pub mod store;

View File

@@ -8,7 +8,7 @@ use crate::serialize::SerializableCompilation;
use crate::serialize::SerializableModule;
use crate::unwind::UnwindRegistry;
use std::sync::{Arc, Mutex};
use wasmer_compiler::{CompileError, Features, Triple};
use wasmer_compiler::{CompileError, Features, SymbolRegistry, Triple};
#[cfg(feature = "compiler")]
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment};
use wasmer_engine::{
@@ -303,4 +303,8 @@ impl Artifact for JITArtifact {
serialized.extend(bytes);
Ok(serialized)
}
fn symbol_registry(&self) -> &dyn SymbolRegistry {
unimplemented!("TODO: figure out why engine JIT doesn't have a SymbolRegistry")
}
}

View File

@@ -598,4 +598,8 @@ impl Artifact for NativeArtifact {
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
Ok(std::fs::read(&self.sharedobject_path)?)
}
fn symbol_registry(&self) -> &dyn SymbolRegistry {
&self.metadata
}
}

View File

@@ -237,174 +237,6 @@ impl ObjectFileArtifact {
Self::from_parts_crosscompiled(&mut *engine_inner, metadata, obj_bytes, metadata_length)
}
/// Generate the header file that goes with the generated object file.
pub fn generate_header_file(&self) -> String {
use crate::header::*;
let mut c_statements = vec![];
c_statements.push(CStatement::Declaration {
name: "module_bytes_len".to_string(),
array: false,
is_extern: false,
is_const: true,
ctype: CType::U32,
definition: Some(Box::new(CStatement::LiteralConstant {
value: format!("{}", self.metadata_length),
})),
});
c_statements.push(CStatement::Declaration {
name: "WASMER_METADATA".to_string(),
array: true,
is_extern: true,
is_const: true,
ctype: CType::U8,
definition: None,
});
for (function_local_index, _function_len) in self.metadata.function_body_lengths.iter() {
let function_name = self
.metadata
.symbol_to_name(Symbol::LocalFunction(function_local_index));
// TODO: figure out the signature here too
c_statements.push(CStatement::Declaration {
name: function_name.clone(),
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::Void],
return_value: None,
},
definition: None,
});
}
// function pointer array
{
let mut function_pointer_array_statements = vec![];
for (function_local_index, _function_len) in self.metadata.function_body_lengths.iter()
{
let function_name = self
.metadata
.symbol_to_name(Symbol::LocalFunction(function_local_index));
// TODO: figure out the signature here too
function_pointer_array_statements.push(CStatement::LiteralConstant {
value: function_name.clone(),
});
}
c_statements.push(CStatement::Declaration {
name: "function_pointers".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: function_pointer_array_statements,
})),
});
}
for (sig_index, _func_type) in self.metadata.compile_info.module.signatures.iter() {
let function_name = self
.metadata
.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
c_statements.push(CStatement::Declaration {
name: function_name.clone(),
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
return_value: None,
},
definition: None,
});
}
// function trampolines
{
let mut function_trampoline_statements = vec![];
for (sig_index, _vm_shared_index) in self.metadata.compile_info.module.signatures.iter()
{
let function_name = self
.metadata
.symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
function_trampoline_statements.push(CStatement::LiteralConstant {
value: function_name,
});
}
c_statements.push(CStatement::Declaration {
name: "function_trampolines".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: function_trampoline_statements,
})),
});
}
for func_index in self
.metadata
.compile_info
.module
.functions
.keys()
.take(self.metadata.compile_info.module.num_imported_functions)
{
let function_name = self
.metadata
.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
// TODO: figure out the signature here
c_statements.push(CStatement::Declaration {
name: function_name,
array: false,
is_extern: false,
is_const: false,
ctype: CType::Function {
arguments: vec![CType::void_ptr(), CType::void_ptr(), CType::void_ptr()],
return_value: None,
},
definition: None,
});
}
// dynamic function trampoline pointer array
{
let mut dynamic_function_trampoline_statements = vec![];
for func_index in self
.metadata
.compile_info
.module
.functions
.keys()
.take(self.metadata.compile_info.module.num_imported_functions)
{
let function_name = self
.metadata
.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
dynamic_function_trampoline_statements.push(CStatement::LiteralConstant {
value: function_name,
});
}
c_statements.push(CStatement::Declaration {
name: "dynamic_function_trampoline_pointers".to_string(),
array: true,
is_extern: false,
is_const: true,
ctype: CType::void_ptr(),
definition: Some(Box::new(CStatement::LiteralArray {
items: dynamic_function_trampoline_statements,
})),
});
}
generate_c(&c_statements)
}
/// Get the default extension when serializing this artifact
pub fn get_default_extension(triple: &Triple) -> &'static str {
match triple.operating_system {
@@ -640,4 +472,8 @@ impl Artifact for ObjectFileArtifact {
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
Ok(self.module_bytes.clone())
}
fn symbol_registry(&self) -> &dyn SymbolRegistry {
&self.metadata
}
}

View File

@@ -1,234 +0,0 @@
#[derive(Debug, Clone)]
pub enum CType {
Void,
PointerTo {
is_const: bool,
inner: Box<CType>,
},
U8,
U16,
U32,
U64,
USize,
I8,
I16,
I32,
I64,
ISize,
Function {
arguments: Vec<CType>,
return_value: Option<Box<CType>>,
},
}
impl CType {
pub fn void_ptr() -> Self {
CType::PointerTo {
is_const: false,
inner: Box::new(CType::Void),
}
}
#[allow(dead_code)]
pub fn const_void_ptr() -> Self {
CType::PointerTo {
is_const: false,
inner: Box::new(CType::Void),
}
}
fn generate_c(&self, w: &mut String) {
match &self {
Self::Void => {
w.push_str("void");
}
Self::PointerTo { is_const, inner } => {
if *is_const {
w.push_str("const ");
}
inner.generate_c(w);
w.push_str("*");
}
Self::U8 => {
w.push_str("unsigned char");
}
Self::U16 => {
w.push_str("unsigned short");
}
Self::U32 => {
w.push_str("unsigned int");
}
Self::U64 => {
w.push_str("unsigned long long");
}
Self::USize => {
w.push_str("unsigned size_t");
}
Self::I8 => {
w.push_str("char");
}
Self::I16 => {
w.push_str("short");
}
Self::I32 => {
w.push_str("int");
}
Self::I64 => {
w.push_str("long long");
}
Self::ISize => {
w.push_str("size_t");
}
Self::Function {
arguments,
return_value,
} => {
// function with no, name, assume it's a function pointer
let ret: CType = return_value
.as_ref()
.map(|i: &Box<CType>| (&**i).clone())
.unwrap_or_default();
ret.generate_c(w);
w.push(' ');
w.push_str("(*)");
w.push('(');
if arguments.len() > 1 {
for arg in &arguments[..arguments.len() - 1] {
arg.generate_c(w);
w.push_str(", ");
}
arguments.last().unwrap().generate_c(w);
} else if arguments.len() == 1 {
arguments[0].generate_c(w);
}
w.push(')');
}
}
}
fn generate_c_with_name(&self, name: &String, w: &mut String) {
match &self {
Self::PointerTo { .. }
| Self::Void
| Self::U8
| Self::U16
| Self::U32
| Self::U64
| Self::USize
| Self::I8
| Self::I16
| Self::I32
| Self::I64
| Self::ISize => {
self.generate_c(w);
w.push(' ');
w.push_str(name);
}
Self::Function {
arguments,
return_value,
} => {
let ret: CType = return_value
.as_ref()
.map(|i: &Box<CType>| (&**i).clone())
.unwrap_or_default();
ret.generate_c(w);
w.push(' ');
w.push_str(&name);
w.push('(');
if arguments.len() > 1 {
for arg in &arguments[..arguments.len() - 1] {
arg.generate_c(w);
w.push_str(", ");
}
arguments.last().unwrap().generate_c(w);
} else if arguments.len() == 1 {
arguments[0].generate_c(w);
}
w.push(')');
}
}
}
}
impl Default for CType {
fn default() -> CType {
CType::Void
}
}
#[derive(Debug, Clone)]
pub enum CStatement {
Declaration {
name: String,
array: bool,
is_extern: bool,
is_const: bool,
ctype: CType,
definition: Option<Box<CStatement>>,
},
LiteralArray {
items: Vec<CStatement>,
},
LiteralConstant {
value: String,
},
}
impl CStatement {
fn generate_c(&self, w: &mut String) {
match &self {
Self::Declaration {
name,
array,
is_extern,
is_const,
ctype,
definition,
} => {
if *is_const {
w.push_str("const ");
}
if *is_extern {
w.push_str("extern ");
}
ctype.generate_c_with_name(name, w);
// TODO: array should be part of the type
if *array {
w.push_str("[]");
}
if let Some(def) = definition {
w.push_str(" = ");
def.generate_c(w);
}
w.push(';');
w.push('\n');
}
Self::LiteralArray { items } => {
w.push('{');
if !items.is_empty() {
w.push('\n');
}
for item in items {
w.push('\t');
item.generate_c(w);
w.push(',');
w.push('\n');
}
w.push('}');
}
Self::LiteralConstant { value } => {
w.push_str(&value);
}
}
}
}
// TODO: add config section
pub fn generate_c(statements: &[CStatement]) -> String {
let mut out = String::new();
for statement in statements {
statement.generate_c(&mut out);
}
out
}

View File

@@ -24,7 +24,6 @@
mod artifact;
mod builder;
mod engine;
pub(crate) mod header;
mod serialize;
pub use crate::artifact::ObjectFileArtifact;

View File

@@ -5,7 +5,7 @@ use std::any::Any;
use std::fs;
use std::path::Path;
use std::sync::Arc;
use wasmer_compiler::Features;
use wasmer_compiler::{Features, SymbolRegistry};
use wasmer_types::entity::{BoxedSlice, PrimaryMap};
use wasmer_types::{
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer,
@@ -21,7 +21,7 @@ use wasmer_vm::{
/// The `Artifact` contains the compiled data for a given
/// module as well as extra information needed to run the
/// module at runtime, such as [`ModuleInfo`] and [`Features`].
pub trait Artifact: Send + Sync + 'static + Upcastable {
pub trait Artifact: Send + Sync {
/// Return a reference-counted pointer to the module
fn module(&self) -> Arc<ModuleInfo>;
@@ -148,41 +148,7 @@ pub trait Artifact: Send + Sync + 'static + Upcastable {
.finish_instantiation(is_bulk_memory, &data_initializers)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
}
// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 .
/// Trait needed to get downcasting from `Artifact` to work.
pub trait Upcastable {
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any;
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any;
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + 'static> Upcastable for T {
#[inline]
fn upcast_any_ref(self: &'_ Self) -> &'_ dyn Any {
self
}
#[inline]
fn upcast_any_mut(self: &'_ mut Self) -> &'_ mut dyn Any {
self
}
#[inline]
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl dyn Artifact + 'static {
/// Downcast a reference to an `Artifact` into a specific implementor.
#[inline]
pub fn downcast_ref<T: 'static>(self: &'_ Self) -> Option<&'_ T> {
self.upcast_any_ref().downcast_ref::<T>()
}
/// Downcast a mutable reference to an `Artifact` into a specific implementor.
#[inline]
pub fn downcast_mut<T: 'static>(self: &'_ mut Self) -> Option<&'_ mut T> {
self.upcast_any_mut().downcast_mut::<T>()
}
/// Get the `SymbolRegistry` used to generate the names used in the Artifact.
fn symbol_registry(&self) -> &dyn SymbolRegistry;
}