jint

jint

跨平台高性能 JavaScript 解释器

Jint 是一款专为 .NET 平台开发的 JavaScript 解释器,兼容 .NET Standard 2.0 和 .NET 4.6.2 及更高版本。它提供安全的沙盒环境执行 JavaScript 代码,支持访问原生 .NET 对象和函数,实现脚本化功能。Jint 广泛支持 ECMAScript 标准,覆盖 ES6 到 ES2024 的众多特性,并提供执行限制和 .NET 互操作能力。

JintJavaScript解释器.NET跨平台脚本引擎Github开源项目

构建 NuGet NuGet MyGet 加入Gitter聊天

Jint

Jint是一个适用于.NET的JavaScript解释器,支持.NET Standard 2.0和.NET 4.6.2(及更高版本)目标,可以在任何现代.NET平台上运行。

使用场景和用户

  • 在您的.NET应用程序中安全沙箱环境中运行JavaScript
  • 将原生.NET对象和函数暴露给您的JavaScript代码(获取数据库查询结果为JSON,调用.NET方法等)
  • 支持在.NET应用程序中进行脚本编写,允许用户使用JavaScript自定义您的应用程序(如Unity游戏)

Jint的一些用户包括 RavenDBEventStoreOrchardCoreELSA WorkflowsdocfxJavaScript Engine Switcher 等等。

支持的功能

ECMAScript 2015 (ES6)

  • ✔ ArrayBuffer
  • ✔ 箭头函数表达式
  • ✔ 二进制和八进制字面量
  • ✔ 类支持
  • ✔ DataView
  • ✔ 解构
  • ✔ 默认参数、rest参数和展开运算符
  • ✔ 增强的对象字面量
  • for...of
  • ❌ 生成器
  • ✔ 模板字符串
  • ✔ 变量的词法作用域(let和const)
  • ✔ Map和Set
  • ✔ 模块和模块加载器
  • ✔ Promise(实验性,API不稳定)
  • ✔ Reflect
  • ✔ 代理
  • ✔ Symbol
  • ❌ 尾调用
  • ✔ 类型化数组
  • ✔ Unicode
  • ✔ Weakmap和Weakset

ECMAScript 2016

  • Array.prototype.includes
  • awaitasync
  • ✔ 变量和函数的块级作用域
  • ✔ 指数运算符 **
  • ✔ 解构模式(变量)

ECMAScript 2017

  • Object.valuesObject.entriesObject.getOwnPropertyDescriptors
  • ❌ 共享内存和原子操作

ECMAScript 2018

  • Promise.prototype.finally
  • ✔ 正则表达式命名捕获组
  • ✔ 对象字面量的rest/spread运算符(...identifier
  • ✔ SharedArrayBuffer

ECMAScript 2019

  • Array.prototype.flatArray.prototype.flatMap
  • String.prototype.trimStartString.prototype.trimEnd
  • Object.fromEntries
  • Symbol.description
  • ✔ 可选的catch绑定

ECMAScript 2020

  • BigInt
  • export * as ns from
  • for-in增强
  • globalThis对象
  • import
  • import.meta
  • ✔ 空值合并运算符(??
  • ✔ 可选链
  • Promise.allSettled
  • String.prototype.matchAll

ECMAScript 2021

  • ✔ 逻辑赋值运算符(&&= ||= ??=
  • ✔ 数字分隔符(1_000
  • AggregateError
  • Promise.any
  • String.prototype.replaceAll
  • WeakRef
  • FinalizationRegistry

ECMAScript 2022

  • ✔ 类字段
  • ✔ 正则表达式匹配索引
  • ✔ 顶层await
  • ✔ 私有字段的人体工程学品牌检查
  • .at()
  • ✔ 可访问的Object.prototype.hasOwnPropertyObject.hasOwn
  • ✔ 类静态块
  • ✔ 错误原因

ECMAScript 2023

  • ✔ 数组从末尾查找
  • ✔ 通过复制更改数组
  • ✔ Hashbang语法
  • ✔ Symbol作为WeakMap键

ECMAScript 2024

  • ✔ ArrayBuffer增强 - ArrayBuffer.prototype.resizeArrayBuffer.prototype.transfer
  • Atomics.waitAsync
  • ✔ 确保字符串格式正确 - String.prototype.ensureWellFormedString.prototype.isWellFormed
  • ✔ 同步可迭代对象分组 - Object.groupByMap.groupBy
  • Promise.withResolvers
  • ❌ 正则表达式标志 /v

ECMAScript Stage 3(尚未确定版本)

  • ✔ Float16Array(需要NET 6或更高版本)
  • ✔ Import属性
  • ✔ JSON模块
  • Promise.try
  • ✔ Set方法(intersectionuniondifferencesymmetricDifferenceisSubsetOfisSupersetOfisDisjointFrom
  • ✔ ShadowRealm
  • ✔ Uint8Array与base64互转

其他

  • 进一步完善的.NET CLR互操作能力
  • 执行约束(递归、内存使用、持续时间)

性能

  • 由于Jint既不生成任何.NET字节码也不使用DLR,它可以非常快速地运行相对较小的脚本
  • 如果您重复运行相同的脚本,应该缓存由Esprima生成的ScriptModule实例,而不是内容字符串
  • 您应该优先在严格模式下运行引擎,这可以提高性能

您可以查看引擎比较结果,请记住每个用例都不同,基准测试可能无法反映您的实际使用情况。

讨论

加入Gitter聊天,或在stackoverflow上使用jint标签提问。

视频

这里有一个简短的视频,展示了Jint的工作原理和一些示例用法

https://docs.microsoft.com/shows/code-conversations/sebastien-ros-on-jint-javascript-interpreter-net

线程安全

引擎实例不是线程安全的,不应同时从多个线程访问它们。

示例

这个例子定义了一个名为log的新值,指向Console.WriteLine,然后运行一个调用log('Hello World!')的脚本。

var engine = new Engine() .SetValue("log", new Action<object>(Console.WriteLine)); engine.Execute(@" function hello() { log('Hello World'); }; hello(); ");

在这里,变量x被设置为3,并在JavaScript中计算x * x。结果直接返回给.NET,在这种情况下作为double9

var square = new Engine() .SetValue("x", 3) // 定义一个新变量 .Evaluate("x * x") // 计算一个语句 .ToObject(); // 将值转换为.NET对象

您还可以直接传递POCO或匿名对象,并从JavaScript中使用它们。例如,在这个例子中,一个新的Person实例从JavaScript中被操作。

var p = new Person { Name = "Mickey Mouse" }; var engine = new Engine() .SetValue("p", p) .Execute("p.Name = 'Minnie'"); Assert.AreEqual("Minnie", p.Name);

您可以调用JavaScript函数引用

var result = new Engine() .Execute("function add(a, b) { return a + b; }") .Invoke("add",1, 2); // -> 3

或直接通过名称调用

var engine = new Engine() .Execute("function add(a, b) { return a + b; }"); engine.Invoke("add", 1, 2); // -> 3

访问.NET程序集和类

您可以通过配置引擎实例来允许引擎访问任何.NET类,如下所示:

var engine = new Engine(cfg => cfg.AllowClr());

然后,您可以将System命名空间作为全局值访问。以下是在命令行实用程序上下文中使用它的方式:

jint> var file = new System.IO.StreamWriter('log.txt'); jint> file.WriteLine('Hello World !'); jint> file.Dispose();

甚至可以为常用的.NET方法创建快捷方式

jint> var log = System.Console.WriteLine; jint> log('Hello World !'); => "Hello World !"

当允许CLR时,您可以选择性地传递自定义程序集以加载类型。

var engine = new Engine(cfg => cfg .AllowClr(typeof(Bar).Assembly) );

然后,要以与System相同的方式分配本地命名空间,请使用importNamespace

jint> var Foo = importNamespace('Foo'); jint> var bar = new Foo.Bar(); jint> log(bar.ToString());

添加特定CLR类型引用可以这样做

engine.SetValue("TheType", TypeReference.CreateTypeReference<TheType>(engine));

并以这种方式使用

jint> var o = new TheType();

也支持泛型类型。以下是如何声明、实例化和使用List<string>

jint> var ListOfString = System.Collections.Generic.List(System.String); jint> var list = new ListOfString(); jint> list.Add('foo'); jint> list.Add(1); // 自动转换为String jint> list.Count; // 2

国际化

如果您不想使用计算机的默认值,可以强制引擎在使用本地JavaScript方法时使用特定的时区或文化。

这个例子强制时区为太平洋标准时间。

var PST = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); var engine = new Engine(cfg => cfg.LocalTimeZone(PST)); engine.Execute("new Date().toString()"); // Wed Dec 31 1969 16:00:00 GMT-08:00

本例使用法语作为默认区域性。

var FR = CultureInfo.GetCultureInfo("fr-FR"); var engine = new Engine(cfg => cfg.Culture(FR)); engine.Execute("new Number(1.23).toString()"); // 1.23 engine.Execute("new Number(1.23).toLocaleString()"); // 1,23

执行约束

执行约束用于在脚本执行期间确保满足资源消耗方面的要求,例如:

  • 脚本不应使用超过 X 内存。
  • 脚本应该只运行最长时间。

你可以通过以下选项配置它们:

var engine = new Engine(options => { // 将内存分配限制为 4 MB options.LimitMemory(4_000_000); // 设置 4 秒的超时。 options.TimeoutInterval(TimeSpan.FromSeconds(4)); // 设置执行语句的限制为 1000 条。 options.MaxStatements(1000); // 使用取消令牌。 options.CancellationToken(cancellationToken); }

你也可以通过派生 Constraint 基类来编写自定义约束:

public abstract class Constraint { /// 在脚本运行之前调用,当你使用一个引擎对象进行多次执行时非常有用。 public abstract void Reset(); // 在每个语句之前调用,以检查是否满足你的要求;如果不满足 - 抛出异常。 public abstract void Check(); }

例如,我们可以编写一个约束,当 CPU 使用率过高时停止脚本:

class MyCPUConstraint : Constraint { public override void Reset() { } public override void Check() { var cpuUsage = GetCPUUsage(); if (cpuUsage > 0.8) // 80% { throw new OperationCancelledException(); } } } var engine = new Engine(options => { options.Constraint(new MyCPUConstraint()); });

当你重复使用引擎并想要使用取消令牌时,你必须在每次调用 Execute 之前重置令牌:

var engine = new Engine(options => { options.CancellationToken(new CancellationToken(true)); }); var constraint = engine.Constraints.Find<CancellationConstraint>(); for (var i = 0; i < 10; i++) { using (var tcs = new CancellationTokenSource(TimeSpan.FromSeconds(10))) { constraint.Reset(tcs.Token); engine.SetValue("a", 1); engine.Execute("a++"); } }

使用模块

你可以使用模块从多个脚本文件中 importexport 变量:

var engine = new Engine(options => { options.EnableModules(@"C:\Scripts"); }) var ns = engine.Modules.Import("./my-module.js"); var value = ns.Get("value").AsString();

默认情况下,模块解析算法将被限制在 EnableModules 中指定的基本路径内,并且没有包支持。但是你可以通过两种方式提供自己的包。

使用 JavaScript 源代码定义模块:

engine.Modules.Add("user", "export const name = 'John';"); var ns = engine.Modules.Import("user"); var name = ns.Get("name").AsString();

使用模块构建器定义模块,这允许你从 .NET 导出 CLR 类和值:

// 创建包含 MyClass 类和 version 变量的 'lib' 模块 engine.Modules.Add("lib", builder => builder .ExportType<MyClass>() .ExportValue("version", 15) ); // 创建一个用户定义的模块并使用 'lib' engine.Modules.Add("custom", @" import { MyClass, version } from 'lib'; const x = new MyClass(); export const result as x.doSomething(); "); // 导入用户定义的模块;这将执行导入链 var ns = engine.Modules.Import("custom"); // 结果包含对模块的"实时"绑定 var id = ns.Get("result").AsInteger();

注意,如果你只使用通过 Engine.Modules.Add 创建的模块,则不需要 EnableModules

.NET 互操作性

  • 从 JavaScript 操作 CLR 对象,包括:
    • 单个值
    • 对象
      • 属性
      • 方法
    • 委托
    • 匿名对象
  • 将 JavaScript 值转换为 CLR 对象
    • 原始值
    • Object -> expando 对象(IDictionary<string, object> 和 dynamic)
    • Array -> object[]
    • Date -> DateTime
    • number -> double
    • string -> string
    • boolean -> bool
    • Regex -> RegExp
    • Function -> Delegate
  • 扩展方法

安全性

以下功能为你提供了一个安全的沙箱环境来运行用户脚本。

  • 定义内存限制,以防止分配耗尽内存。
  • 启用/禁用 BCL 的使用,以防止脚本调用 .NET 代码。
  • 限制语句数量,以防止无限循环。
  • 限制调用深度,以防止深度递归调用。
  • 定义超时,以防止脚本运行时间过长。

分支和发布

编辑推荐精选

讯飞智文

讯飞智文

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

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

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

讯飞星火

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

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

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

Spark-TTS

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

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

Trae

Trae

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

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

AI工具TraeAI IDE协作生产力转型热门
咔片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 的技术优势。

Hunyuan3D-2

Hunyuan3D-2

高分辨率纹理 3D 资产生成

Hunyuan3D-2 是腾讯开发的用于 3D 资产生成的强大工具,支持从文本描述、单张图片或多视角图片生成 3D 模型,具备快速形状生成能力,可生成带纹理的高质量 3D 模型,适用于多个领域,为 3D 创作提供了高效解决方案。

3FS

3FS

一个具备存储、管理和客户端操作等多种功能的分布式文件系统相关项目。

3FS 是一个功能强大的分布式文件系统项目,涵盖了存储引擎、元数据管理、客户端工具等多个模块。它支持多种文件操作,如创建文件和目录、设置布局等,同时具备高效的事件循环、节点选择和协程池管理等特性。适用于需要大规模数据存储和管理的场景,能够提高系统的性能和可靠性,是分布式存储领域的优质解决方案。

下拉加载更多