这是一个轻量级的弱类型脚本执行引擎,他没有引用任何第三方组件,它通过将脚本编译为字节码然后通过虚拟机来解释运行
它目前还是个玩具,速度可能会很慢,但是它可以正常的跑起来还可以输出一些东西,还支持异常的调用堆栈获取 。
设计它时借鉴了javascript的一些机制和语法但它不是javascript,它不会遵守ECMA规范。
对脚本的VM做了性能方面的优化,目前的性能已经可以满足大部分业务场景的需求。
如果你也喜欢它,请给它一个Star⭐️ 欢迎PR和Issue
BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2)
13th Gen Intel Core i7-13700KF 3.40GHz, 1 CPU, 24 logical and 16 physical cores
.NET SDK 10.0.101
[Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3
DefaultJob : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3
| Method | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| TestIfTrue | 225.1 ns | 0.21 ns | 0.19 ns | 224.8 ns | 225.5 ns | - | - | - | - |
| TestIfFalse | 227.0 ns | 0.54 ns | 0.51 ns | 225.6 ns | 227.5 ns | - | - | - | - |
| TestNegate | 230.9 ns | 0.57 ns | 0.54 ns | 230.1 ns | 231.9 ns | - | - | - | - |
| TestExecuteEmpty | 233.6 ns | 1.16 ns | 1.08 ns | 231.3 ns | 235.6 ns | - | - | - | - |
| TestMulVar | 238.0 ns | 0.39 ns | 0.36 ns | 237.4 ns | 238.7 ns | - | - | - | - |
| TestDivVar | 240.3 ns | 0.74 ns | 0.69 ns | 238.8 ns | 241.3 ns | - | - | - | - |
| TestAddVar | 240.6 ns | 0.54 ns | 0.50 ns | 239.7 ns | 241.3 ns | - | - | - | - |
| TestLessThan | 243.2 ns | 0.50 ns | 0.45 ns | 242.5 ns | 244.1 ns | - | - | - | - |
| TestGetVar | 243.5 ns | 0.39 ns | 0.37 ns | 242.8 ns | 244.1 ns | - | - | - | - |
| TestBitXor | 244.8 ns | 0.37 ns | 0.35 ns | 244.1 ns | 245.4 ns | - | - | - | - |
| TestNot | 247.4 ns | 0.30 ns | 0.27 ns | 247.0 ns | 247.9 ns | - | - | - | - |
| TestDecerment | 247.6 ns | 0.58 ns | 0.54 ns | 246.7 ns | 248.3 ns | - | - | - | - |
| TestEqual | 248.0 ns | 0.20 ns | 0.19 ns | 247.7 ns | 248.2 ns | - | - | - | - |
| TestSubVar | 248.9 ns | 0.57 ns | 0.53 ns | 248.3 ns | 249.8 ns | - | - | - | - |
| TestTypeOf | 249.1 ns | 0.31 ns | 0.29 ns | 248.7 ns | 249.6 ns | - | - | - | - |
| TestNotEqual | 249.9 ns | 0.94 ns | 0.88 ns | 249.0 ns | 251.4 ns | - | - | - | - |
| TestAndVar | 250.4 ns | 0.23 ns | 0.20 ns | 250.0 ns | 250.8 ns | - | - | - | - |
| TestSetVar | 250.7 ns | 0.41 ns | 0.39 ns | 250.3 ns | 251.4 ns | - | - | - | - |
| TestIncerment | 250.8 ns | 0.31 ns | 0.27 ns | 250.3 ns | 251.2 ns | - | - | - | - |
| TestLessEqual | 251.1 ns | 0.80 ns | 0.75 ns | 249.9 ns | 252.0 ns | - | - | - | - |
| TestGreaterEqual | 252.2 ns | 0.78 ns | 0.73 ns | 251.0 ns | 253.1 ns | - | - | - | - |
| TestBitOr | 252.5 ns | 0.32 ns | 0.29 ns | 252.0 ns | 253.1 ns | - | - | - | - |
| TestGreaterThan | 253.7 ns | 0.31 ns | 0.29 ns | 253.1 ns | 254.1 ns | - | - | - | - |
| TestBitNot | 254.8 ns | 0.41 ns | 0.38 ns | 254.1 ns | 255.4 ns | - | - | - | - |
| TestMod | 255.0 ns | 0.46 ns | 0.41 ns | 254.1 ns | 255.7 ns | - | - | - | - |
| TestOrVar | 256.9 ns | 0.86 ns | 0.77 ns | 255.9 ns | 258.3 ns | - | - | - | - |
| TestSetProperty | 257.0 ns | 1.13 ns | 1.05 ns | 255.7 ns | 259.3 ns | 0.0029 | - | - | 48 B |
| TestSetElement | 257.9 ns | 0.38 ns | 0.35 ns | 257.0 ns | 258.4 ns | - | - | - | - |
| TestGetElement | 260.6 ns | 0.70 ns | 0.66 ns | 259.8 ns | 261.9 ns | - | - | - | - |
| TestGetProperty | 269.3 ns | 0.47 ns | 0.44 ns | 268.6 ns | 270.2 ns | - | - | - | - |
| TestAddSI | 283.6 ns | 1.02 ns | 0.95 ns | 282.3 ns | 285.6 ns | 0.0076 | - | - | 120 B |
| TestGetModule | 328.5 ns | 1.36 ns | 1.20 ns | 327.5 ns | 331.4 ns | 0.0091 | - | - | 144 B |
| TestClone | 515.6 ns | 1.43 ns | 1.34 ns | 512.7 ns | 517.6 ns | 0.0677 | - | - | 1072 B |
| TestIterator | 822.1 ns | 2.24 ns | 2.09 ns | 818.8 ns | 825.1 ns | 0.0668 | - | - | 1056 B |
| TestDeConstruct | 919.2 ns | 2.12 ns | 1.98 ns | 915.2 ns | 922.7 ns | 0.2718 | 0.0029 | - | 4264 B |
| TestDatetime | 1,159.9 ns | 7.61 ns | 7.11 ns | 1,148.1 ns | 1,171.5 ns | 0.1202 | - | - | 1888 B |
| TestJson | 1,907.2 ns | 6.98 ns | 6.53 ns | 1,898.5 ns | 1,920.3 ns | 0.2861 | - | - | 4520 B |
| TestRegex | 3,604.4 ns | 8.77 ns | 7.77 ns | 3,594.0 ns | 3,620.1 ns | 0.5531 | 0.0038 | - | 8696 B |
| TestClrType | 6,812.7 ns | 12.54 ns | 11.11 ns | 6,784.6 ns | 6,832.1 ns | 0.1221 | - | - | 1944 B |
| TestStringBuffer | 37,419.4 ns | 105.17 ns | 93.23 ns | 37,245.9 ns | 37,591.2 ns | 4.3945 | - | - | 69584 B |
| TestMD5Sum | 110,246.3 ns | 134.53 ns | 125.84 ns | 109,968.9 ns | 110,437.2 ns | 0.9766 | - | - | 16936 B |
| TestStrings | 979,702.8 ns | 3,497.44 ns | 3,271.51 ns | 975,041.8 ns | 986,709.0 ns | 220.7031 | - | - | 3481472 B |
| TestClosure | 1,003,582.0 ns | 2,424.35 ns | 2,267.74 ns | 1,000,014.3 ns | 1,007,457.6 ns | - | - | - | 496 B |
| TestObjects | 1,383,228.6 ns | 5,413.17 ns | 5,063.48 ns | 1,373,949.8 ns | 1,391,253.3 ns | 326.1719 | - | - | 5139952 B |
| TestArrays | 1,404,547.1 ns | 16,174.92 ns | 15,130.03 ns | 1,351,338.7 ns | 1,413,918.4 ns | 199.2188 | 199.2188 | 199.2188 | 2227105 B |
| TestFor100W | 10,255,789.1 ns | 127,175.07 ns | 112,737.38 ns | 10,024,128.1 ns | 10,384,582.8 ns | - | - | - | - |
| TestMD5Sum100 | 11,206,236.6 ns | 24,191.28 ns | 22,628.54 ns | 11,157,268.8 ns | 11,242,393.8 ns | 93.7500 | - | - | 1693600 B |
- Domain
- 模块
- 方法+闭包
- 方法调用/Clr方法调用
- 脚本执行 中断/继续
- where for
- export 导出模块方法和变量
- Import
- MD5函数与Javascript输出一致
- 迭代器 Iterator
- for in
- 调试符号表、闭包方法名、行号、列号、调用者行号
- 导出属性的访问权限 export const, 变量编译期间检测,属性运行时检测
- 固定大小的本地变量表测量
- CallFrame 和 ExecuteContext复用 降内存
- 脚本对象NumberValue、StringValue的优化
- Regex 正则表达式
- JSON 序列化支持
- CLR类型定义, 支持在脚本中创建对象
- CLR类型自动解析
- 闭包方法的引用、回调。
- 脚本用户上下文
- Date 日期对象实现
- 数组和对象的解构(仅实现合并)
- const常量编译期间折叠优化
- import 'constant.as' 语法,直接包含脚本文件,仅引入constant定义
- Enum 枚举类型支持,已完成语法解析,未实现编译支持
隔离的脚本环境,Domain之间的执行环境是隔离的,但是他们共用了AuroraEngine的Global对象
Domain也有自己的Global对象,他继承了AuroraEngine的Global对象
每个脚本文件为一个Module,可以理解为一个对象。
Module内的root方法和root变量作为 Module的Property,他们是可以被外部访问的。
在脚本头部可以通过使用@module("MODULENAME");定义Module名字,如果未定义则使用文件的相对路径作为Module名字
Module之间可以通过 Import xxx from 'modulefile'; 进行引用,这里会将modulefile作为当前module的xxx变量,这样你可以通过xxx.yy来访问modulefile的导出方法或属性。
方法调用支持闭包方法(支持function|func关键字)、Lambda方法、方法指针,你可以发挥你的想象。
脚本中可以通过yield指令进行中断,中断后Execute方法会立即返回,可以通过ExecuteContext的Continue方法从中断位置继续执行。
也可以通过ExecuteOptions选项禁用yield指令,或通过AutoInterruption字段定义自动中断机制。
在异常上下文上调用Continue可能会导致不可预料的结果,在计算类的脚本执行过程中出现异常应拒绝Continue继续执行,异常继续的机制适用于在面向方法的脚本中。
- Null
- Boolean
- Number
- String
- Object
- Array
- Regex
- Date
- Function
- ClrType
- ClrFunction
- ClrBonding
- StringBuffer
- console
- JSON
- Math
this 始终指向当前模块对象
global 指向Domain的Global对象
$state 指向当前执行上下文 用户State
$args 获取当前方法的参数数组
$('modulename') 动态获取模块名 TODO
// 创建一个脚本环境
var engine = new AuroraEngine(new EngineOptions() { BaseDirectory = "./tests/" });
// 编译脚本,编译器会扫描工作目录下所有脚本文件,并根据Import语句自动编译依赖的脚本
await engine.BuildAsync();// 1. 普通方法,直接设置属性值, 可读性、可写性、可枚举性都为true
engine.Global.SetPropertyValue("PI", g.GetPropertyValue("PI"))
// 2. 高级方法,支持设置属性的可读性、可写性、可枚举性
engine.Global.Define("PI", g.GetPropertyValue("PI"));
// 3. 定义CLR方法
engine.Global.Define("debug", new ClrFunction(LOG), writeable: false, enumerable: false);
// CLR方法
public static ScriptObject LOG(ExecuteContext context, ScriptObject thisObject, Span<ScriptDatum> args)
{
if (args != null && args.Length > 0)
{
Console.WriteLine(string.Join(", ", args.Select(a => a.ToObject()?.ToString() ?? "null")));
}
return ScriptObject.Null;
}
// 注册 CLR 类型别名,供脚本侧使用
engine.RegisterClrType("Math", typeof(System.Math));
engine.RegisterClrType("StringBuilder", typeof(System.Text.StringBuilder));
// 脚本侧可直接调用:
// let sb = StringBuilder("seed");
// console.log(Math.Abs(-42));
// 获取全局变量
var pi = engine.Global.GetPropertyValue("PI");
// Domain 的全局变量
domain.Global.SetPropertyValue("PI", g.GetPropertyValue("PI"))
domain.Global.Define("PI", g.GetPropertyValue("PI"));
var pi = domain.Global.GetPropertyValue("PI");// 1. 如果你的模块脚本顶级语句使用了全局变量,那么你需要提前创建好Global环境。
var g = engine.NewEnvironment();
g.Define("PI", new NumberValue(Math.PI), readable: true, writeable: false, enumerable: false);
var domain = engine.CreateDomain(g);
// 2. 如果你的模块脚本顶级语句没有使用模块以外的变量,可以直接创建一个Domain
var domain = engine.CreateDomain();
domain.Global.Define("PI", new NumberValue(Math.PI), readable: true, writeable: false, enumerable: false);var forCount = 10000000;
// 执行UNIT_LIB中的 forTest 方法 并传入参数 10000000
var testFor = domain.Execute("UNIT_LIB", "forTest", new NumberValue(forCount));
Console.WriteLine($"for:{forCount} UsedTime {testFor.UsedTime}ms");// 1. 手动控制中断继续
var testContinue = domain.Execute("UNIT_LIB", "testContinue");
if (testContinue.Status == ExecuteStatus.Interrupted)
{
testContinue.Continue();
}
// 2. 自动完成,如遇到中断自动继续直到完成,如果遇到异常则返回
var testContinue = domain.Execute("UNIT_LIB", "testContinue").Done();
// 3. 自动完成,不管异常还是中断都可以继续执行直到完成
var testContinue = domain.Execute("UNIT_LIB", "testContinue").Done(AbnormalStrategy.Continue);// 1. 双引号
log("Hello");
// 2. 单引号
log('Wrold');
// 3. 多行文本
log(`1. 这是一个特殊的字符串模板
2. 支持多行文本
3. 它会让代码看起来更舒服
4. <Buy/@Buy> <Close/@Close>`);
// 4. 多行文本
log(
|> 1. 这是一个特殊的字符串模板
|> 2. 支持多行文本
|> 3. 它会让代码看起来更舒服
|> 4. <Buy/@Buy> <Close/@Close>
);