-
Notifications
You must be signed in to change notification settings - Fork 0
Native Functions
显式声明可编译为 CLR 原生 ABI 的模块函数,用于热路径上的直接调用与零分配数值内核。
适用于 4.0.0。
Important
native func 与「原生模块」不是同一概念。
AuroraScript 的普通 func 使用灵活的 ScriptDatum 调用约定:参数装箱、闭包帧、热补丁友好,但热循环中的数值调用会有额外开销。
native func 是模块作用域上的显式 ABI 契约。编译器为每个 native 函数生成:
-
name$native:接收ScriptContext与声明类型的 CLR 参数,函数体直接发射到此方法;不推送脚本调用帧,也不包装 try/catch。 -
name$typed(按需):与ScriptDatum兼容的壳层,供宿主Execute、未证明类型的脚本调用,以及把函数当作值逃逸时使用。
因此,native func 既是性能优化手段,也是语义边界:它不改变 AuroraScript 仍是动态脚本语言这一事实,但为已声明的热路径提供可预测的原生 CIL 表示。
在以下情况考虑 native func:
- 热循环或模块边界上反复调用的数值 / 布尔 / packed array 辅助函数。
- 需要稳定参数与返回类型契约,且调用方参数在编译期可证明兼容。
- 希望跨模块导入后,证明兼容的调用直接走
$native,避免通用动态分派。
不应在以下情况使用:
- 需要 HotPatch 增删改函数(native 函数不可热更新)。
- 需要默认参数、rest 参数(
...)或$args。 - 需要把函数重新赋值或当作可替换的回调槽(native 函数不可赋值)。
- 整个 API 都应保持完全动态、频繁变更且依赖热补丁——请继续使用普通
func。
native 是上下文关键字:仅修饰模块作用域的 func / function 声明;在其他位置仍是合法标识符。
@module(MATH);
// 私有 native 辅助函数
native func add(Number left, Number right) Number {
return left + right;
}
// 导出的 native 入口
export native func distance(Number dx, Number dy) Number {
return Math.sqrt(dx * dx + dy * dy);
}
export func run() Number {
return add(20, 22);
}
ABI 要求:
| 要求 | 说明 |
|---|---|
| 模块作用域 | 仅模块顶层;不能嵌套在块或另一函数内。 |
| 显式返回类型 | 必须写 Number、Boolean、String、Object、数组类型等返回契约;省略会编译失败。 |
| 显式参数类型 | 参数类型决定原生槽位;未声明类型的参数走 ScriptDatum。 |
无默认 / rest / $args
|
签名必须固定,便于生成稳定 $native 方法。 |
| 不可赋值 | 不能对 native 函数名做 =、+= 等赋值。 |
| 正常构建 | 仅通过完整编译引入或修改;不能通过热补丁添加、替换或重定义。 |
可配合 export 导出;私有 native 函数仍可在模块内被证明兼容的调用直接命中 $native。
编译器将函数体直接发射到 name$native,第一个参数为隐式的 ScriptContext(脚本源码中不可见)。声明为 Number、Boolean、Int32 流以及 packed array 的参数与局部变量,在证明稳定时保持为 CLR 原生表示(double、bool、int、packed 存储等),避免在算术循环中反复构造 ScriptDatum。
函数体内仍可使用完全动态的表达式(例如读取 Object 属性、调用普通 func)。编译器仅在跨越动态边界的表达式处转换为 ScriptDatum;不会因此让整个函数回退到纯动态路径。其余证明稳定的局部变量继续走原生 CIL。
export native func weighted(Number value, Object options) Number {
// value 保持 CLR double;options.factor 的动态读取仅在该表达式处跨 Datum 边界
return value * options.factor;
}
| 调用场景 | 行为 |
|---|---|
| 同模块内,参数类型已证明兼容 | 直接 call $native
|
| 同模块内,参数未证明兼容 | 经 $typed 壳层,保留精确参数检查 |
跨模块导入的 export native func,参数已证明兼容 |
直接调用导入模块的 $native
|
| 跨模块,参数未证明兼容 | 经导出壳层(ScriptDatum 路径) |
宿主 ScriptDomain.Execute / 动态调用 |
经 $typed 壳层 |
私有 native 函数若作为值逃逸(例如 return increment),调用方同样走壳层路径。
$native 不为每次调用推送脚本帧,也不包裹额外异常适配器。这使热路径更接近手写 CIL 辅助方法。编译器对 $native 使用积极内联提示;是否内联仍由 CLR/JIT 决定。
声明为下列类型时,参数可映射到 CLR 原生槽位(否则为 ScriptDatum):
| 脚本类型 | CLR 表示(概念上) |
|---|---|
Number |
double |
Boolean |
bool |
String |
string |
通用 Array
|
ScriptArray |
Int8Array … UInt64Array、Float64Array、BooleanArray
|
对应 packed 存储 |
Object 及其他 |
ScriptDatum |
-
Number/ 整数流 →double或int -
Boolean→bool - 其他声明返回 →
ScriptDatum(例如String、Object)
若签名无法生成合法的原生方法(绑定/发射阶段无法构造 $native),编译器报错:cannot be emitted with a native signature。
即使被调函数是 native func,调用方仍须在每个实参上满足类型证明,才会走直接 $native 路径。把 packed array 存入普通 Object 再读回,会擦除元素类型,调用回退到动态壳层——这与 性能与基准测试 中的「强类型路径」建议一致。
Native 函数不是「整函数静态类型化」:
- 仍可使用
throw、动态属性访问、普通闭包调用与模块访问(经ScriptContext)。 - 动态表达式局部跨越 Datum 边界;不会禁用函数内其他原生局部变量。
- 需要
$args、可变 arity 或默认参数时,应使用普通func,或把可变部分拆到普通函数,由 native 内核处理固定 arity 部分。
$native 不推送脚本调用帧。运行时出错时,引擎通过 CLR 异常栈 与已记录的脚本源位置合并栈信息:RuntimeExceptionStackAnalyzer 将 $native 帧与脚本追踪关联,使 AuroraRuntimeException 仍能报告 native 函数名与脚本位置。
从 native 函数内部 throw new Error(...) 时,栈追踪可包含 native callee 与 native caller 名称(见引擎测试 NativeDirectCallsPreserveScriptStackFrames)。
| 限制 | 后果 |
|---|---|
| 不可热补丁 |
HotPatch.replace / incremental 不得添加、替换或重定义 native 函数;违反时编译失败。 |
| 不可赋值 | 不能把 native 函数绑定到其他变量槽位。 |
| 固定签名 | 无默认参数、无 ...rest、函数体内不可用 $args。 |
| 必须声明返回类型 | 无返回类型注解的 native 声明被拒绝。 |
| 仅正常构建 | 修改 native 函数需重新 BuildAsync,不能靠运行时补丁。 |
| 非全局优化开关 | 没有「自动把普通 func 提升为 native」的引擎选项;必须显式写 native func。 |
| 函数注解已移除 | 4.0 不再支持 @directCall 等函数注解;请改用 native func。 |
普通 func
|
native func |
|
|---|---|---|
| 调用约定 |
ScriptDatum / 闭包 |
$native + 可选 $typed 壳层 |
| 返回类型 | 由 flow 推断 | 必须显式声明 |
| 参数默认值 / rest | 支持 | 不支持 |
$args |
支持 | 不支持 |
| 热补丁 | 支持(宿主启用时) | 不支持 |
| 赋值 / 替换 | 支持 | 不支持 |
| 热路径分配 | 可能有装箱 | 证明路径上零分配(见基准测试) |
| 适用场景 | 通用业务逻辑 | 稳定 ABI 的热内核 |
AuroraScript 的「强类型路径」主要依赖 flow 分析,而非源码上的全面类型注解:
- 稳定数值局部、布尔条件、整数循环变量、已知 packed array → 原生 CIL 局部变量。
- Packed array → 连续原始存储,而非每元素一个
ScriptDatum。 -
native func→ 显式函数调用 ABI,避免通用属性查找与动态分派。
普通 func 在推断足够时同样能在函数体内使用原生局部变量;native func 额外固定入口/出口与跨函数调用的 ABI。若只需循环内优化,可先保持普通 func 并遵循 性能与基准测试 的编写建议;当模块边界调用本身成为瓶颈时,再提升为 native func。
@module(KERNEL);
native func clamp(Number value, Number min, Number max) Number {
if (value < min) return min;
if (value > max) return max;
return value;
}
export func run(Number input) Number {
return clamp(input, 0, 100);
}
// math.as
export native func add(Number left, Number right) Number {
return left + right;
}
// main.as
@module(APP);
import math from "./math";
export func run() Number {
return math.add(20, 22);
}
当 math.add 的两个实参在 run 中证明为 Number 时,发射代码直接调用 math 模块的 add$native。
@module(API);
export native func add(Number a, Number b) Number {
return a + b;
}
宿主 ScriptDomain.Execute("API", "add", ...) 经 $typed 壳层调用;同模块内证明兼容的脚本调用可走 $native。
AuroraScript.JIT 4.0.0 · 文档首页 · 仓库 · MIT License