use crate::common::{Error, GlobalNames};
use core::any::Any;
use gear_core::{memory::HostPointer, str::LimitedStr};
use gear_lazy_pages_common::{GlobalsAccessError, GlobalsAccessMod, GlobalsAccessor};
use gear_sandbox_host::sandbox::SandboxInstance;
use sp_wasm_interface_common::Value;
#[derive(Debug, Clone, Copy)]
pub(crate) enum GlobalNo {
Gas = 0,
}
#[derive(Debug)]
pub(crate) struct GlobalsContext {
pub names: GlobalNames,
pub access_ptr: HostPointer,
pub access_mod: GlobalsAccessMod,
}
struct GlobalsAccessWasmRuntime<'a> {
pub instance: &'a mut SandboxInstance,
}
impl GlobalsAccessor for GlobalsAccessWasmRuntime<'_> {
fn get_i64(&mut self, name: &LimitedStr) -> Result<i64, GlobalsAccessError> {
unsafe {
self.instance
.signal_handler_get_global_val(name.as_str())
.and_then(|value| match value {
Value::I64(value) => Some(value),
_ => None,
})
.ok_or(GlobalsAccessError)
}
}
fn set_i64(&mut self, name: &LimitedStr, value: i64) -> Result<(), GlobalsAccessError> {
unsafe {
self.instance
.signal_handler_set_global_val(name.as_str(), Value::I64(value))
.ok()
.flatten()
.ok_or(GlobalsAccessError)?
}
Ok(())
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
unimplemented!("Has no use cases for this struct")
}
}
struct GlobalsAccessNativeRuntime<'a, 'b> {
pub inner_access_provider: &'a mut &'b mut dyn GlobalsAccessor,
}
impl GlobalsAccessor for GlobalsAccessNativeRuntime<'_, '_> {
fn get_i64(&mut self, name: &LimitedStr) -> Result<i64, GlobalsAccessError> {
self.inner_access_provider.get_i64(name)
}
fn set_i64(&mut self, name: &LimitedStr, value: i64) -> Result<(), GlobalsAccessError> {
self.inner_access_provider.set_i64(name, value)
}
fn as_any_mut(&mut self) -> &mut dyn Any {
unimplemented!("Has no use cases for this struct")
}
}
fn apply_for_global_internal(
mut globals_access_provider: impl GlobalsAccessor,
name: &str,
mut f: impl FnMut(u64) -> Result<Option<u64>, Error>,
) -> Result<u64, Error> {
let name = LimitedStr::try_from(name).map_err(|_| Error::AccessGlobal(GlobalsAccessError))?;
let current_value = globals_access_provider.get_i64(&name)? as u64;
if let Some(new_value) = f(current_value)? {
globals_access_provider.set_i64(&name, new_value as i64)?;
Ok(new_value)
} else {
Ok(current_value)
}
}
pub(crate) unsafe fn apply_for_global(
globals_ctx: &GlobalsContext,
global_name: &str,
f: impl FnMut(u64) -> Result<Option<u64>, Error>,
) -> Result<u64, Error> {
match globals_ctx.access_mod {
GlobalsAccessMod::WasmRuntime => {
let instance = (globals_ctx.access_ptr as *mut SandboxInstance)
.as_mut()
.ok_or(Error::HostInstancePointerIsInvalid)?;
apply_for_global_internal(GlobalsAccessWasmRuntime { instance }, global_name, f)
}
GlobalsAccessMod::NativeRuntime => {
let inner_access_provider = (globals_ctx.access_ptr as *mut &mut dyn GlobalsAccessor)
.as_mut()
.ok_or(Error::DynGlobalsAccessPointerIsInvalid)?;
apply_for_global_internal(
GlobalsAccessNativeRuntime {
inner_access_provider,
},
global_name,
f,
)
}
}
}