chicory

chicory

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

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

WebAssemblyJVMChicoryJava运行时Github开源项目

Chicory运行时

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

Zulip

<img align="right" width="200" src="https://yellow-cdn.veclightyear.com/835a84d5/374f34cb-9953-47a3-9935-359d84b16ddf.png">

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 次
<!-- ```java writeResultFile("hostFunction.result", hostFunctionResult); ``` -->

开发

为什么需要这个?

如果你更倾向于观看视频而不是阅读,请查看我们在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实现。

编辑推荐精选

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

下拉加载更多