use std::fmt;
use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct VerifierFlags: u8 {
const VERBOSE = 0b0000_0001;
const TRACE = 0b0000_0010;
const CACHE_RESULTS = 0b0000_0100;
const ALLOW_JSR_RET = 0b0000_1000;
const STRICT_EXCEPTION_HANDLERS = 0b0001_0000;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum VerifyMode {
All,
#[default]
Remote,
None,
}
impl fmt::Display for VerifyMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::All => write!(f, "all"),
Self::Remote => write!(f, "remote"),
Self::None => write!(f, "none"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FallbackStrategy {
#[default]
Strict,
FallbackToInference,
AlwaysInference,
}
impl fmt::Display for FallbackStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Strict => write!(f, "strict"),
Self::FallbackToInference => write!(f, "fallback"),
Self::AlwaysInference => write!(f, "inference"),
}
}
}
#[derive(Debug, Clone)]
pub struct VerifierConfig {
pub verify_mode: VerifyMode,
pub fallback_strategy: FallbackStrategy,
pub flags: VerifierFlags,
pub max_inference_iterations: usize,
pub stackmap_required_version: u16,
}
impl Default for VerifierConfig {
fn default() -> Self {
Self {
verify_mode: VerifyMode::default(),
fallback_strategy: FallbackStrategy::default(),
flags: VerifierFlags::STRICT_EXCEPTION_HANDLERS,
max_inference_iterations: 1000,
stackmap_required_version: 50, }
}
}
impl VerifierConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn strict() -> Self {
Self {
verify_mode: VerifyMode::All,
fallback_strategy: FallbackStrategy::Strict,
flags: VerifierFlags::STRICT_EXCEPTION_HANDLERS,
..Default::default()
}
}
#[must_use]
pub fn permissive() -> Self {
Self {
fallback_strategy: FallbackStrategy::FallbackToInference,
flags: VerifierFlags::ALLOW_JSR_RET,
..Default::default()
}
}
#[must_use]
pub const fn with_verify_mode(mut self, mode: VerifyMode) -> Self {
self.verify_mode = mode;
self
}
#[must_use]
pub const fn with_fallback_strategy(mut self, strategy: FallbackStrategy) -> Self {
self.fallback_strategy = strategy;
self
}
#[must_use]
pub fn with_verbose(mut self, verbose: bool) -> Self {
self.flags.set(VerifierFlags::VERBOSE, verbose);
self
}
#[must_use]
pub fn with_trace(mut self, trace: bool) -> Self {
self.flags.set(VerifierFlags::TRACE, trace);
self
}
#[must_use]
pub const fn with_max_inference_iterations(mut self, max: usize) -> Self {
self.max_inference_iterations = max;
self
}
#[must_use]
pub fn with_cache_results(mut self, cache: bool) -> Self {
self.flags.set(VerifierFlags::CACHE_RESULTS, cache);
self
}
#[must_use]
pub const fn verbose(&self) -> bool {
self.flags.contains(VerifierFlags::VERBOSE)
}
#[must_use]
pub const fn trace(&self) -> bool {
self.flags.contains(VerifierFlags::TRACE)
}
#[must_use]
pub const fn should_verify(&self, is_trusted: bool) -> bool {
match self.verify_mode {
VerifyMode::All => true,
VerifyMode::Remote => !is_trusted,
VerifyMode::None => false,
}
}
#[must_use]
pub const fn requires_stackmap(&self, major_version: u16) -> bool {
major_version >= self.stackmap_required_version
}
#[must_use]
pub const fn use_inference(&self) -> bool {
matches!(self.fallback_strategy, FallbackStrategy::AlwaysInference)
}
#[must_use]
pub const fn allows_inference_fallback(&self) -> bool {
matches!(
self.fallback_strategy,
FallbackStrategy::FallbackToInference | FallbackStrategy::AlwaysInference
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = VerifierConfig::default();
assert_eq!(config.verify_mode, VerifyMode::Remote);
assert_eq!(config.fallback_strategy, FallbackStrategy::Strict);
assert!(!config.verbose());
assert!(!config.trace());
}
#[test]
fn test_strict_config() {
let config = VerifierConfig::strict();
assert_eq!(config.verify_mode, VerifyMode::All);
assert_eq!(config.fallback_strategy, FallbackStrategy::Strict);
assert!(
config
.flags
.contains(VerifierFlags::STRICT_EXCEPTION_HANDLERS)
);
}
#[test]
fn test_permissive_config() {
let config = VerifierConfig::permissive();
assert_eq!(
config.fallback_strategy,
FallbackStrategy::FallbackToInference
);
assert!(
!config
.flags
.contains(VerifierFlags::STRICT_EXCEPTION_HANDLERS)
);
assert!(config.flags.contains(VerifierFlags::ALLOW_JSR_RET));
}
#[test]
fn test_should_verify() {
let all = VerifierConfig::new().with_verify_mode(VerifyMode::All);
assert!(all.should_verify(true));
assert!(all.should_verify(false));
let remote = VerifierConfig::new().with_verify_mode(VerifyMode::Remote);
assert!(!remote.should_verify(true));
assert!(remote.should_verify(false));
let none = VerifierConfig::new().with_verify_mode(VerifyMode::None);
assert!(!none.should_verify(true));
assert!(!none.should_verify(false));
}
#[test]
fn test_requires_stackmap() {
let config = VerifierConfig::default();
assert!(!config.requires_stackmap(49)); assert!(config.requires_stackmap(50)); assert!(config.requires_stackmap(52)); assert!(config.requires_stackmap(65)); }
}