use crate::{
metadata::{
method::MethodMap,
tables::{
ImportScopeMap, LocalConstantMap, LocalScope, LocalScopeRc, LocalVariableMap,
MetadataTable, TableId, TableInfoRef, TableRow,
},
token::Token,
},
Result,
};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct LocalScopeRaw {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub method: u32,
pub import_scope: u32,
pub variable_list: u32,
pub constant_list: u32,
pub start_offset: u32,
pub length: u32,
}
impl LocalScopeRaw {
pub fn to_owned(
&self,
methods: &MethodMap,
import_scopes: &ImportScopeMap,
variables: &LocalVariableMap,
constants: &LocalConstantMap,
scope_table: &MetadataTable<LocalScopeRaw>,
) -> Result<LocalScopeRc> {
let method_token = Token::new(
0x0600_0000_u32
.checked_add(self.method)
.ok_or_else(|| malformed_error!("Method token overflow: {}", self.method))?,
);
let method = methods
.get(&method_token)
.ok_or_else(|| malformed_error!("Invalid method index {} in LocalScope", self.method))?
.value()
.clone();
let import_scope =
if self.import_scope == 0 {
None
} else {
let import_token =
Token::new(0x3500_0000_u32.checked_add(self.import_scope).ok_or_else(
|| malformed_error!("ImportScope token overflow: {}", self.import_scope),
)?);
Some(
import_scopes
.get(&import_token)
.ok_or_else(|| {
malformed_error!(
"Invalid import scope index {} in LocalScope",
self.import_scope
)
})?
.value()
.clone(),
)
};
let next_rid = self.rid.checked_add(1).ok_or_else(|| {
malformed_error!(
"LocalScope rid overflow when computing next rid: {}",
self.rid
)
})?;
let variables = if self.variable_list == 0 {
Arc::new(boxcar::Vec::new())
} else {
let start = self.variable_list;
let end = if let Some(next_scope) = scope_table.get(next_rid) {
if next_scope.variable_list != 0 {
next_scope.variable_list
} else {
let len = u32::try_from(variables.len()).map_err(|_| {
malformed_error!("LocalVariable count exceeds u32: {}", variables.len())
})?;
len.checked_add(1).ok_or_else(|| {
malformed_error!("LocalVariable end index overflow: {}", len)
})?
}
} else {
let len = u32::try_from(variables.len()).map_err(|_| {
malformed_error!("LocalVariable count exceeds u32: {}", variables.len())
})?;
len.checked_add(1)
.ok_or_else(|| malformed_error!("LocalVariable end index overflow: {}", len))?
};
let list = Arc::new(boxcar::Vec::new());
for i in start..end {
let var_token_value = 0x3300_0000_u32
.checked_add(i)
.ok_or_else(|| malformed_error!("LocalVariable token overflow: {}", i))?;
let var_token = Token::new(var_token_value);
if let Some(var_entry) = variables.get(&var_token) {
list.push(var_entry.value().clone());
}
}
list
};
let constants = if self.constant_list == 0 {
Arc::new(boxcar::Vec::new())
} else {
let start = self.constant_list;
let end = if let Some(next_scope) = scope_table.get(next_rid) {
if next_scope.constant_list != 0 {
next_scope.constant_list
} else {
let len = u32::try_from(constants.len()).map_err(|_| {
malformed_error!("LocalConstant count exceeds u32: {}", constants.len())
})?;
len.checked_add(1).ok_or_else(|| {
malformed_error!("LocalConstant end index overflow: {}", len)
})?
}
} else {
let len = u32::try_from(constants.len()).map_err(|_| {
malformed_error!("LocalConstant count exceeds u32: {}", constants.len())
})?;
len.checked_add(1)
.ok_or_else(|| malformed_error!("LocalConstant end index overflow: {}", len))?
};
let list = Arc::new(boxcar::Vec::new());
for i in start..end {
let const_token_value = 0x3400_0000_u32
.checked_add(i)
.ok_or_else(|| malformed_error!("LocalConstant token overflow: {}", i))?;
let const_token = Token::new(const_token_value);
if let Some(const_entry) = constants.get(&const_token) {
list.push(const_entry.value().clone());
}
}
list
};
let local_scope = LocalScope {
rid: self.rid,
token: self.token,
offset: self.offset,
method,
import_scope,
variables,
constants,
start_offset: self.start_offset,
length: self.length,
};
Ok(Arc::new(local_scope))
}
}
impl TableRow for LocalScopeRaw {
#[rustfmt::skip]
fn row_size(sizes: &TableInfoRef) -> u32 {
u32::from(
sizes.table_index_bytes(TableId::MethodDef)
.saturating_add(sizes.table_index_bytes(TableId::ImportScope))
.saturating_add(sizes.table_index_bytes(TableId::LocalVariable))
.saturating_add(sizes.table_index_bytes(TableId::LocalConstant))
.saturating_add(4)
.saturating_add(4)
)
}
}