451 lines
18 KiB
Rust
451 lines
18 KiB
Rust
//! 安装一次 VM 域需要的宿主类型、对象和方法。类型注册表是源代码调用原生能力的入口。
|
|
//! 先建立类型与桌面基础设施,再绑定构造器、视觉协议和开发工具工厂;返回的根负责保持这些对象存活。
|
|
//!
|
|
//! Allocate and connect the managed desktop, type tables and host globals.
|
|
use super::*;
|
|
|
|
/// 把本模块的类型、方法或能力注册到所属运行时上下文。
|
|
pub fn install(vm: &mut Vm, roots: &mut Vec<ObjectRef>) -> Result<UiBridge> {
|
|
let desktop = vm.new_object()?;
|
|
roots.push(desktop);
|
|
vm.set_attr(desktop, "viewport", Value::Nil)?;
|
|
let tabs = vm.new_dict()?;
|
|
roots.push(tabs);
|
|
vm.set_attr(desktop, "tabs", Value::Object(tabs))?;
|
|
let ui = vm.new_object()?;
|
|
roots.push(ui);
|
|
let session = Arc::new(Mutex::new(Session {
|
|
nodes: Vec::new(),
|
|
presentations: std::collections::BTreeMap::from([(
|
|
crate::window::MAIN_WINDOW,
|
|
crate::presentation::Presentation::new(desktop, tabs),
|
|
)]),
|
|
current: crate::window::MAIN_WINDOW,
|
|
master: desktop,
|
|
next_window: crate::window::MAIN_WINDOW,
|
|
next_mount: 0,
|
|
retired_commands: Vec::new(),
|
|
refresh_yields: 0,
|
|
refresh_restarts: 0,
|
|
layout_cancels: 0,
|
|
paint_cancels: 0,
|
|
paint_slices: 0,
|
|
binding_evaluations: 0,
|
|
exiting: false,
|
|
}));
|
|
let mut public_functions = Vec::new();
|
|
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),
|
|
] {
|
|
let function = vm.allocate(ContextNativeFunction::new(
|
|
name,
|
|
Function {
|
|
signature: None,
|
|
operation,
|
|
owner: None,
|
|
session: session.clone(),
|
|
requested_type: None,
|
|
},
|
|
vec![desktop],
|
|
))?;
|
|
roots.push(function);
|
|
vm.set_attr(ui, name, Value::Object(function))?;
|
|
public_functions.push((name, function, operation));
|
|
}
|
|
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))?;
|
|
let mut definitions = vec![
|
|
("Object", root_types.object),
|
|
("Type", root_types.type_type),
|
|
];
|
|
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")),
|
|
] {
|
|
let base = object(Some(&vm.get_attr(ui, base_name)?))?;
|
|
let constructor = match constructor_name {
|
|
Some("object-list") => {
|
|
let factory = vm.allocate(ContextNativeFunction::new(
|
|
"object-list",
|
|
crate::object_list::Constructor {
|
|
desktop,
|
|
signature: None,
|
|
},
|
|
vec![desktop],
|
|
))?;
|
|
roots.push(factory);
|
|
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
|
|
vm.attach_type(factory, function_type)?;
|
|
TypeConstructor::NativeTyped(factory)
|
|
}
|
|
Some("event") => {
|
|
let factory = vm.allocate(ContextNativeFunction::new(
|
|
"event",
|
|
crate::event_dispatch::Constructor { signature: None },
|
|
vec![],
|
|
))?;
|
|
roots.push(factory);
|
|
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
|
|
vm.attach_type(factory, function_type)?;
|
|
TypeConstructor::NativeTyped(factory)
|
|
}
|
|
Some(constructor_name) => {
|
|
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"))?,
|
|
};
|
|
let factory = vm.allocate(ContextNativeFunction::new(
|
|
constructor_name,
|
|
NativeFactory {
|
|
signature: None,
|
|
options: None,
|
|
operation,
|
|
session: session.clone(),
|
|
},
|
|
vec![desktop],
|
|
))?;
|
|
roots.push(factory);
|
|
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
|
|
vm.attach_type(factory, function_type)?;
|
|
TypeConstructor::NativeTyped(factory)
|
|
}
|
|
None => TypeConstructor::Abstract,
|
|
};
|
|
if let (Some(tag), TypeConstructor::NativeTyped(factory)) = (constructor_name, constructor)
|
|
{
|
|
factories.push((tag, factory));
|
|
}
|
|
let definition = vm.new_type(name, base, constructor)?;
|
|
roots.push(definition);
|
|
vm.set_attr(ui, name, Value::Object(definition))?;
|
|
definitions.push((name, definition));
|
|
}
|
|
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![]),
|
|
] {
|
|
let fields = fields
|
|
.into_iter()
|
|
.map(|name| endor_core::ClassField {
|
|
name: name.into(),
|
|
type_name: if matches!(name, "children" | "commands") {
|
|
"Object"
|
|
} else {
|
|
"Number"
|
|
}
|
|
.into(),
|
|
})
|
|
.collect();
|
|
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.
|
|
let types = vm.new_dict()?;
|
|
roots.push(types);
|
|
for (name, definition) in definitions {
|
|
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)?;
|
|
let function_type = object(Some(&vm.get_attr(ui, "Function")?))?;
|
|
vm.set_function_type(function_type)?;
|
|
vm.set_source_type_checks(true)?;
|
|
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 {
|
|
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),
|
|
"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 {
|
|
let options = operation_signature(vm.object_store(), desktop, operation)?;
|
|
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,
|
|
};
|
|
let mut captures = vec![desktop];
|
|
captures.extend(options.references());
|
|
vm.replace_payload(
|
|
function,
|
|
ContextNativeFunction::new(
|
|
name,
|
|
NativeFactory {
|
|
signature: Some(envelope),
|
|
options: Some(options),
|
|
operation,
|
|
session: session.clone(),
|
|
},
|
|
captures,
|
|
),
|
|
)?;
|
|
} else {
|
|
let kind = if name == "event" {
|
|
"Event"
|
|
} else {
|
|
"ObjectList"
|
|
};
|
|
let signature = CallSignature {
|
|
arity: endor_core::CallArity::range(
|
|
if name == "event" { 2 } else { 1 },
|
|
usize::MAX,
|
|
),
|
|
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" {
|
|
vm.replace_payload(
|
|
function,
|
|
ContextNativeFunction::new(
|
|
name,
|
|
crate::event_dispatch::Constructor {
|
|
signature: Some(signature),
|
|
},
|
|
vec![],
|
|
),
|
|
)?;
|
|
} else {
|
|
vm.replace_payload(
|
|
function,
|
|
ContextNativeFunction::new(
|
|
name,
|
|
crate::object_list::Constructor {
|
|
desktop,
|
|
signature: Some(signature),
|
|
},
|
|
vec![desktop],
|
|
),
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
for name in [
|
|
"open",
|
|
"text",
|
|
"rect",
|
|
"spacer",
|
|
"row",
|
|
"column",
|
|
"button",
|
|
"page",
|
|
"present",
|
|
"state",
|
|
"textOf",
|
|
"stats",
|
|
"viewport",
|
|
"window",
|
|
"isInstance",
|
|
"isSubtype",
|
|
"implement",
|
|
"implements",
|
|
"supports",
|
|
] {
|
|
let function = object(Some(&vm.get_attr(ui, name)?))?;
|
|
vm.attach_type(function, function_type)?;
|
|
}
|
|
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),
|
|
] {
|
|
let expected = object(Some(&vm.get_attr(ui, type_name)?))?;
|
|
let method = vm.allocate(ContextNativeFunction::new(
|
|
name,
|
|
TypeMethod {
|
|
signature: method_signature(vm.object_store(), desktop, expected, operation)?,
|
|
operation,
|
|
expected,
|
|
session: session.clone(),
|
|
},
|
|
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)?;
|
|
let block_type = object(Some(&vm.get_attr(ui, "Block")?))?;
|
|
crate::native_visual::install(vm, roots, desktop, block_type, function_type)?;
|
|
let event_type = object(Some(&vm.get_attr(ui, "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)?;
|
|
let tasks = crate::tasks::install(vm, roots, ui, desktop)?;
|
|
developer_pages::install(vm, ui, &session)?;
|
|
let bridge = UiBridge { ui, session, tasks };
|
|
developer_pages::install_start_page(vm, &bridge)?;
|
|
Ok(bridge)
|
|
}
|