#mono #dotnet #framework #c-sharp #scripting-language #safe-wrapper #f-sharp

sys wrapped_mono

wrapped_mono 是围绕 mono 库的一个安全、轻量级的包装。它允许在 Rust 项目中嵌入 mono 运行时。在这个嵌入的运行时中,可以运行支持 .NET 框架的语言(如 C# 和 F#)编写的代码。这允许使用这些语言编写的库,并将它们用作脚本语言。mono 运行时被许多游戏引擎使用,这个包装器允许使用 Rust 编写的项目使用它。

3 个不稳定版本

0.3.1 2023年4月22日
0.3.0 2023年4月22日
0.2.0 2022年10月17日

#184 in 内存管理

MIT 许可证

700KB
18K SLoC

wrapped_mono

wrapped_mono 是围绕 mono 库的一个安全、轻量级的包装。它允许在 Rust 项目中嵌入 mono 运行时。在这个嵌入的运行时中,可以运行支持 .NET 框架的语言(如 C# 和 F#)编写的代码。这允许使用这些语言编写的库,并将它们用作脚本语言。mono 运行时被许多游戏引擎使用,这个包装器允许使用 Rust 编写的项目使用它。

工作进行中

缺少 API

尽管 wrapped_mono 已经支持了 mono 运行时的大部分功能,但一些小的 API 尚未完成和完全测试。这些未完成的 API 通常很专业(例如,高级调试、访问性能分析器(性能数据)、访问汇编元数据、动态代码生成),并且始终存在可用的替代不安全绑定。

安全性检查

此 API 尽可能遵循 Rust 的安全和错误处理规则,但一些检查尚未完成,无法捕获所有潜在的问题,或者由于它们会引入严重的性能损失,只覆盖了文档中明确标记的利基案例。这类案例的一个很好的例子是在删除该对象所在的域或关闭运行时后访问该对象。大多数可能发生的错误都进行了检查,并且可以通过禁用这些检查来加快 wrapped_mono 的速度,但这不建议这样做。这些检查的成本通常可以忽略不计(小于调用函数成本的 1%),并且它们可以防止许多潜在的错误。

支持的平台

wrapped_mono 支持 Linux(在 Fedora 37、Debian Bullseye 和 Arch 上进行了测试),以及 Windows(在 Windows 10 上进行了测试)。其他平台,如 MacOS,未官方支持,但可以通过更改 build.rs 以包括特定平台的链接标志轻松添加。

依赖项

外部

  • Mono 库 - 此 crate 包装的库。可以在此处下载:此处。安装时,请使用网站上的默认说明。仅在系统 crate 编译的计算机上需要(链接为静态)。

Rust

  • wrapped_mono_macros - 包含 wrapped_mono 所使用的自定义宏的子 crate。它是独立的,因为 proc_macro 必须是独立的 crate。
  • document-features - 用于文档
  • lazy_static - 在使用 crate 时用于懒加载类,如 System.Delegate

功能

完全完成

  • 初始化 mono 运行时
  • 创建不同的应用程序域,以防止不同程序集的代码交互。
  • 从程序集中加载程序集并获取类
  • 创建类的新实例
  • 获取类的静态、非静态、虚拟方法 - 带签名检查
  • 调用静态、虚拟、特定对象上的方法
  • 读取和设置类的字段 - 静态、非静态
  • 使用类的 getter、setter 和索引器属性。
  • 装箱、拆箱、克隆、获取哈希、大小、类型转换、将对象转换为字符串。
  • 抛出和捕获异常
  • 创建 n 维数组,读取和设置任意索引处的元素。
  • 在托管代码和非托管代码之间传递基本类型(整数、字符、浮点数、指针、数组、字符串、异常、对象、类型、委托)
  • 调用委托
  • 实现简单的特质,以在 Rust 和 C#/F# 代码之间传递任何类型!
  • 自动为从实现辅助特质的其他类型创建的结构实现 interop 辅助特质。
  • 在 Rust 和 C# 代码之间传递简单的 Rust 枚举。
  • 将 C# 方法作为对 Rust 代码的内部调用实现。
  • 自动从 Rust 代码注册所有对托管对象的引用。Rust 代码中每个对象的引用都会注册,并且当可以和不可以运行时,垃圾收集器会自动通知。
  • 加载自定义配置文件。

进行中功能

  • 性能分析器 - 获取有关您的 C#/F# 程序集运行的见解 - 哪些对象被创建,每个方法被调用的频率(已支持几个不同的事件,但存在一些错误)
  • 程序集元数据访问 - 已工作,但未完成。

计划中的功能

  • C#/F# 程序集绑定生成器
  • 动态代码生成
  • 安全 API
  • Rust 的 System.Thread 表示
  • 调试功能

示例

use wrapped_mono::*;
fn main(){
    // Initialise the runtime with default version(`None`), and root domian named "main_domain"
    let domain = jit::init("main_domain",None);

    // Load assembly "SomeAssembly.dll"
    let assembly = domain.assembly_open("SomeAssembly.dll").expect("Could not load assembly!");
    // Get the image, the part of assembly containing executable code(classes,methods, etc.)
    let image = assembly.get_image();
    // Get class named SomeClass in SomeNamespace
    let class = Class::from_name(&image,"SomeNamespace","SomeClass").expect("Could not find SomeClass!");
    // Create an instance of this class
    let instance = Object::new(&domain,&class);
    // Creating an instance of a class DOES NOT CALL ITS CONSTRUCTOR. The constructor is a method named '.ctor', that has to be called separately

    // Get a constructor method of SomeClass accepting an integer and a string (2 parameters)
    let ctor:Method<(i32,String)> = Method::get_from_name(&class,".ctor(int,System.String)",2).expect("Could not find the constructor!");
    // Call the constructor
    ctor.invoke(Some(instance.clone()),(12,"SomeString".to_owned())).expect("Got an exception while calling the constructor!");
    // Get a method "DoABackflip" form SomeClass with 1 parameter of type int returning a byte
    let met:Method<(i32,String)> = Method::get_from_name(&class,"DoABackflip",1).expect("Could not find method \"DoABackFlip\"!");
    // Call "DoABackflip" method on an instance
    let res_obj = met.invoke(Some(instance),(32,"Message".to_owned())).expect("Got an exception while calling DoABackflip!").expect("Got null from DoABackFlip");
    // Unbox the result to get a raw integer from a boxed integer
    let res = res_obj.unbox::<u8>();
    // Create a function with the special "invokable" attribute
    #[invokable]
    fn sqrt(input:f32)->f32{
        if input < 0.0{
            // can't get sqrt of a negative number, so create a managed exception and throw it
            unsafe{Exception::arithmetic().raise()};
        }
        input.sqrt()
    }
    // Replace a method with "[MethodImplAttribute(MethodImplOptions.InternalCall)]" atribute with a rust function
    add_internal_call!("SomeClass::SqrtInternalCall",sqrt);
    // This supports all types with `InteropRecive` trait
    #[invokable]
    fn avg(input:Array<Dim1D,f32>)->f32{
        let mut avg = 0.0;
        for i in 0..input.len(){
            let curr = input.get([i]);// get the element at index i
            avg += curr/(input.len() as f32);
          }
        avg
    }
    // Replace a method with "[MethodImplAttribute(MethodImplOptions.InternalCall)]" attribute with a rust function
    add_internal_call!("SomeClass::AvgInternalCall",sqrt);
}

依赖项

~2MB
~41K SLoC