docs: fill annotation audit gaps in object and type paths
This commit is contained in:
@@ -340,6 +340,7 @@ fn failed_nominal_return_releases_value_and_captured_type_roots() {
|
||||
let expected = vm
|
||||
.new_type("Expected", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 创建与 Expected 同父但身份不同的类型,返回其实例应触发名义返回约束失败。
|
||||
let other = vm
|
||||
.new_type("Other", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
|
||||
@@ -160,6 +160,7 @@ impl SourceFile {
|
||||
impl Drop for SourceFile {
|
||||
/// 退出文件测试作用域时清理文件,忽略已不存在等删除错误,避免 Drop 再次失败。
|
||||
fn drop(&mut self) {
|
||||
// 只尽力删除测试文件,显式忽略删除结果,不在析构中制造第二个错误。
|
||||
let _ = std::fs::remove_file(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +459,7 @@ fn verify(
|
||||
let height = heights[index].unwrap();
|
||||
// 逻辑操作码与完整 u32 参数;前缀已由解码器合并。
|
||||
let Instruction { opcode, argument } = instruction.instruction;
|
||||
// 完整 u32 操作数转成当前平台索引/数量,用于下面栈需求与布局计算。
|
||||
let argument = argument as usize;
|
||||
// required 是最深访问需求,popped/pushed 是净变化来源;Copy/Swap 不弹出其深层项。
|
||||
let (required, popped, pushed) = match opcode {
|
||||
|
||||
@@ -28,6 +28,7 @@ enum Fact {
|
||||
impl Fact {
|
||||
// 把实际值降为抽象身份事实,直接标量不保留其内容。
|
||||
fn value(value: Value) -> Self {
|
||||
// 对象保留借用身份,标量统一变成 Unknown;不把抽象事实当作实际求值结果。
|
||||
match value {
|
||||
Value::Object(object) => Self::Object(object),
|
||||
_ => Self::Unknown,
|
||||
@@ -35,6 +36,7 @@ impl Fact {
|
||||
}
|
||||
// 只从普通对象事实提取身份,不把方法对当作对象。
|
||||
fn object(self) -> Option<ObjectRef> {
|
||||
// 仅 Object 事实可用于属性接收者或调用身份,Unknown/Null/Method 都不冒充普通对象。
|
||||
match self {
|
||||
Self::Object(object) => Some(object),
|
||||
_ => None,
|
||||
|
||||
@@ -118,6 +118,7 @@ pub enum ReadDependency {
|
||||
impl ReadDependency {
|
||||
/// 缓存/读取分析使用者取得观察所属对象,不创建外部拥有根。
|
||||
pub fn object(&self) -> ObjectRef {
|
||||
// 三种依赖形状都提取其观察对象身份,不重新读取内容或改变依赖精度。
|
||||
match self {
|
||||
Self::Object { object, .. }
|
||||
| Self::DictionaryKind { object }
|
||||
@@ -311,6 +312,7 @@ impl Heap {
|
||||
))?;
|
||||
// 锁的取得顺序与结束路径一致:先分类区间,再精确依赖区间。
|
||||
let mut regions = scope.regions.lock().expect("read tracker poisoned");
|
||||
// 与分类区间相同锁顺序取得精确依赖区间栈,令牌检查必须同时核对两者。
|
||||
let mut precise = scope
|
||||
.dependency_regions
|
||||
.lock()
|
||||
@@ -564,6 +566,7 @@ impl Heap {
|
||||
let object = self.get_mut(reference)?;
|
||||
// 暂存旧边/旧载荷,等新状态与通知建立后再释放旧状态。
|
||||
let old_edges = std::mem::replace(&mut object.payload_edges, edges);
|
||||
// 旧载荷也要暂存到变更通知及旧引用边释放之后再析构,保持状态提交顺序。
|
||||
let old_payload = std::mem::replace(&mut object.payload, Box::new(payload));
|
||||
self.record_change(reference, ChangeKey::Contents);
|
||||
self.release_edges(old_edges);
|
||||
@@ -966,6 +969,7 @@ mod tests {
|
||||
let mut heap = Heap::new().unwrap();
|
||||
// owner 接收属性边,target 模拟无法再增加强计数的对象。
|
||||
let owner = heap.allocate(Box::new(Record)).unwrap();
|
||||
// 另建目标对象,随后将强计数推至上限,测试 retain 失败不能修改外部根或 owner 的边。
|
||||
let target = heap.allocate(Box::new(Record)).unwrap();
|
||||
// 仅改强计数,外部根仍为一;后面的断言检查失败是否意外改动 external_refs。
|
||||
heap.get_mut(target).unwrap().header.strong_refs = usize::MAX;
|
||||
|
||||
@@ -171,6 +171,7 @@ impl Instruction {
|
||||
pub const fn compare(operation: CompareOp) -> Self {
|
||||
Self::new(
|
||||
Opcode::CompareOp,
|
||||
// 将六种比较映射为稳定字节码编号,必须与 eval 的 CompareOp 解码一致。
|
||||
match operation {
|
||||
CompareOp::Lt => 0,
|
||||
CompareOp::Le => 1,
|
||||
|
||||
@@ -90,6 +90,7 @@ pub enum CallType {
|
||||
Union(Vec<crate::AnnotationType>),
|
||||
}
|
||||
impl From<crate::AnnotationType> for CallType {
|
||||
/// 宿主用单个 AnnotationType 构建原生签名时由 From/Into 包成 Single;这里不新增类型边或校验堆身份。
|
||||
fn from(value: crate::AnnotationType) -> Self {
|
||||
Self::Single(value)
|
||||
}
|
||||
@@ -479,6 +480,7 @@ impl<'a> NativeContext<'a> {
|
||||
context.protect(&Value::Object(function))?;
|
||||
// 可选完整签名存在时才校验/保护;缺失元数据保留既有调用协议。
|
||||
if let Some(signature) = &signature {
|
||||
// 逐项保护签名持有的类型/能力定义,使回调执行期间不依赖宿主另外保留根。
|
||||
for reference in signature.references() {
|
||||
context.protect(&Value::Object(reference))?;
|
||||
}
|
||||
@@ -603,6 +605,7 @@ impl ObjectPayload for ContextNativeFunction {
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
// 签名中的参数、可变参数和返回约束定义也属于载荷拥有边。
|
||||
if let Some(signature) = &self.signature {
|
||||
// 按签名实际引用报告每条拥有边,包括重复出现的定义,不因某个联合候选已匹配而省略。
|
||||
for reference in signature.references() {
|
||||
visit(reference);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ impl EndorInteger {
|
||||
pub fn to_i64(&self) -> Option<i64> {
|
||||
// 先读 u64 幅值以容纳 i64::MIN 的绝对值,再检查有符号范围。
|
||||
let magnitude = self.digits.parse::<u64>().ok()?;
|
||||
// 根据符号选择有符号范围检查,负数还需容纳 i64::MIN 的额外一单位幅值。
|
||||
if self.negative {
|
||||
// 最小负数的幅值比最大正数多一,必须单独处理。
|
||||
if magnitude == (i64::MAX as u64) + 1 {
|
||||
@@ -70,6 +71,7 @@ impl EndorInteger {
|
||||
impl fmt::Display for EndorInteger {
|
||||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// 仅负值输出前导减号,数字幅值随后原样写出,不通过浮点格式化。
|
||||
if self.negative {
|
||||
f.write_str("-")?;
|
||||
}
|
||||
@@ -104,10 +106,13 @@ impl EndorDecimal {
|
||||
}
|
||||
// 分别记录已有小数点、数字数量与尾数是否全零。
|
||||
let mut decimal_point = false;
|
||||
// 统计尾数数字个数,允许前导零,但至少要有一个数字。
|
||||
let mut digits = 0_usize;
|
||||
// 初始假设尾数全零,每读到一个非零数字便保持为 false,用于零值语义。
|
||||
let mut zero = true;
|
||||
// 按 ASCII 字节扫描,第二个点或其他字符都拒绝。
|
||||
for byte in mantissa.bytes() {
|
||||
// 数字更新计数及全零状态,首个点只登记位置,第二个点或其他字节明确失败。
|
||||
match byte {
|
||||
b'.' if !decimal_point => decimal_point = true,
|
||||
b'0'..=b'9' => {
|
||||
@@ -209,6 +214,7 @@ pub enum EndorReal {
|
||||
impl EndorReal {
|
||||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||||
pub fn is_zero(&self) -> bool {
|
||||
// 按实际实数表示判断零;分数看分子,二进制浮点按其原有 f64 零语义判断。
|
||||
match self {
|
||||
Self::Integer(value) => value.is_zero(),
|
||||
Self::Decimal(value) => value.is_zero(),
|
||||
@@ -221,6 +227,7 @@ impl EndorReal {
|
||||
impl fmt::Display for EndorReal {
|
||||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// 各精确表示委托自己的 Display;BinaryFloat 才按存储的浮点位模式显示。
|
||||
match self {
|
||||
Self::Integer(value) => value.fmt(f),
|
||||
Self::Decimal(value) => value.fmt(f),
|
||||
@@ -275,6 +282,7 @@ impl EndorNumber {
|
||||
|
||||
/// VM 算术/转换查询实数,Complex 即使虚部为零也返回 None。
|
||||
pub fn as_real(&self) -> Option<&EndorReal> {
|
||||
// 只有 Real 能借用实数部分;Complex 即使虚部为零也不在此自动降级。
|
||||
match self {
|
||||
Self::Real(value) => Some(value),
|
||||
Self::Complex { .. } => None,
|
||||
@@ -283,6 +291,7 @@ impl EndorNumber {
|
||||
|
||||
/// VM 快路径仅接受整数变体,不自动转小数或可整除分数。
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
// 仅整数实数尝试收窄为 i64,小数、分数、浮点和复数均不隐式转换。
|
||||
match self {
|
||||
Self::Real(EndorReal::Integer(value)) => value.to_i64(),
|
||||
_ => None,
|
||||
@@ -291,6 +300,7 @@ impl EndorNumber {
|
||||
|
||||
/// 数值真值/分母检查使用的零判断,具体实现按表示处理。
|
||||
pub fn is_zero(&self) -> bool {
|
||||
// 实数委托自身零判断;复数要求实部和虚部都为零。
|
||||
match self {
|
||||
Self::Real(value) => value.is_zero(),
|
||||
Self::Complex { real, imaginary } => real.is_zero() && imaginary.is_zero(),
|
||||
@@ -315,6 +325,7 @@ impl From<f64> for EndorNumber {
|
||||
impl fmt::Display for EndorNumber {
|
||||
/// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// 实数保持原表示,复数分别显示两个实数部分和显式虚数单位。
|
||||
match self {
|
||||
Self::Real(value) => value.fmt(f),
|
||||
Self::Complex { real, imaginary } => write!(f, "({real} + ({imaginary})i)"),
|
||||
@@ -412,6 +423,7 @@ pub enum ArithmeticOp {
|
||||
impl ArithmeticOp {
|
||||
/// eval 解 BinaryArithmetic 的 0..3 运算编号,其他值返回 None。
|
||||
pub fn from_argument(value: u32) -> Option<Self> {
|
||||
// 只接受字节码定义的四个算术编号,其他值返回 None 交调用方拒绝。
|
||||
match value {
|
||||
0 => Some(Self::Add),
|
||||
1 => Some(Self::Subtract),
|
||||
@@ -617,6 +629,7 @@ fn exact_real(value: &EndorReal) -> Result<num_rational::BigRational, VmError> {
|
||||
s.parse()
|
||||
.map_err(|_| VmError::InvalidArgument("invalid exact number"))
|
||||
};
|
||||
// 不同实数表示转成运算用精确有理数;浮点必须有限,十进制另检查指数运算预算。
|
||||
match value {
|
||||
EndorReal::Integer(n) => Ok(BigRational::from_integer(integer(&n.to_string())?)),
|
||||
EndorReal::Rational(n) => Ok(BigRational::new(
|
||||
|
||||
@@ -196,6 +196,7 @@ impl ObjectStore {
|
||||
}
|
||||
// 旧式宿主原地赋值在调用前检查写屏障;实现不得悄悄改变 trace 中的托管边。
|
||||
self.heap.prepare_change(target)?;
|
||||
// 调用宿主原地赋值协议,返回是否确实改变;只有 true 才发布 Contents 变更。
|
||||
let changed = self
|
||||
.object(target)?
|
||||
.payload()
|
||||
@@ -295,7 +296,9 @@ impl ObjectStore {
|
||||
pub fn attribute_path(&self, reference: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
|
||||
// path 保存访问顺序,seen 检测转发环,current 是本轮需要验证并展开的对象。
|
||||
let mut path = Vec::new();
|
||||
// 记录已经展开的对象身份,转发环在再次遇到同一对象时失败。
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
// 从原始门面开始逐跳追踪 attribute_target,最终返回整个存储路径。
|
||||
let mut current = reference;
|
||||
// 每次至少加入一个对象;遇到无转发目标才正常结束,因此成功路径一定非空。
|
||||
loop {
|
||||
@@ -364,6 +367,7 @@ impl ObjectStore {
|
||||
) -> Result<bool, VmError> {
|
||||
// 借用双方载荷;这条底层协议路径不通过 object,因此不自动收集对象版本依赖。
|
||||
let left_payload = self.heap.get(left)?.payload();
|
||||
// 借用右侧载荷,左侧不支持时才反向比较;此底层访问不自动登记内容依赖。
|
||||
let right_payload = self.heap.get(right)?.payload();
|
||||
// 左侧能处理就立即返回;Some(false) 也是已实现结果,不能继续尝试右侧。
|
||||
if let Some(result) = left_payload.compare(right_payload, operation)? {
|
||||
|
||||
@@ -188,6 +188,7 @@ impl ObjectStore {
|
||||
self.key_hash(&key)?;
|
||||
// 对象值必须来自本堆且仍有效;标量无需额外句柄检查。
|
||||
self.validate_value(&value)?;
|
||||
// 先找相等键再决定覆盖或追加,保证保留首个键对象及插入顺序。
|
||||
if let Some(index) = self.dict_position(&dict, &key)? {
|
||||
// 相等键只覆盖值,保留第一次插入的键对象及其顺序,不保存本次传入的替代键。
|
||||
dict.entries[index].1 = value;
|
||||
@@ -241,6 +242,7 @@ impl ObjectStore {
|
||||
self.validate_value(value)?;
|
||||
// 直接值使用标准哈希器;对象委托各自实现,可由载荷拒绝不可哈希种类。
|
||||
let mut hasher = DefaultHasher::new();
|
||||
// 标量按已定义类别散列;对象委托载荷哈希,尚无数值哈希协议时拒绝 Number。
|
||||
match value {
|
||||
Value::Nil => "nil".hash(&mut hasher),
|
||||
Value::Bool(value) => value.hash(&mut hasher),
|
||||
|
||||
@@ -522,6 +522,7 @@ impl ObjectStore {
|
||||
let mut current = Some(type_object);
|
||||
// 基类固定于创建,逐层校验类型句柄而不是按名称检索。
|
||||
while let Some(type_object) = current {
|
||||
// 本层必须是有效 Type 载荷,随后只在该层方法表查找名称,未命中才沿固定基类继续。
|
||||
let definition = self.payload::<EndorTypeObject>(type_object, "Type")?;
|
||||
// 找到方法即返回借用身份,不替调用方 retain。
|
||||
if let Some(function) = definition.methods.get(name) {
|
||||
@@ -1075,10 +1076,11 @@ impl ObjectStore {
|
||||
/// 方法/能力更新前扫描存活类型,返回包含 base 自身的全部子类型借用身份。
|
||||
fn descendant_types(&self, base: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
|
||||
self.payload::<EndorTypeObject>(base, "Type")?;
|
||||
// 去重能力身份,不建立堆外拥有注册表或延长能力寿命。
|
||||
// 收集受更新影响的类型借用身份;不创建后代拥有索引,也不延长类型寿命。
|
||||
let mut found = vec![];
|
||||
// 扫描当前存活对象;只选类型载荷及其名义子类型。
|
||||
for reference in self.heap.live_references() {
|
||||
// 只收集仍存活的 Type 对象及 base 的名义后代,普通实例和无关类型不参与更新检查。
|
||||
if self
|
||||
.object(reference)?
|
||||
.payload()
|
||||
|
||||
@@ -133,6 +133,7 @@ fn read_only_scopes_block_existing_container_and_type_writes_but_allow_fresh_res
|
||||
let record = store.new_object().unwrap();
|
||||
// 已有字典受同一只读屏障保护,不只保护普通属性。
|
||||
let dict = store.new_dict().unwrap();
|
||||
// 进入只读前创建的 Cell 同样属于受保护堆对象,不能靠内部值更新绕过写屏障。
|
||||
let cell = store
|
||||
.new_cell(Some(objects::Value::Number(1_i64.into())))
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user