Repository navigation
Script Native Types
用源生成器把 C# Core 方法暴露为脚本 Type,并在类型已证明时由编译器直调。
适用于 4.0.0。
首页 · 宿主集成 · .NET 宿主 API 参考
Important
这与脚本 native func 以及 NativePackages(fs / http)不是同一概念。
-
脚本原生类型:C# 上的
[NativeType]/[Export],生成 Datum 适配器、脚本 Type 与编译器目录。引擎自有的 String / Number / Int64 / UInt64 也走这条路径。 -
native func:脚本模块函数的显式 ABI。 -
扩展包:宿主通过
NativePackages启用的fs、http。导入后是脚本模块,但类别是扩展包,不是「原生模块」。
本页目录
当宿主全局应当是带类型化 Core 方法的脚本 Type,而不是 BondingFunction 或普通 CLR 委托时使用本路径。
应用 Type 必须通过 WithNativeTypes、AddNativeType<T>() 或 AddNativeTypes 加入引擎 catalog;ClearNativeTypes() 会清掉已选的应用 Type。引擎把它们注册到共享全局。每个类型必须是带 [NativeType] 与生成 Register 的 sealed 顶层 ScriptObject。包类型请用 WithPackages,不要列入 NativeTypes。
引用 AuroraScript.JIT 即会自动带上宿主源生成器(打在包内 analyzers/dotnet/cs/,没有独立的 Generators NuGet 包)。解决方案内引用 AuroraScript 工程时同样会挂上分析器。
声明 public sealed partial 顶层类,并标注 [NativeType]。Persistence / OnlyRun 把直调发到 AuroraScript.Generated 程序集,因此不支持 internal NativeType。仅含静态成员、且没有导出构造函数的 Type 可以派生 ScriptObject,也可以不派生;含实例导出或导出构造函数时必须派生 ScriptObject。生成代码会加上 IAuroraNativeInstance 标记。
using AuroraScript.Hosting;
using AuroraScript.Runtime;
using AuroraScript.Runtime.Types;
[NativeType("Stats")]
public sealed partial class StatsSupport : ScriptObject
{
[Export("mean", MatchFailure.ReturnNaN)]
public static double MeanCore(double a, double b) => (a + b) / 2D;
[Export("echo", MatchFailure.Throw)]
public static ScriptDatum EchoCore(ScriptDatum value) => value;
}通常一次在引擎上选出应用 Type。引擎把它们注册到共享全局:
var options = EngineOptions.Default.WithCompiler(compiler =>
compiler.AddNativeType<StatsSupport>().AddNativeType<Vec2>());需要挂到嵌套对象、或不在 catalog 中的 Type 时,再调用生成的 Register:
StatsSupport.Register(global);Register 接受任意 ScriptObject。不要手写 [AuroraGeneratedExport] / [AuroraGeneratedConstant] / [AuroraGeneratedNativeObject]。
Export 的归属直接由 CLR 形状决定:static Core 属于脚本 Type 对象,instance Core 属于普通 NativeType 实例。引擎自有 NativeReceiverType 的实例 Core 使用独立的 [ReceiverExport],因此不再需要额外的 Target 枚举。
同一 [NativeType] 也支持固定形状的原生实例。含实例导出的类型必须派生 ScriptObject。原生字段和 getter/setter 保持 CLR 存储或 Core 实现;未标注 [Export] 的 CLR 成员不会经反射回退暴露。普通实例方法位于该 NativeType 独享的生成原型上。
using AuroraScript.Hosting;
using AuroraScript.Runtime.Types;
[NativeType("Vec2")]
public sealed partial class Vec2 : ScriptObject
{
[Export("x")] public double X;
[Export("y")] public double Y;
[Export]
public Vec2(double x, double y) : base(NativePrototype)
{
X = x;
Y = y;
}
[Export("length")]
public double LengthCore() => Math.Sqrt((X * X) + (Y * Y));
}脚本即可构造并使用实例:
var vec = new Vec2(3, 4);
return vec.length();宿主也可以 new Vec2(3, 4) 并以 ScriptDatum.FromObject(vec) 传入脚本。
规则:
- 实例字段必须是 public 的
double、int、bool或string。 - 实例方法使用与静态导出相同的 Core 签名,但没有
thisObject参数。 - 只要存在普通实例方法,生成器就会创建惰性初始化的
NativePrototype,并通过RegisterNativeMembers按Writable、Enumerable注册原型成员;没有可写成员时才冻结。所有用户编写的构造函数都必须显式调用base(NativePrototype);生成器自行补充的构造函数会自动调用。 - 仅含原生字段、getter/setter 或静态导出的类型不会生成
NativePrototype,此时沿用普通ScriptObject构造函数,不要写base(NativePrototype)。 - 原生字段以及
IsGetter/IsSetter导出由生成的GetPropertyDatum/SetPropertyDatum重写直接路由,不进入 HiddenClass,也不注册为原型方法。 - 原型方法不可枚举,因此动态调用
vec.length()有效,但Object.keys(vec)只包含可枚举的原生字段和动态自有属性。 - 脚本
new需要恰好一个标了[Export]的 public 构造函数。没有导出构造函数时,该 Type 不可构造。 - 导出的静态方法仍写在实例 CLR 类上,可以访问其私有字段。
- 不要把原生字段放进 HiddenClass 槽位;脚本额外添加的属性仍走普通对象路径。
编译期证明局部变量始终持有某一原生对象类型后,该局部以 CLR 类型存储(例如 Vec2),而不是 ScriptDatum。new、字段读写、++/--、+= 一类复合赋值以及方法调用直接绑定 CLR 构造函数、字段和方法(newobj / ldfld / stfld / callvirt),不再装箱拆箱。被闭包捕获的局部、被赋成无法证明类型的值,以及无法证明的接收者(例如函数参数)仍走动态属性协议。
静态 [Export] 方法与 public static readonly double 常量挂在生成的 Type 上(例如 Vec2.Type)。返回 Vec2 的静态工厂会保留具体原生类型证明。
脚本用 context player as Vec2; 把当前 ScriptContext.UserState 证明为该实例类型。每个使用该名称的函数入口加载一次并缓存。export native func current() Vec2 的 $native 返回 CLR Vec2;已证明的调用点不再经过 ScriptDatum.FromObject / ToObject。类型必须是 public 并列入 WithNativeTypes。
已有 public 实例属性时,将 [Export] 放到 get 访问器上即可,无需再写转发用的 Core 方法:
public int Count
{
[Export("count", IsGetter = true)]
get => _count;
}目前不支持把 [Export] 放在属性声明上。getter 必须无参数、无 context 前缀,返回类型属于生成器支持的类型。显式填写脚本成员名;生成器将 CLR get_Count 记录为入口,类型已证明的读取直接调用它,动态读取沿用生成的适配器。IsSetter 仍用于一个必需参数且返回 void 的实例方法。
内置 Date 的 Year、Month、Day、Hour、Minute、Second、Millisecond、DayOfYear 均采用此方式,已移除对应 *Core() 包装器。Date 保留已有动态适配器及冻结原型上的 getter。DayOfWeekCore、TicksCore 仍负责枚举/整数到脚本 Number 的转换,不能直接用不同返回类型的 CLR 属性替换。
减少包装器的明确收益是减少重复实现;原来的简单方法可能被 JIT 内联,因此不承诺固定的运行性能提升。
实现 INativeTypedDocument,并把类型列入 WithNativeTypes 后,脚本 tdoc Vec2 { x 3, y 4 } / tdoc Vec2 [3, 4] / tdoc Flag false 以及宿主 TDoc.parse / TDoc.stringify 会直接构造该 NativeType,而不是 ClrInstanceObject。
WriteTypedDocument 决定规范形态:WriteMember 写成对象,WriteElement 写成数组,WriteValue 写成标量(null / boolean / number / string)。ReadTypedDocument 通过 TypedDocumentInput 读取这三种形态。构造由 NativeType 生成器补上,不必手写 CreateTypedDocument()。
详见 宿主集成 与 Typed Document。
支持的参数与返回类型:
-
double、int、long、ulong、bool、string - CLR
int对应脚本小写int32。检查边界要求精确有符号 32 位整数;已证明的直调使用System.Int32ABI,整数算术按 CLRint环绕。 - CLR
long/ulong对应脚本int64/uint64。同类型脚本算术按 CLR 整数环绕;混合运算成为double。 - CLR
void返回:过程。直调语句不物化结果;动态、导出或取值调用观察到脚本null,与脚本native func work(...) void一致。 ScriptDatum- 任意
ScriptObject子类(ScriptArray、packed array、Path、Proxy、Regex、Date、HashMap、Error、ClosureFunction、包装器)
可选隐式前缀,且只能按此顺序:
-
ScriptContext ctx— 不是脚本实参。 -
ScriptObject thisObject— 接收者;参数名必须为thisObject。
允许尾部 C# 默认值。params double[] 与 params ScriptDatum[] 仅用于 Datum 适配器。普通对象 NativeType 也可以用显式 DynamicAdapter 保留宽泛的动态行为,同时导出共享该适配器的固定参数 Core 重载供编译器直调;Path.append 的一段、两段和三段重载采用这一模式。
public static readonly double 字段可标 [Export("PI")],成为脚本常量。未遮蔽读取发射 ldsfld,而不是装箱的 ldc.r8。
[AuroraParam(MatchLevel.Exact)] 或 Strict 收紧适配器强制转换。MatchFailure 控制不匹配行为:Default 对数值返回推断为 NaN,否则为 null;Throw 抛出 AuroraRuntimeException。
对象返回值通过 ScriptDatum.WriteObject / FromObject 保留原来的 ValueKind,因此 Array、Date、Error 不会塌成普通 object。
不支持:async、Span/ref/out/in、泛型方法、嵌套类型、record、空导出名、params 与 [AuroraParam] 同时使用、把 ctx/thisObject 放在脚本参数之后。
参数类型已证明时,编译器直接调用 Core 方法。条件:
- 未遮蔽的全局接收者(
Math.abs(x),而不是var m = Math; m.abs(x))。 - 无
params的 public Core 方法。 - 实参类型可证明兼容,含尾部可选默认值。
- 目录元数据存在。引擎程序集始终扫描。应用 Type 必须列入
CompilerOptions.NativeTypes:
var options = EngineOptions.Default.WithCompiler(compiler =>
compiler.AddNativeType<StatsSupport>().AddNativeType<Vec2>());params 导出、spread 实参、被遮蔽的全局以及未证明的实参类型走生成的 Datum 适配器。@global() 中成员块形式的 declare type 不会驱动编译器推断(方法类型 declare type Name(...) ReturnType; 例外,它是编译期契约)。
引擎用同一套 NativeType 生成器与编译器目录实现不可变 String / Number / Int64 / UInt64 成员,而不是手写原型回调。
NativeType.NativeReceiverType 声明引擎自有的 string、double、long、ulong 存储,不是宿主扩展原始接收者表示的入口。ReceiverExport 把 static Core 标记为脚本实例成员:可选 ScriptContext 之后的第一个参数是原始 CLR 接收者。Export 标记的 static Core 始终属于 Type 对象。
NativeType.NativeConstructor 命名一个已导出的静态 Type Core,其返回类型必须匹配 NativeReceiverType;String(...) 与 new String(...) 共用它。StringValue.Register(Global) 取代手写 StringConstructor。
[NativeType(
"String",
NativeReceiverType = typeof(string),
NativeConstructor = nameof(CreateCore))]
public sealed partial class StringValue
{
[ReceiverExport("trim")]
public static string TrimCore(string value) => value.Trim();
[Export("valueOf")]
public static string CreateCore(string value = "") => value;
}省略 DynamicAdapter 时,生成器按常规强制转换、失败处理与结果写入生成动态入口。指定 DynamicAdapter 后直接使用已有回调,不再生成动态包装器;等元数重载必须共享同一个显式适配器。零参 getter 设 IsGetter = true;匹配脚本名称的一参 void Core 可设 IsSetter = true。普通对象 NativeType 的 getter/setter 走生成的属性重写;NativeReceiverType 不支持 setter。
方法与静态常量的 Writable、Enumerable 默认均为 false。Native 实例字段默认按 CLR 可变性决定是否可写,并默认可枚举;作者可显式覆盖。可写的方法或常量槽可能被脚本替换,不进入 Native 直调目录。只要生成的原型或 Type 含可写槽,它就不会被冻结。
编译器在类型分析阶段选定并保存已证明的签名,再发出直调。String 的 substring / slice 只导出 int 索引,第二个索引仍是 end 而不是 length。已证明的 string 接收者走 CLR 直调;未知接收者与不支持的调用形状保持动态分派。split 返回 ScriptArray;match / matchAll 返回 ScriptDatum,因为结果形状不是无条件的。
String.fromCharCode 取原生 int(第一个 UTF-16 代码单元);String.compare 比较两个 string 的首个代码单元,任一侧为空时返回 1。填充宽度是原生 int,填充字符仍取第一个 UTF-16 代码单元。
Number 的 double 接收者允许 int / uint Core 特化,必须带显式兼容适配器,不能把任意动态 Number 静默收窄为 Int32。NumberValue.FormatString 对 double / int / uint / long / ulong 重载;精确 64 位值不经 double。Int64 / UInt64 有冻结自有原型:toString(16) 格式化全部 64 位大写十六进制;无参为不变区域性十进制;其它进制仍选十进制。Number 保留当前区域性格式化以及历史 Int32 十六进制(含大于 Int32.MaxValue 的 UInt32 兼容路径)。
在 Resolver 可见的 @global() 文件中手写编辑器契约。不要使用 declare class、declare module 或结构体 type 来描述宿主 Type。成员签名仅供语言服务,不参与类型推断。
@global();
declare type Stats {
static func mean(Number a, Number b) Number;
}
declare type Vec2 {
constructor(Number x, Number y);
Number x;
func length() Number;
}仅含 static 成员、没有 constructor 的 Type 不可 new。typeof(Math) / typeof(console) 为 "type"。
| 代码 | 级别 | 含义 |
|---|---|---|
AURORAEXP001 |
Error | 无效的 NativeType 声明 |
AURORAEXP002 |
Error | 不支持的导出成员或签名 |
AURORAEXP003 |
Error | 重复的脚本成员名 |
引擎始终注册 Math、JSON、TDoc、console、Conv8 和 HotPatch 为基础设施 Type,不必列入 WithNativeTypes。typeof 为 "type";没有导出构造函数时 new Math() / new console() / new Conv8() 失败。
Math.max / Math.min 使用 params double[],因此始终走适配器;Math.abs 与 Math.PI 在未遮蔽且类型已证明时可直调或 ldsfld。Conv8 的固定参数读写在 UInt8Array 与整数偏移已证明时可直调 Core。
console.log 复用普通导出重载选择:原始类型实参可选择对应 CLR 重载,一个或两个未知类型实参可直调 ScriptDatum 重载。spread 或不支持的参数形状仍使用共享动态适配器。
- 宿主集成 — 引擎、resolver、domain 与 CLR 互操作。
- NativeTypeAttribute · ExportAttribute · ReceiverExportAttribute
- INativeTypedDocument · TypedDocumentOutput · TypedDocumentInput
-
Native 函数 — 脚本侧
native func。 - MCP 资源:
aurora://docs/host-integration、aurora://schema/host-api、aurora://schema/features。
AuroraScript.JIT 4.0.0 · 文档首页 · 仓库 · MIT License
- 宿主集成
- 脚本原生类型
- .NET 宿主 API 参考
- 引擎与执行
- 编译配置
- 源解析
- 脚本原生类型
- NativeTypeAttribute
- NativePackageAttribute
- ExportAttribute
- ReceiverExportAttribute
- AuroraExportValueKind
- AuroraParamAttribute
- AuroraGeneratedExportAttribute
- AuroraGeneratedConstantAttribute
- MatchFailure
- MatchLevel
- IAuroraNativeInstance
- INativeTypedDocument
- AuroraNativeObject
- AuroraNativeObjectAttribute
- 运行时值
- ScriptDatum
- ValueKind
- ScriptObject
- ScriptArray
- ScriptType
- TypeAccess
- ObjectFlags
- NumberValue
- Int64Value
- UInt64Value
- StringValue
- BooleanValue
- NullValue
- ScriptDate
- ScriptError
- ScriptHashMap
- ScriptRegex
- ScriptProxy
- StringBuffer
- ScriptEnumerator
- ScriptFunctionDelegate
- ScriptMethodDelegate
- ClosureFunction
- CLR 互操作
- 宿主 TDoc
- 扩展包
- 序列化与调试
- 异常与诊断