pr 2.3
This commit is contained in:
parent
96062bf1d0
commit
df9b248c98
@ -237,6 +237,44 @@ pub enum LoadError {
|
||||
},
|
||||
}
|
||||
|
||||
/// Load-time error for PBX-declared syscall resolution.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DeclaredLoadError {
|
||||
/// The `(module, name, version)` triple is not known by the host.
|
||||
UnknownSyscall {
|
||||
module: String,
|
||||
name: String,
|
||||
version: u16,
|
||||
},
|
||||
/// The cartridge lacks required capabilities for the syscall.
|
||||
MissingCapability {
|
||||
required: CapFlags,
|
||||
provided: CapFlags,
|
||||
module: String,
|
||||
name: String,
|
||||
version: u16,
|
||||
},
|
||||
/// The PBX-declared ABI does not match the authoritative host metadata.
|
||||
AbiMismatch {
|
||||
module: String,
|
||||
name: String,
|
||||
version: u16,
|
||||
declared_arg_slots: u16,
|
||||
declared_ret_slots: u16,
|
||||
expected_arg_slots: u16,
|
||||
expected_ret_slots: u16,
|
||||
},
|
||||
}
|
||||
|
||||
fn resolve_syscall_impl(module: &str, name: &str, version: u16) -> Option<SyscallResolved> {
|
||||
for entry in SYSCALL_TABLE {
|
||||
if entry.meta.module == module && entry.meta.name == name && entry.meta.version == version {
|
||||
return Some(SyscallResolved { id: entry.meta.id, meta: entry.meta });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Resolve a canonical syscall identity to its numeric id and metadata.
|
||||
///
|
||||
/// Returns `None` if the identity is unknown.
|
||||
@ -245,12 +283,7 @@ pub fn resolve_syscall(
|
||||
name: &'static str,
|
||||
version: u16,
|
||||
) -> Option<SyscallResolved> {
|
||||
for entry in SYSCALL_TABLE {
|
||||
if entry.meta.module == module && entry.meta.name == name && entry.meta.version == version {
|
||||
return Some(SyscallResolved { id: entry.meta.id, meta: entry.meta });
|
||||
}
|
||||
}
|
||||
None
|
||||
resolve_syscall_impl(module, name, version)
|
||||
}
|
||||
|
||||
/// Resolve all declared program syscalls and enforce capability gating.
|
||||
@ -288,6 +321,58 @@ pub fn resolve_program_syscalls(
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Resolve and validate syscall declarations carried by a PBX module.
|
||||
///
|
||||
/// This is the loader-facing path for `BytecodeModule.syscalls`:
|
||||
/// - canonical identity must exist in the host table
|
||||
/// - capability requirements must be satisfied by granted cart flags
|
||||
/// - declared ABI must match authoritative host metadata
|
||||
pub fn resolve_declared_program_syscalls(
|
||||
declared: &[prometeu_bytecode::SyscallDecl],
|
||||
caps: CapFlags,
|
||||
) -> Result<Vec<SyscallResolved>, DeclaredLoadError> {
|
||||
let mut out = Vec::with_capacity(declared.len());
|
||||
|
||||
for decl in declared {
|
||||
let Some(res) = resolve_syscall_impl(&decl.module, &decl.name, decl.version) else {
|
||||
return Err(DeclaredLoadError::UnknownSyscall {
|
||||
module: decl.module.clone(),
|
||||
name: decl.name.clone(),
|
||||
version: decl.version,
|
||||
});
|
||||
};
|
||||
|
||||
let missing = res.meta.caps & !caps;
|
||||
if missing != 0 {
|
||||
return Err(DeclaredLoadError::MissingCapability {
|
||||
required: res.meta.caps,
|
||||
provided: caps,
|
||||
module: decl.module.clone(),
|
||||
name: decl.name.clone(),
|
||||
version: decl.version,
|
||||
});
|
||||
}
|
||||
|
||||
let expected_arg_slots = u16::from(res.meta.arg_slots);
|
||||
let expected_ret_slots = res.meta.ret_slots;
|
||||
if decl.arg_slots != expected_arg_slots || decl.ret_slots != expected_ret_slots {
|
||||
return Err(DeclaredLoadError::AbiMismatch {
|
||||
module: decl.module.clone(),
|
||||
name: decl.name.clone(),
|
||||
version: decl.version,
|
||||
declared_arg_slots: decl.arg_slots,
|
||||
declared_ret_slots: decl.ret_slots,
|
||||
expected_arg_slots,
|
||||
expected_ret_slots,
|
||||
});
|
||||
}
|
||||
|
||||
out.push(res);
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Canonical registry of all syscalls and their metadata.
|
||||
///
|
||||
/// IMPORTANT: This table is the single authoritative source for the slot-based
|
||||
@ -1394,4 +1479,89 @@ mod tests {
|
||||
assert_eq!(ok.len(), 1);
|
||||
assert_eq!(ok[0].id, 0x1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_resolver_returns_expected_id_for_known_identity() {
|
||||
let declared = [prometeu_bytecode::SyscallDecl {
|
||||
module: "gfx".into(),
|
||||
name: "clear".into(),
|
||||
version: 1,
|
||||
arg_slots: 1,
|
||||
ret_slots: 0,
|
||||
}];
|
||||
|
||||
let ok =
|
||||
resolve_declared_program_syscalls(&declared, caps::GFX).expect("must resolve with ABI");
|
||||
assert_eq!(ok.len(), 1);
|
||||
assert_eq!(ok[0].id, 0x1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_resolver_rejects_unknown_identity() {
|
||||
let declared = [prometeu_bytecode::SyscallDecl {
|
||||
module: "gfx".into(),
|
||||
name: "nonexistent".into(),
|
||||
version: 1,
|
||||
arg_slots: 1,
|
||||
ret_slots: 0,
|
||||
}];
|
||||
|
||||
let err = resolve_declared_program_syscalls(&declared, caps::GFX).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
DeclaredLoadError::UnknownSyscall {
|
||||
module: "gfx".into(),
|
||||
name: "nonexistent".into(),
|
||||
version: 1,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_resolver_rejects_missing_capability() {
|
||||
let declared = [prometeu_bytecode::SyscallDecl {
|
||||
module: "gfx".into(),
|
||||
name: "clear".into(),
|
||||
version: 1,
|
||||
arg_slots: 1,
|
||||
ret_slots: 0,
|
||||
}];
|
||||
|
||||
let err = resolve_declared_program_syscalls(&declared, caps::NONE).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
DeclaredLoadError::MissingCapability {
|
||||
required: caps::GFX,
|
||||
provided: caps::NONE,
|
||||
module: "gfx".into(),
|
||||
name: "clear".into(),
|
||||
version: 1,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_resolver_rejects_abi_mismatch() {
|
||||
let declared = [prometeu_bytecode::SyscallDecl {
|
||||
module: "gfx".into(),
|
||||
name: "draw_line".into(),
|
||||
version: 1,
|
||||
arg_slots: 4,
|
||||
ret_slots: 0,
|
||||
}];
|
||||
|
||||
let err = resolve_declared_program_syscalls(&declared, caps::GFX).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
DeclaredLoadError::AbiMismatch {
|
||||
module: "gfx".into(),
|
||||
name: "draw_line".into(),
|
||||
version: 1,
|
||||
declared_arg_slots: 4,
|
||||
declared_ret_slots: 0,
|
||||
expected_arg_slots: 5,
|
||||
expected_ret_slots: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user