Files
endorphin-tessera-gui/src/bridge/ui_bridge/bootstrap.rs
T

533 lines
28 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 安装一次 VM 域需要的宿主类型、对象和方法。类型注册表是源代码调用原生能力的入口。
//! 先建立类型与桌面基础设施,再绑定构造器、视觉协议和开发工具工厂;返回的根负责保持这些对象存活。
//!
//! 调用链:`endor_host::compile` / `ReplSession::new` 创建候选 VM,
//! 随后调用本模块的 `install`;运行时再通过 install_globals 注入源程序使用的 UI 值。
//! 本文件只建立宿主环境。源程序稍后调用构造器或实例方法时,才会进入 `calls::Function::invoke`。
//! 安装返回的 UiBridge 由运行时保存;传入的 roots 由候选运行时持有,并在失败或退役时统一释放。
use super::*;
/// 为一次候选程序/REPL 会话安装 Tessera 宿主环境,供运行时后续注入全局值和执行源程序。
///
/// 类型要先创建,签名才能引用它们;因此先分配没有签名的函数,再原地补齐 payload。
/// 所有 `?` 都把安装失败交还候选运行时,不会在这里把半成品替换成活跃程序。
/// `roots` 保存明确的 VM 拥有根;Rust 的 ObjectRef 数值和 Arc<Mutex<Session>> 均不能替代它们。
pub fn install(vm: &mut Vm, roots: &mut Vec<ObjectRef>) -> Result<UiBridge> {
// 先建立 desktop/ui/tabs 并加入显式 roots,后面初始化失败时由上层运行时统一释放,避免依赖 Rust 句柄保活。
// desktop 是此桥接域的类型注册表和窗口资源入口;它不是操作系统桌面或原生窗口。
let desktop = vm.new_object()?;
roots.push(desktop);
// 安装时尚无窗口尺寸;运行时收到 viewport 后再填充,避免在 VM 引导期间依赖 GUI 状态。
vm.set_attr(desktop, "viewport", Value::Nil)?;
// tabs 是主窗口的托管页面注册表,Presentation 和 window::install 会使用同一字典身份。
let tabs = vm.new_dict()?;
roots.push(tabs);
vm.set_attr(desktop, "tabs", Value::Object(tabs))?;
// ui 是对源代码公开的命名空间;desktop 保存内部记账,二者用途不能混同。
let ui = vm.new_object()?;
roots.push(ui);
// 原生 Session 借用托管身份,主窗口先拥有独立 Presentation;业务对象仍全部位于传入的单一 VM。
let session = Arc::new(Mutex::new(Session {
// 节点弱索引用来检查数量与回收失效记录,不应让已关闭的业务对象继续存活。
nodes: Vec::new(),
// 先登记主窗口上下文;后续窗口通过 create_window 建立各自的 Presentation。
presentations: std::collections::BTreeMap::from([(
crate::window::MAIN_WINDOW,
crate::presentation::Presentation::new(desktop, tabs),
)]),
// Session 的 Deref 按 current 选择布局/挂载状态;初始所有操作都落在主窗口。
current: crate::window::MAIN_WINDOW,
// master 始终指向主 desktop,副窗口仍借用同一 VM 的类型和资源域。
master: desktop,
// 分配器从现有最高窗口 ID 继续增长;Mount 则尚无任何已分配出现位置。
next_window: crate::window::MAIN_WINDOW,
next_mount: 0,
// 关闭后尚未交给 GUI 的原生命令暂存在这里,不能随 Presentation 删除而丢失。
retired_commands: Vec::new(),
// 累计诊断计数由刷新流程更新,供 ui.stats/开发工具观察本会话的实际工作量。
refresh_yields: 0,
refresh_restarts: 0,
layout_cancels: 0,
paint_cancels: 0,
paint_slices: 0,
binding_evaluations: 0,
// 清理阶段会置位,create_window 等操作据此拒绝在退出期间重新创建资源。
exiting: false,
}));
// 先分配函数占位再补签名,因为签名会引用尚未建立的名义类型;保存列表用于第二阶段替换 payload。
// 保存 ui.name 的函数身份及 Operation,等全部类型注册后再为这些函数补齐精确签名。
let mut public_functions = Vec::new();
// 另外记录 TypeConstructor 所用的工厂;它们多接收一个 Type 首参,不能套用自由函数签名。
let mut factories = Vec::new();
for (name, operation) in [
("implement", Operation::Implement),
("implements", Operation::Implements),
("supports", Operation::Supports),
("isInstance", Operation::IsInstance),
("isSubtype", Operation::IsSubtype),
("open", Operation::OpenValue),
("text", Operation::Text),
("rect", Operation::Rect),
("spacer", Operation::Spacer),
("row", Operation::Row),
("column", Operation::Column),
("overlay", Operation::Overlay),
("scene2D", Operation::Scene2D),
("canvasViewport", Operation::CanvasViewport),
("camera2D", Operation::Camera2D),
("button", Operation::Button),
("page", Operation::Page),
("present", Operation::Present),
("state", Operation::State),
("textOf", Operation::TextOf),
("stats", Operation::Stats),
("viewport", Operation::Viewport),
("window", Operation::Window),
] {
// 每个名字得到自己的 VM callable,实际操作由 Function 根据 operation 分派。
// desktop 放在 captures 中,确保函数仍可达时它所依赖的类型注册表也能被 GC 追踪。
let function = vm.allocate(ContextNativeFunction::new(
name,
Function {
signature: None,
operation,
owner: None,
session: session.clone(),
requested_type: None,
},
vec![desktop],
))?;
roots.push(function);
// 属性供源程序的 ui.text/ui.present 等查询;列表供本函数第二阶段补签名,职责不同。
vm.set_attr(ui, name, Value::Object(function))?;
public_functions.push((name, function, operation));
}
// Object/Type 来自 VM 类型引导,不能用普通类代替;之后所有宿主名义类型从此根体系建立。
let root_types = vm.bootstrap_types()?;
roots.extend([root_types.object, root_types.type_type]);
vm.set_attr(ui, "Object", Value::Object(root_types.object))?;
vm.set_attr(ui, "Type", Value::Object(root_types.type_type))?;
// definitions 汇集全部类型,稍后写入 desktop.types,供 desktop_type 按名称查精确身份。
let mut definitions = vec![
("Object", root_types.object),
("Type", root_types.type_type),
];
// 类型按基类先于子类的顺序创建。None 表示抽象能力/宿主结果类型,不开放普通源构造器。
for (name, base_name, constructor_name) in [
("Trait", "Object", None),
("Function", "Object", None),
("Block", "Object", None),
("ContainerBlock", "Block", None),
("TextBlock", "Block", Some("text")),
("RectBlock", "Block", Some("rect")),
("Spacer", "Block", Some("spacer")),
("ScrollBlock", "ContainerBlock", Some("scroll")),
("ScrollView", "ScrollBlock", Some("scroll")),
("Row", "ContainerBlock", Some("row")),
("Column", "ContainerBlock", Some("column")),
("Overlay", "ContainerBlock", Some("overlay")),
("Scene2D", "ContainerBlock", Some("scene2D")),
("CanvasViewport", "ContainerBlock", Some("canvasViewport")),
("Camera2D", "Object", Some("camera2D")),
("Vec3", "Object", Some("vec3")),
("Triangle3D", "Object", Some("triangle3D")),
("Geometry3D", "Object", Some("geometry3D")),
("Mesh3D", "Object", Some("mesh3D")),
("Box3D", "Mesh3D", Some("box3D")),
("Group3D", "Object", Some("group3D")),
("Scene3D", "Object", Some("scene3D")),
("DirectionalLight", "Object", Some("directionalLight")),
("PerspectiveCamera", "Object", Some("perspectiveCamera")),
("OrthographicCamera", "Object", Some("orthographicCamera")),
("OrbitController", "Object", Some("orbitController")),
("Viewport3D", "Block", Some("viewport3D")),
("Panel3D", "Object", Some("panel3D")),
("RayHit3D", "Object", None),
("Button", "ContainerBlock", Some("button")),
("Page", "ContainerBlock", Some("page")),
("Window", "Object", Some("window-create")),
("Mount", "Object", None),
("Event", "Object", Some("event")),
("Subscription", "Object", None),
("Task", "Object", None),
("ObjectList", "Object", Some("object-list")),
] {
// 依照表中基类名取得当前 VM 的真实类型,不能用另一个 VM 中的同名 Type 代替。
let base = object(Some(&vm.get_attr(ui, base_name)?))?;
// 根据构造规则选择专用工厂、通用工厂或抽象类型;这里只注册,不分配业务实例。
let constructor = match constructor_name {
// ObjectList 与 Event 有特殊的“类型首参+子类字段”构造规则,单独原生适配;其他类型共用 NativeFactory。
Some("object-list") => {
// ObjectList 构造要保持托管元素身份与字段规则,由 object_list::Constructor 处理。
let factory = vm.allocate(ContextNativeFunction::new(
"object-list",
crate::object_list::Constructor {
desktop,
signature: None,
},
vec![desktop],
))?;
roots.push(factory);
// 工厂本身也是语言中的 Function 实例,挂类型后才能通过名义函数检查。
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
vm.attach_type(factory, function_type)?;
TypeConstructor::NativeTyped(factory)
}
Some("event") => {
// Event 另有必填事件名;其专用 Constructor 在真正调用时解析事件参数和子类字段。
let factory = vm.allocate(ContextNativeFunction::new(
"event",
crate::event_dispatch::Constructor { signature: None },
vec![],
))?;
roots.push(factory);
// 与列表工厂保持相同函数类型体系;无 desktop 捕获的分配器不额外引入桌面强边。
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
vm.attach_type(factory, function_type)?;
TypeConstructor::NativeTyped(factory)
}
Some(constructor_name) => {
// 把构造标签映射到宿主 Operation;3D 族委托其元数据,未知标签立即失败,不静默创建错误类型。
let operation = match constructor_name {
"text" => Operation::Text,
"rect" => Operation::Rect,
"spacer" => Operation::Spacer,
"scroll" => Operation::Scroll,
"row" => Operation::Row,
"column" => Operation::Column,
"overlay" => Operation::Overlay,
"scene2D" => Operation::Scene2D,
"canvasViewport" => Operation::CanvasViewport,
"camera2D" => Operation::Camera2D,
"viewport3D" => Operation::Viewport3D,
"button" => Operation::Button,
"page" => Operation::Page,
"window-create" => Operation::NewWindow,
name => crate::scene3d_model::Constructor::from_function(name)
.map(Operation::SceneObject)
.ok_or(invalid("unknown native type allocator"))?,
};
// 通用 NativeFactory 在调用时区分原生参数与源子类字段,并保留 requested Type。
let factory = vm.allocate(ContextNativeFunction::new(
constructor_name,
NativeFactory {
signature: None,
options: None,
operation,
session: session.clone(),
},
vec![desktop],
))?;
roots.push(factory);
// 这个函数属于构造器元数据,不直接放在 ui.name;但仍需标为 Function 实例。
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
vm.attach_type(factory, function_type)?;
TypeConstructor::NativeTyped(factory)
}
// 没有公开分配规则的类型仍可用于类型检查/协议;直接把它当构造器调用会由 VM 拒绝。
None => TypeConstructor::Abstract,
};
// 记住实际工厂身份,类型构建完成后原地补签名,避免替换类型内指向的构造函数对象。
if let (Some(tag), TypeConstructor::NativeTyped(factory)) = (constructor_name, constructor)
{
factories.push((tag, factory));
}
// 生成新 Type 后同时公开到 ui,并加入待提交注册表,两个入口必须指向同一对象。
let definition = vm.new_type(name, base, constructor)?;
roots.push(definition);
vm.set_attr(ui, name, Value::Object(definition))?;
definitions.push((name, definition));
}
// 协议值采用普通托管类:Constraints/Bounds/Point 等字段有类型注解,PaintCommand/CameraGesture 后续再初始化属性。
for (name, fields) in [
(
"Constraints",
vec!["minWidth", "minHeight", "maxWidth", "maxHeight"],
),
("MeasureResult", vec!["width", "height"]),
("Bounds", vec!["x", "y", "width", "height"]),
("Point", vec!["x", "y"]),
("Arrangement", vec!["children"]),
("DisplayList", vec!["commands"]),
("PaintCommand", vec![]),
("CameraGesture", vec![]),
] {
// 表里的字段名转成 VM 可检查的字段声明;map 的每次调用只描述一个字段,不执行布局。
let fields = fields
.into_iter()
.map(|name| endor_core::ClassField {
name: name.into(),
// children/commands 先声明 Object 接口,后续各协议解码器再要求受控列表;几何字段统一为 Number。
type_name: if matches!(name, "children" | "commands") {
"Object"
} else {
// 余下字段都是测量/几何数值,让 VM 在实例化时先拒绝非数字实参。
"Number"
}
.into(),
})
.collect();
// 这些协议值只携带源字段,使用普通 class 分配;不需要 Block 的原生 payload。
let definition = vm.new_class(
name,
root_types.object,
fields,
std::collections::BTreeMap::new(),
)?;
roots.push(definition);
vm.set_attr(ui, name, Value::Object(definition))?;
definitions.push((name, definition));
}
// The desktop owns the registry through ordinary traced attributes. The
// Rust session only borrows handles and adds no hidden lifetime roots.
// 桌面通过普通属性拥有类型字典;后续 nominal 查询必须使用同一 desktop 中注册的精确类型身份。
// 类型字典由 desktop 属性托管持有;后续桥接调用可从其查找 Block、Event 等精确类型。
let types = vm.new_dict()?;
roots.push(types);
for (name, definition) in definitions {
// 字符串键是宿主查询约定,值才是参与 is_instance/is_subtype 的真实名义身份。
vm.dict_set(types, Value::Text(name.into()), Value::Object(definition))?;
}
vm.set_attr(desktop, "types", Value::Object(types))?;
vm.attach_type(desktop, root_types.object)?;
vm.attach_type(ui, root_types.object)?;
// 安装后续编译函数的默认类型,也用于下方所有方法 wrapper 的 attach_type。
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
// 让后续 VM 创建的函数使用宿主 Function 名义类型,并打开源类型检查,再安装完整本机签名。
vm.set_function_type(function_type)?;
vm.set_source_type_checks(true)?;
// 以同一函数身份补完 operation_signature,不改变先前 ui 属性引用的函数对象。
for (name, function, operation) in public_functions {
vm.replace_payload(
function,
ContextNativeFunction::new(
name,
Function {
signature: Some(operation_signature(vm.object_store(), desktop, operation)?),
operation,
owner: None,
session: session.clone(),
requested_type: None,
},
vec![desktop],
),
)?;
}
for (name, function) in factories {
// 恢复第一阶段记录的分配器类别;Event/ObjectList 不走 NativeFactory,需要单独补外壳。
let operation = match name {
"text" => Some(Operation::Text),
"rect" => Some(Operation::Rect),
"spacer" => Some(Operation::Spacer),
"scroll" => Some(Operation::Scroll),
"row" => Some(Operation::Row),
"column" => Some(Operation::Column),
"overlay" => Some(Operation::Overlay),
"scene2D" => Some(Operation::Scene2D),
"canvasViewport" => Some(Operation::CanvasViewport),
"camera2D" => Some(Operation::Camera2D),
"viewport3D" => Some(Operation::Viewport3D),
"button" => Some(Operation::Button),
"page" => Some(Operation::Page),
"window-create" => Some(Operation::NewWindow),
// None 在本轮只表示专用构造器,不表示前一阶段的 Abstract 类型。
"event" | "object-list" => None,
name => Some(
crate::scene3d_model::Constructor::from_function(name)
.map(Operation::SceneObject)
.ok_or(invalid("unknown deferred factory"))?,
),
};
if let Some(operation) = operation {
// options 只描述原生构造选项,调用时由 NativeFactory 在切掉 Type 和子类字段后校验。
let options = operation_signature(vm.object_store(), desktop, operation)?;
// 外层 NativeTyped 工厂接收 Type 首参和任意候选尾参,允许子类字段;NativeFactory 用 options 再核对基构造参数。
let envelope = CallSignature {
arity: endor_core::CallArity::range(options.arity.required + 1, usize::MAX),
parameters: vec![AnnotationType::Type.into()],
variadic: Some(AnnotationType::Any.into()),
returns: options.returns.clone(),
effect: options.effect,
};
// 初始强边是注册表;随后追加签名引用的类型,供 ContextNativeFunction::trace 使用。
let mut captures = vec![desktop];
// 参数和返回注解中的名义类型也是托管引用,必须纳入 captures,不能只把它们存进 Rust 签名结构。
captures.extend(options.references());
vm.replace_payload(
function,
ContextNativeFunction::new(
name,
NativeFactory {
signature: Some(envelope),
options: Some(options),
operation,
session: session.clone(),
},
captures,
),
)?;
} else {
// 特殊构造器保留宽变参外壳,再由对应 Constructor 按类型字段数拆实参,返回准确 Event/ObjectList 名义类型。
// 为返回值选取专用名义类型,避免宽变参外壳同时放宽了返回值检查。
let kind = if name == "event" {
"Event"
} else {
"ObjectList"
};
// Event 的 Type 之后还有必填字符串事件名;ObjectList 只有 Type 为必填,其余由实现解释。
let signature = CallSignature {
arity: endor_core::CallArity::range(
// 这里包含 Type 首参;源子类字段由构造器验证,所以最大个数保留开放。
if name == "event" { 2 } else { 1 },
usize::MAX,
),
// 固定前缀与 required 数量保持一致,额外实参由 variadic 和字段验证共同约束。
parameters: if name == "event" {
vec![AnnotationType::Type.into(), AnnotationType::String.into()]
} else {
vec![AnnotationType::Type.into()]
},
variadic: Some(AnnotationType::Any.into()),
returns: AnnotationType::Nominal(desktop_type(vm.object_store(), kind, desktop)?)
.into(),
effect: CallEffect::ReadOnly,
};
if name == "event" {
// 原地更新 Event 工厂,TypeConstructor 中已保存的对象引用继续有效。
vm.replace_payload(
function,
ContextNativeFunction::new(
name,
crate::event_dispatch::Constructor {
signature: Some(signature),
},
vec![],
),
)?;
} else {
// 这里只有 ObjectList;保留 desktop 捕获,以便专用分配器给结果附加列表类型。
vm.replace_payload(
function,
ContextNativeFunction::new(
name,
crate::object_list::Constructor {
desktop,
signature: Some(signature),
},
vec![desktop],
),
)?;
}
}
}
// 对这批公开入口补 Function 类型身份;不重新分配 callable,ui 属性仍指向原对象。
for name in [
"open",
"text",
"rect",
"spacer",
"row",
"column",
"button",
"page",
"present",
"state",
"textOf",
"stats",
"viewport",
"window",
"isInstance",
"isSubtype",
"implement",
"implements",
"supports",
] {
// 从 ui 取回前面已补完签名的函数;名称表与安装表不一致会在这里报错而非静默漏装。
let function = object(Some(&vm.get_attr(ui, name)?))?;
vm.attach_type(function, function_type)?;
}
// 实例方法安装到对应名义类型;继承复用基类方法,TypeMethod 会验证实际接收者及参数契约。
for (type_name, name, operation) in [
("Block", "bind", Operation::Bind),
("OrbitController", "update", Operation::CameraControl),
("Mesh3D", "translate", Operation::Translate3D),
("Group3D", "translate", Operation::Translate3D),
("Panel3D", "translate", Operation::Translate3D),
("PerspectiveCamera", "translate", Operation::Translate3D),
("OrthographicCamera", "translate", Operation::Translate3D),
("Mesh3D", "rotate", Operation::Rotate3D),
("Group3D", "rotate", Operation::Rotate3D),
("Panel3D", "rotate", Operation::Rotate3D),
("Block", "mounts", Operation::Mounts),
("Block", "invalidate", Operation::Invalidate),
("Block", "onClick", Operation::OnClick),
("Block", "on", Operation::On),
("Block", "dispatch", Operation::Dispatch),
("Block", "onEvent", Operation::OnEvent),
("Block", "onMount", Operation::OnMount),
("Block", "onUnmount", Operation::OnUnmount),
("Block", "click", Operation::Click),
("TextBlock", "setText", Operation::SetText),
("TextBlock", "setColor", Operation::SetColor),
("RectBlock", "setColor", Operation::SetColor),
("ContainerBlock", "append", Operation::Append),
("ContainerBlock", "insert", Operation::Insert),
("ContainerBlock", "remove", Operation::Remove),
("ContainerBlock", "replace", Operation::Replace),
("ContainerBlock", "move", Operation::Move),
("ContainerBlock", "children", Operation::Children),
("Window", "close", Operation::CloseWindow),
("Page", "moveToWindow", Operation::MoveWindow),
("Page", "moveTab", Operation::MoveTab),
("Page", "detachToWindow", Operation::Detach),
("Page", "render", Operation::Render),
("Page", "unmount", Operation::Unmount),
("Page", "open", Operation::Open),
("Page", "window", Operation::PageWindow),
("Page", "close", Operation::Close),
("Page", "activate", Operation::Activate),
("Page", "navigate", Operation::Navigate),
("Page", "back", Operation::Back),
("Page", "forward", Operation::Forward),
] {
// expected 同时用于方法所属类型、receiver 校验和签名中的名义参数身份。
let expected = object(Some(&vm.get_attr(ui, type_name)?))?;
// VM 的方法分派负责绑定 receiver;TypeMethod 再验证它并转交 Function 执行业务操作。
let method = vm.allocate(ContextNativeFunction::new(
name,
TypeMethod {
signature: method_signature(vm.object_store(), desktop, expected, operation)?,
operation,
expected,
session: session.clone(),
},
// 方法 wrapper 捕获所属类型与桌面,保证分发和签名引用在方法仍存活时有效。
vec![expected, desktop],
))?;
roots.push(method);
vm.attach_type(method, function_type)?;
// 写到类型方法表而非每个实例的属性,实例与源子类可共享同一个原生实现。
vm.define_method(expected, name, method)?;
}
// 类型与基础方法就绪后再装窗口、默认视觉、事件方法和能力契约,顺序满足各模块查类型/方法的前提。
crate::window::install(vm, roots, desktop, tabs)?;
// 默认 measure/arrange/paint 安装到 Block 体系;源代码之后可按协议覆盖它们。
let block_type = object(Some(&vm.get_attr(ui, "Block")?))?;
crate::native_visual::install(vm, roots, desktop, block_type, function_type)?;
// 事件方法需要识别 Event 实例;订阅返回值则使用 Subscription 身份管理取消连接。
let event_type = object(Some(&vm.get_attr(ui, "Event")?))?;
// token_type 传给事件安装器,不能拿 Event 类型代替,否则取消令牌会违反返回契约。
let token_type = object(Some(&vm.get_attr(ui, "Subscription")?))?;
crate::event_dispatch::install(vm, roots, event_type, token_type, function_type)?;
crate::protocols::install(vm, roots, desktop, ui)?;
// 每个桥接域配套自己的任务 Scheduler,再接工具入口及托管起始页工厂,最后返回可使用的 UiBridge。
// Scheduler 随 UiBridge 交还运行时;它还未开始执行用户任务,只完成 ui 异步 API 注册。
let tasks = crate::tasks::install(vm, roots, ui, desktop)?;
crate::bridge::graphics::install(vm, roots, ui, desktop)?;
developer_pages::install(vm, ui, &session)?;
// 将语言命名空间、展示会话与任务调度打包,运行时随后通过同一 bridge 推进刷新与回调。
let bridge = UiBridge { ui, session, tasks };
developer_pages::install_start_page(vm, &bridge)?;
Ok(bridge)
}