Project Icon

chicory

JVM原生WebAssembly运行时 无依赖执行Wasm

Chicory是JVM原生WebAssembly运行时,无需原生依赖或JNI即可在JVM环境中执行Wasm程序。它支持加载实例化模块、调用导出函数、处理复杂类型和内存操作,以及定义主机函数。Chicory为Java应用提供纯JVM的Wasm执行环境,简化了部署流程,避免了分发和运行原生代码的复杂性。该项目注重简单性和安全性,适用于各种JVM场景。

Chicory运行时

解释器测试结果 AOT测试结果 WASI测试结果

Zulip

Chicory是一个JVM原生的WebAssembly运行时。它允许你在没有任何本地依赖或JNI的情况下运行WebAssembly程序。Chicory可以在JVM能够运行的任何地方运行Wasm。它在设计时考虑了简单性和安全性。查看开发部分以更好地了解我们想要实现的目标及其原因。

联系我们:Chicory还处于早期开发阶段,可能会有一些粗糙之处。在我们正式向世界宣布测试版之前,我们希望能与一些早期采用者和贡献者交流。如果你有兴趣提供反馈或贡献,或者只是想跟进开发进度,请使用此邀请链接加入我们的Zulip团队聊天

入门指南(作为用户)

安装依赖

要使用运行时,你需要将com.dylibso.chicory:runtime依赖添加到你的依赖管理系统中。

Maven

<dependency>
  <groupId>com.dylibso.chicory</groupId>
  <artifactId>runtime</artifactId>
  <version>0.0.12</version>
</dependency>

Gradle

implementation 'com.dylibso.chicory:runtime:0.0.12'

安装CLI(实验性)

Chicory CLI可在Maven上通过以下链接下载:

https://repo1.maven.org/maven2/com/dylibso/chicory/cli/<version>/cli-<version>.sh

你可以用几行命令下载最新版本并在本地使用:

export VERSION=$(wget -q -O - https://api.github.com/repos/dylibso/chicory/tags --header "Accept: application/json" | jq -r '.[0].name')
wget -O chicory https://repo1.maven.org/maven2/com/dylibso/chicory/cli/${VERSION}/cli-${VERSION}.sh
chmod a+x chicory
./chicory

加载和实例化代码

首先,你的Wasm模块必须从磁盘加载,然后进行"实例化"。让我们下载一个测试模块。 这个模块包含了计算阶乘的代码:

从链接下载或使用curl:

curl https://raw.githubusercontent.com/dylibso/chicory/main/wasm-corpus/src/main/resources/compiled/iterfact.wat.wasm > factorial.wasm

现在让我们加载这个模块并实例化它:

import com.dylibso.chicory.runtime.ExportFunction;
import com.dylibso.chicory.wasm.types.Value;
import com.dylibso.chicory.wasm.Module;
import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.runtime.Instance;
import java.io.File;

// 将此处指向你磁盘上的路径
Module module = Parser.parse(new File("./factorial.wasm"));
Instance instance = Instance.builder(module).build();

你可以把module看作是静态的代码,而instance则是加载了代码并准备执行的虚拟机。

调用导出函数

Wasm模块,像所有代码模块一样,可以向外部世界导出函数。这个模块导出了一个名为"iterFact"的函数。我们可以使用Instance#export(String)来获取这个函数的句柄:

ExportFunction iterFact = instance.export("iterFact");

可以使用apply()方法调用iterFact。我们必须将任何Java类型映射到wasm类型,反之亦然。这个导出函数接受一个i32参数。我们可以在返回值上使用Value#asInt()这样的方法来获取Java整数:

Value result = iterFact.apply(Value.i32(5))[0];
System.out.println("Result: " + result.asInt()); // 应该打印120(5的阶乘)

注意:Wasm中的函数可以有多个返回值,但这里我们只取第一个返回值。

内存和复杂类型

Wasm只理解基本的整数和浮点数原始类型。因此,跨边界传递更复杂的类型涉及传递指针。为了读取、写入或分配模块中的内存,Chicory为你提供了Memory类。让我们看一个例子,其中有一个用Rust编写的count_vowels.wasm模块,它接受一个字符串输入并计算字符串中的元音数量:

curl https://raw.githubusercontent.com/dylibso/chicory/main/wasm-corpus/src/main/resources/compiled/count_vowels.rs.wasm > count_vowels.wasm

构建并实例化这个模块:

Instance instance = Instance.builder(Parser.parse(new File("./count_vowels.wasm"))).build();
ExportFunction countVowels = instance.export("count_vowels");

要传递一个字符串,我们首先需要将字符串放入模块的内存中。为了使这更容易和安全,该模块给我们提供了一些额外的导出函数,允许我们分配和释放内存:

ExportFunction alloc = instance.export("alloc");
ExportFunction dealloc = instance.export("dealloc");

让我们为字符串分配Wasm内存并将其放入实例的内存中。我们可以使用Memory#put来做到这一点:

import com.dylibso.chicory.runtime.Memory;
Memory memory = instance.memory();
String message = "Hello, World!";
int len = message.getBytes().length;
// 分配{len}字节的内存,这会返回一个指向该内存的指针
int ptr = alloc.apply(Value.i32(len))[0].asInt();
// 现在我们可以将消息写入模块的内存:
memory.writeString(ptr, message);

现在我们可以用这个指向字符串的指针调用countVowels。它会完成它的工作并返回计数。我们将调用dealloc来释放模块中的那块内存。当然,如果你愿意,模块也可以自己做这件事:

Value result = countVowels.apply(Value.i32(ptr), Value.i32(len))[0];
dealloc.apply(Value.i32(ptr), Value.i32(len));
assert(3 == result.asInt()); // Hello, World! 中有3个元音

宿主函数

单独的Wasm只能进行计算,不能影响外部世界。这可能看起来像是一个弱点,但实际上这是Wasm最大的优势。默认情况下,程序是沙盒化的,没有任何能力。如果你想让程序有能力,你必须提供它们。这使你处于操作系统的位置。模块可以通过在其字节码格式中列出"导入"函数来请求能力。你可以用用Java编写的宿主函数来满足这个导入。无论模块的语言是什么,当它需要时都可以调用这个Java函数。如果有帮助的话,你可以把宿主函数想象成系统调用或语言的标准库,但你决定它们是什么以及它们如何行为,而且它是用Java编写的。

让我们下载另一个示例模块来演示这一点:

curl https://raw.githubusercontent.com/dylibso/chicory/main/wasm-corpus/src/main/resources/compiled/host-function.wat.wasm > logger.wasm

这个模块期望我们提供一个名为console.log的导入,这将允许模块输出到stdout。让我们编写该宿主函数:

import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.wasm.types.ValueType;
var func = new HostFunction(
    (Instance instance, Value... args) -> { // 反编译为:console_log(13, 0);
        var len = args[0].asInt();
        var offset = args[1].asInt();
        var message = instance.memory().readString(offset, len);
        println(message);
        return null;
    },
    "console",
    "log",
    List.of(ValueType.I32, ValueType.I32),
    List.of());

这里我们再次处理指针。模块调用console.log时传入字符串的长度和它在内存中的指针(偏移量)。我们再次使用Memory类,但这次我们是从内存中提取字符串。然后我们可以代表我们的Wasm程序将其打印到stdout。

注意,HostFunction需要3个东西:

  1. 当Wasm模块调用导入时要调用的lambda
  2. 导入的命名空间和函数名(在我们的例子中分别是console和log)
  3. Wasm类型签名(这个函数接受2个i32作为参数,不返回任何内容) 现在我们只需在实例化阶段传入这个主机函数:
import com.dylibso.chicory.runtime.HostImports;
var imports = new HostImports(new HostFunction[] {func});
var instance = Instance.builder(Parser.parse(new File("./logger.wasm"))).withHostImports(imports).build();
var logIt = instance.export("logIt");
logIt.apply();
// 应该打印 "Hello, World!" 10 次

开发

为什么需要这个?

如果你更倾向于观看视频而不是阅读,请查看我们在2024 Wasm I/O上的相关演讲:

Wasm I/O Chicory 演讲

有很多成熟的 Wasm 运行时可供选择来执行 Wasm 模块。 例如 v8wasmtimewasmerwasmedge 等。

尽管这些可能是运行 Wasm 应用程序的很好选择,但将它们嵌入到现有的 Java 应用程序中有一些缺点。因为这些运行时是用 C/C++/Rust 等语言编写的,它们必须作为 本机代码分发和运行。这会导致两个主要的摩擦点:

1. 分发

如果你正在分发 Java 库(jar、war 等),你现在必须随之分发针对正确 架构和操作系统的本机对象。这个矩阵可能会变得相当大。这消除了发布 Java 代码的很多简单性和原始好处。

2. 运行时

在运行时,你必须使用 FFI 来执行模块。虽然对某些模块来说这样做可能会带来性能上的好处, 但当你这样做时,你实际上是在逃离 JVM 的安全性和可观察性。拥有纯 JVM 运行时意味着所有的 安全性和内存保证,以及你的工具,都可以保持不变。

目标

  • 尽可能安全
    • 我们愿意为安全性和简单性牺牲性能等方面
  • 使在任何 JVM 环境中运行 Wasm 变得容易,无需本机代码,包括非常严格的环境。
  • 完全支持核心 Wasm 规范
  • 使与 Java(和其他主机语言)的集成变得简单和符合习惯。

非目标:

  • 成为独立的运行时
  • 成为最快的运行时
  • 成为每个 JVM 项目的正确选择

路线图

Chicory 的开发始于 2023 年 9 月。以下是我们的目标里程碑。这些 可能会发生变化,但代表了我们根据当前信息做出的最佳猜测。这些并不一定是按顺序进行的, 有些可能会同时进行。除非特别说明,任何未勾选的框都尚未计划或开始。 如果你对其中任何一项感兴趣,请在 Zulip 上联系我们!

引导字节码解释器和测试套件 (2023 年底)

  • Wasm 二进制解析器 链接
  • 简单的字节码解释器
  • 建立基本的编码和测试模式
  • 从 wasm 测试套件生成 JUnit 测试 链接

使解释器可用于生产环境 (2024 年夏)

  • 使所有测试在解释器上通过(对正确性很重要)
  • 实现验证逻辑(对安全性很重要)
    • 接近完成
  • v1.0 API 草案(对稳定性和开发体验很重要)

提高性能 (2024 年底)

这里的主要目标是创建一个生成 JVM 字节码的 AOT 编译器, 因为解释字节码的速度是有限的。

  • 解耦解释器,创建独立的编译器和解释器"引擎"
  • AOT 编译器概念验证(运行一些模块子集)
  • AOT 引擎通过与解释器相同的所有规范(延伸目标)
    • 进行中
  • 堆外线性内存(延伸目标)

提高兼容性 (2024 年底)

  • WASIp1 支持(包括测试生成)
  • SIMD 支持
    • 已开始
  • 多内存支持
  • GC 支持
  • 线程支持
  • 组件模型支持

先前的工作

构建运行时

贡献者和其他高级用户可能希望从源代码构建运行时。要做到这一点,你需要安装 Maven。 需要 Java 11+版本 才能正确构建。你可以下载并安装 Java 11 Temurin

基本步骤:

  • mvn clean install 运行项目的所有测试并在本地仓库中安装库
  • mvn -Dquickly 安装库,跳过所有测试
  • mvn spotless:apply 自动格式化代码
  • ./scripts/compile-resources.sh 将重新编译并重新生成 resources/compiled 文件夹

注意: install 目标依赖 wabt 库来编译测试套件。目前 ARM 平台(例如使用 Apple Silicon 的新 Mac)尚未发布该库。然而,可以通过 Homebrew 获得 wabt,所以在运行 mvn clean install 之前执行 brew install wabt 应该可以解决问题。

日志记录

为了最大程度的兼容性并避免外部依赖,我们默认使用JDK平台日志记录(JEP 264)。 您可以通过提供一个使用java.util.logging.config.file属性的logging.properties文件来配置它,您可以在这里找到可能的配置选项。

对于更高级的配置场景,我们建议您提供一个替代的、兼容的适配器:

如果JDK平台日志记录不可用或不适合,还可以提供自定义的com.dylibso.chicory.log.Logger实现。

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号