乐于分享
好东西不私藏

Deno 源码解析:Ryan Dahl 用 Rust 重新定义 JavaScript 运行时

Deno 源码解析:Ryan Dahl 用 Rust 重新定义 JavaScript 运行时

引言

Deno 是 Node.js 创始人 Ryan Dahl 的”第二次尝试”。他在 2018 年的演讲中列举了 Node.js 的设计遗憾,然后用 Rust 从零构建了 Deno。核心改进:默认安全(沙箱权限)、原生 TypeScript 支持、基于 URL 的模块系统。

项目背景

Deno 使用 Rust 编写核心运行时,V8 作为 JavaScript 引擎,Tokio 作为异步运行时。2024 年推出的 Deno 2.0 增加了 Node.js 兼容层,大幅降低了迁移成本。

核心源码解析

1. 权限系统

Deno 最大的创新是默认拒绝所有权限:

pub struct Permissions {
    pub read: UnaryPermission<ReadDescriptor>,
    pub write: UnaryPermission<WriteDescriptor>,
    pub net: UnaryPermission<NetDescriptor>,
    pub env: UnaryPermission<EnvDescriptor>,
    pub run: UnaryPermission<RunDescriptor>,
    pub ffi: UnaryPermission<FfiDescriptor>,
}

impl Permissions {
    pub fn check_read(&self, path: &Path) -> Result<(), AnyError> {
        self.read.check(path)
    }
}

每次文件读写、网络请求、环境变量访问都需要显式授权(--allow-read--allow-net 等)。这在运行时层面实现了最小权限原则。

2. Op 系统(运行时扩展)

Deno 通过 “Op” 机制将 Rust 函数暴露给 JavaScript:

#[op]
async fn op_read_file(state: &mut OpState, path: String) -> Result<Vec<u8>, AnyError> {
    let permissions = state.borrow::<Permissions>();
    permissions.check_read(&PathBuf::from(&path))?;
    tokio::fs::read(path).await.map_err(Into::into)
}
#[op] 宏自动生成 V8 绑定代码,开发者只需要写 Rust 函数,框架负责 JS↔Rust 的桥接。

3. 模块加载器

基于 URL 的模块系统,支持远程导入:

pub struct ModuleLoader {
    pub async fn load(specifier: &ModuleSpecifier) -> Result<ModuleSource, AnyError> {
        match specifier.scheme() {
            "file" => self.load_from_disk(specifier).await,
            "https" | "http" => self.load_from_network(specifier).await,
            _ => Err(anyhow!("Unsupported scheme")),
        }
    }
}

远程模块会被缓存到本地,后续加载直接使用缓存,兼顾了便利性和性能。

关键设计模式

1. 默认安全:权限系统在运行时层面强制执行,而非依赖开发者自觉。 2. 宏驱动的 FFI#[op] 宏大幅简化了 Rust↔JS 的互操作。 3. URL 即模块:消除了 node_modules 的复杂性。

总结

Deno 是对 Node.js 设计缺陷的系统性修正。权限沙箱、Op 系统、URL 模块加载,每个设计决策都有明确的动机。对于关注运行时安全和 Rust+V8 集成的开发者,Deno 的源码是必读材料。