diff --git a/crates/compiler/src/README.md b/crates/compiler/src/README.md index 66acd2a..aa57c50 100644 --- a/crates/compiler/src/README.md +++ b/crates/compiler/src/README.md @@ -7,3 +7,5 @@ | [codegen.rs](codegen.rs) | AST 到函数与类创建指令、嵌套代码模板与源码签名保留、EndorCode 验证、显式 VM 装载与临时根释放 | 节点定义在 frontend/Endor.asdl;这里匹配生成的 AST,不复制节点类型。源码和调用方提供的 AST 都先分析后生成,不执行 AST。修改名称语义、代码布局或所有权时同步 [编译契约](../../../docs/runtime/compiler.md)、[检查](../tests/README.md) 与 [示例](../examples/README.md)。返回 [compiler](../README.md)。 + +analysis.rs遍历Await操作数,codegen.rs发出Await字节码;运行时等待交给VM及宿主。 diff --git a/crates/core/tests/README.md b/crates/core/tests/README.md index ed16568..fe33e7b 100644 --- a/crates/core/tests/README.md +++ b/crates/core/tests/README.md @@ -17,3 +17,5 @@ execution.rs 同时覆盖宿主可选 assign_from 的全局/局部赋值、别名身份、拒绝替换与执行根清理;不在本地运行。 execution.rs 的 managed_coroutine 用例覆盖交替恢复不重放写入、累计预算错误释放、取消幂等和暂停帧引用环回收;本地只经宿主Clippy编译,不运行测试。 + +execution.rs补AwaitGate,覆盖真实Waiting、前缀写入不重放、等待操作数保活及拥有式对象结果;本地仅编译不执行。 diff --git a/crates/core/tests/execution.rs b/crates/core/tests/execution.rs index 017050d..c887fe7 100644 --- a/crates/core/tests/execution.rs +++ b/crates/core/tests/execution.rs @@ -1622,3 +1622,93 @@ fn managed_coroutine_hard_budget_accumulates_and_error_releases_frames() { } assert_eq!(vm.object_count(), 0); } + +#[derive(Debug)] +struct AwaitGate { + ready: Arc, + value: Value, +} +impl ObjectPayload for AwaitGate { + fn as_any(&self) -> &dyn Any { + self + } + fn kind_name(&self) -> &'static str { + "await-gate" + } + fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { + if let Value::Object(reference) = self.value { + visit(reference); + } + } + fn poll_await(&self) -> Result { + Ok(if self.ready.load(Ordering::Acquire) { + endor_core::AwaitPoll::Ready(self.value.clone()) + } else { + endor_core::AwaitPoll::Pending + }) + } +} + +#[test] +fn await_keeps_operand_and_owned_object_result_without_replaying_prefix() { + use endor_core::CoroutineProgress; + let mut vm = Vm::new().unwrap(); + let ready = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let value = vm.new_object().unwrap(); + vm.set_attr(value, "marker", text("kept")).unwrap(); + let gate = vm + .allocate(AwaitGate { + ready: ready.clone(), + value: Value::Object(value), + }) + .unwrap(); + let globals = vm.new_dict().unwrap(); + let code = make_code( + &mut vm, + &[ + op(Opcode::LoadConst, 0), + op(Opcode::StoreGlobal, 0), + op(Opcode::LoadConst, 1), + simple(Opcode::Await), + simple(Opcode::ReturnValue), + ], + vec![text("prefix"), Value::Object(gate)], + CodeLayout { + names: vec!["step".into()], + ..CodeLayout::default() + }, + ); + let function = vm + .new_function("awaited", code, globals, vec![], vec![]) + .unwrap(); + let coroutine = vm + .new_coroutine(function, &[], ExecutionLimits::default()) + .unwrap(); + assert!( + matches!(vm.resume_coroutine(coroutine, 64).unwrap(), CoroutineProgress::Waiting { awaitable, .. } if awaitable == gate) + ); + vm.release(value).unwrap(); + vm.release(gate).unwrap(); + vm.dict_set(globals, text("step"), text("interleaved")) + .unwrap(); + vm.collect_cycles(); + ready.store(true, Ordering::Release); + let CoroutineProgress::Complete { value: result, .. } = + vm.resume_coroutine(coroutine, 64).unwrap() + else { + panic!("ready await must complete") + }; + assert_eq!( + vm.dict_get(globals, &text("step")).unwrap(), + Some(text("interleaved")) + ); + for object in [coroutine, function, code, globals] { + vm.release(object).unwrap(); + } + let Value::Object(reference) = result else { + panic!("owned object result") + }; + assert_eq!(vm.get_attr(reference, "marker").unwrap(), text("kept")); + vm.release_value(result).unwrap(); + assert_eq!(vm.object_count(), 0); +} diff --git a/crates/frontend/src/README.md b/crates/frontend/src/README.md index 961409c..446c228 100644 --- a/crates/frontend/src/README.md +++ b/crates/frontend/src/README.md @@ -17,3 +17,5 @@ 函数体允许独立方法调用等表达式语句;末尾 return 可省略,已有编译器隐式返回 Nil。 改节点先编辑 ../Endor.asdl,再用 scripts/generate_ast.py 更新 Rust/编辑器产物,契约见 [AST 模型](../../../docs/runtime/ast.md)。改输入适配时同步 [阅读器契约](../../../docs/runtime/reader.md) 与读取错误检查;改词法从 lexer.peg 进入,并同步 token、诊断和错误检查;改语法从 grammar.peg 进入,并同步 AST、[测试](../tests/README.md)、样例与 [前端契约](../../../docs/runtime/frontend.md)。词法与语法两份 PEG 动作均不做 VM 分配、I/O 或修改外部状态,避免回溯造成副作用。返回 [frontend](../README.md)。 + +grammar.peg支持await前缀表达式;AST仍从Endor.asdl生成,不能手改generated.rs/visitor.rs/editor产物。 diff --git a/crates/objects/src/README.md b/crates/objects/src/README.md index 5f5ead2..a46942c 100644 --- a/crates/objects/src/README.md +++ b/crates/objects/src/README.md @@ -23,3 +23,5 @@ 对象操作变更同步 [对象模型](../../../docs/runtime/objects.md),内置载荷同步 [基本对象](../../../docs/runtime/builtins.md)。新增操作码需要同时修改本包 instruction.rs/code.rs 和 core 的 eval.rs 分派,见 [字节码 VM](../../../docs/runtime/bytecode.md)。 Heap、ObjectRef 字段、对象头计数和可变容器字段不向包外开放;公开代码读取接口不允许改写已登记的常量和托管边。检查入口见 [tests](../tests/README.md)。返回 [objects](../README.md)。 + +object.rs提供AwaitPoll与ObjectPayload::poll_await;instruction.rs/code.rs验证Await操作数/栈效果,effects.rs将其视为不能证明只读,error.rs承载等待失败/取消。契约见docs/runtime/bytecode.md。 diff --git a/docs/README.md b/docs/README.md index 60eb54d..f99239c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,3 +22,5 @@ | 后续开发交接 | [交接](contributing/handoff.md) | 实时源码和 Git 状态 | 目录入口:[architecture](architecture/README.md)、[runtime](runtime/README.md)、[operations](operations/README.md)、[contributing](contributing/README.md)。宿主部署、文件信息和数据库正文已移至 Hestia,命令行与实际输出报告已移至 Lyra;本仓库不保留重复正文。研究资料仍在忽略的 work/research,不提交。 + +Await入口:语法见runtime/frontend.md,名称与发码见runtime/compiler.md,等待/恢复与所有权见runtime/bytecode.md,宿主协议见runtime/objects.md。 diff --git a/docs/runtime/bytecode.md b/docs/runtime/bytecode.md index 3fd2bad..374a2ea 100644 --- a/docs/runtime/bytecode.md +++ b/docs/runtime/bytecode.md @@ -195,3 +195,10 @@ LoadAttr 对实例自有属性返回原值;缺失时沿类型父链找方法 与只读续算不同,每个 coroutine 把自己的帧保存在同一 VM 的托管对象载荷中,多个调用可独立暂停;Pending 之间可执行普通调用和堆写入,没有跨量子的只读作用域。resume 不重放先前指令,局部槽、栈、返回类型及原生委托契约保持,所有量子共用原始硬指令/帧预算。原生调用仍是原子的,slice 不保证墙钟延迟。读写共享堆,无快照、事务或自动回滚。不能在只读执行/只读暂停期间创建、恢复或取消 coroutine。 暂停时,帧内原本的执行根转为载荷 trace 的托管边(包括重复边);恢复前先保活为执行根,再清空暂停载荷。函数边继续拥有 code/globals/builtins。运行中的 handle 临时保活,不能恢复或取消 Running/已结束帧;完成和错误均消耗帧,错误保留 traceback。`cancel_coroutine(handle)` 对尚未结束帧返回 true 并释放托管边,已结束返回 false;已发生的写入不回滚。调用方最终 release handle 和完成结果。丢弃未完成且不可达的 coroutine 不要求先 cancel,局部/全局指回 coroutine 的环可由正常循环回收处理,不把暂停帧永久登记为 VM 根。调度、任务归属、等待条件及取消策略由宿主实现,网络/GUI不进入内核。 + + +## Await 等待边界 + +`Await`(opcode 30,无参数)把栈顶 awaitable 换成结果,验证栈效果为1入1出。`ObjectPayload::poll_await()` 是非阻塞、无副作用的宿主协议,默认不支持;返回 Pending 或 Ready(借用 Value),也可返回 AwaitFailed(String)/AwaitCancelled。引擎验证 Ready 句柄并先retain结果,再释放原操作数;不可信宿主仍需遵守借用与非阻塞契约。Await 不被保守效果分析证明为只读,运行时也拒绝只读阶段。 + +在独立coroutine中,Pending保持已求值的栈顶、当前帧和返回契约,返回CoroutineProgress::Waiting { awaitable, instructions };awaitable是由暂停帧保活的借用身份。宿主等待条件满足后再次resume,仅重查同一个Await,不重放操作数表达式、前缀写入或IO请求。每次实际执行Await计入同一硬预算;宿主不应对未就绪对象反复resume来轮询。完成结果与错误沿普通执行/清理路径传播。非coroutine调用允许立即Ready的await,Pending则明确报错,不阻塞线程。调度、取消传播和循环等待检测属于宿主;本引擎不依赖Tessera Task。 diff --git a/docs/runtime/compiler.md b/docs/runtime/compiler.md index 85e59d5..ecea6f3 100644 --- a/docs/runtime/compiler.md +++ b/docs/runtime/compiler.md @@ -58,3 +58,6 @@ lyra run [--entry name] 编译模块、执行初始化、调用无参 类方法使用显式首参数 self;编译器检查本类重复字段/方法和缺少 self。方法代码仍是共享的普通代码对象,参数/返回注解和声明类名随代码保存,由宿主开关选择运行时检查。BuildClass 在 VM 中检查继承字段冲突和原生分配边界;构造失败不发布类型到目标全局名。默认基类 Object 须由宿主通过 CompileOptions 声明并在运行全局中提供,编译器不自行引导类型。普通类构造和继承分派见[类型机制](types.md)。 + + +Await表达式参与普通名称/初始化分析,先编译operand,再发出无参数Await opcode。该链路保存实际结果的栈位置,不通过回放函数实现等待;没有新增async声明或静态awaitable类型检查。语法与运行上下文见frontend及bytecode主题。 diff --git a/docs/runtime/frontend.md b/docs/runtime/frontend.md index f4516d5..265ed3e 100644 --- a/docs/runtime/frontend.md +++ b/docs/runtime/frontend.md @@ -82,3 +82,6 @@ use、namespace、trait、agent、完整类型语法、条件/循环、比较 源码类实现子集采用 `class Name: Base { field: TypeName function method(self: Name): ResultType { ... } }`;基类可省略,执行环境须提供 Object。成员函数沿用既有 function 语法,接收者 self 为显式首参数。普通类字段按基类字段、再本类字段的顺序接收位置构造实参。这里记录实现范围,不确认语言文档中的全部候选构造语法;详细限制见[名义类型](types.md)。 + + +`await expression` 解析为 ExprKind::Await { operand },优先级与前缀负号同级,成员/调用绑定更紧;例如await task.result()等待该调用的返回对象,(await task).field先等待task。await按表达式前缀位置识别,不引入async函数声明。执行是否允许等待由调用上下文决定,见字节码VM的Await章节。 diff --git a/docs/runtime/objects.md b/docs/runtime/objects.md index dccda9e..952cc4c 100644 --- a/docs/runtime/objects.md +++ b/docs/runtime/objects.md @@ -105,3 +105,6 @@ compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。 尚未实现:Python type 兼容、trait 和描述符、任意 VM 重入调用上下文、自动/分代/增量 GC、语言终结器/复活、完整资源限额和 VM 快照;独立 frontend 已提供阅读器、lexer/parser 与 AST,基础源码执行由独立 [Compiler](compiler.md) 接入,见 [源码前端](frontend.md);执行器已有帧数和指令数限额。数据库仅为未来特殊类提供按需连接,普通对象或宿主基础信息不自动持久化,详见 [数据库特殊类](https://gitea.entroia.com/Zayden-Jung/endorphin-hestia-server/src/branch/main/docs/runtime/database-objects.md)。 验证范围与执行约定见 [验证](../operations/validation.md)。 + + +ObjectPayload::poll_await是通用非阻塞等待观察协议,默认UnsupportedOperation。AwaitPoll::Ready的对象结果是借用,调用方须在放弃awaitable前retain;协议不允许内部阻塞/IO/修改对象。完整指令和恢复契约见bytecode.md。