pub mod opcodes;
pub mod pattern;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use crate::atom::AtomTable;
use crate::capability::{CapabilityAuditSink, ViolationHandler};
use crate::distribution::control::DistributionSendFacility;
use crate::distribution::remote_link::DistributionControlFacility;
use crate::distribution::{NetKernel, Node};
use crate::error::ExecError;
use crate::io::{IoFacility, IoSink};
use crate::jit::JitCache;
use crate::module::{Module, ModuleRegistry};
use crate::native::code_management_bifs::CodeManagementFacility;
use crate::native::ets_bifs::EtsFacility;
use crate::native::group_leader::GroupLeaderFacility;
use crate::native::io_message::IoMessageFacility;
use crate::native::links::LinkFacility;
use crate::native::process_info_bifs::ProcessInfoFacility;
use crate::native::spawn::SpawnFacility;
use crate::native::supervision::SupervisionFacility;
use crate::native::system_info_bifs::SystemInfoFacility;
use crate::native::{FileIoFacility, NativeEntry, RemoteSpawnFacility, TcpIoFacility};
use crate::process::{CodePosition, ExitReason, Process};
use crate::replay::ReplayDriver;
use crate::scheduler::dirty::DirtySchedulerKind;
use crate::term::Term;
use crate::timer::TimerWheel;
#[derive(Default)]
pub struct NativeServices {
pub atom_table: Option<Arc<AtomTable>>,
pub local_node: Option<Node>,
pub net_kernel: Option<Arc<NetKernel>>,
pub distribution_send: Option<Arc<dyn DistributionSendFacility>>,
pub timers: Option<Arc<Mutex<TimerWheel>>>,
pub spawn_facility: Option<Arc<dyn SpawnFacility>>,
pub remote_spawn_facility: Option<Arc<dyn RemoteSpawnFacility>>,
pub link_facility: Option<Arc<dyn LinkFacility>>,
pub distribution_control_facility: Option<Arc<dyn DistributionControlFacility>>,
pub global_name_facility: Option<Arc<dyn crate::native::GlobalNameFacility>>,
pub group_leader_facility: Option<Arc<dyn GroupLeaderFacility>>,
pub supervision_facility: Option<Arc<dyn SupervisionFacility>>,
pub process_info_facility: Option<Arc<dyn ProcessInfoFacility>>,
pub io_sink: Option<Arc<dyn IoSink>>,
pub code_management_facility: Option<Arc<dyn CodeManagementFacility>>,
pub system_info_facility: Option<Arc<dyn SystemInfoFacility>>,
pub ets_facility: Option<Arc<dyn EtsFacility>>,
pub pg_facility: Option<Arc<dyn crate::distribution::pg::PgFacility>>,
pub io_facility: Option<Arc<dyn IoFacility>>,
pub io_message_facility: Option<Arc<dyn IoMessageFacility>>,
pub file_io_facility: Option<Arc<dyn FileIoFacility>>,
pub tcp_io_facility: Option<Arc<dyn TcpIoFacility>>,
pub jit_cache: Option<Arc<JitCache>>,
pub replay_driver: Option<Arc<Mutex<ReplayDriver>>>,
pub capability_audit_sink: Option<Arc<dyn CapabilityAuditSink>>,
pub capability_violation_handler: Option<Arc<dyn ViolationHandler>>,
pub bif_registry: Option<Arc<crate::native::BifRegistryImpl>>,
pub wasm_async_nif_facility: Option<Rc<dyn crate::native::WasmAsyncNifFacility>>,
pub nif_private_data: Option<Arc<dyn std::any::Any + Send + Sync>>,
pub suspension_registrar: Option<Arc<dyn crate::native::SuspensionRegistrar>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExecutionResult {
Yielded,
Waiting,
Exited(ExitReason),
DirtyCall {
entry: NativeEntry,
args: Vec<Term>,
module: crate::atom::Atom,
function: crate::atom::Atom,
arity: u8,
kind: DirtySchedulerKind,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InstructionOutcome {
Continue,
Jump(CodePosition),
NativeContinuation,
Yield,
Waiting,
Exit(ExitReason),
OnLoadComplete,
DirtyCall {
entry: NativeEntry,
args: Vec<Term>,
module: crate::atom::Atom,
function: crate::atom::Atom,
arity: u8,
kind: DirtySchedulerKind,
},
}
pub fn run(process: &mut Process, module: &Module) -> Result<ExecutionResult, ExecError> {
let empty = NativeServices::default();
run_loop(process, module, None, &empty)
}
pub fn run_with_registry(
process: &mut Process,
initial_module: &Module,
registry: &ModuleRegistry,
) -> Result<ExecutionResult, ExecError> {
let empty = NativeServices::default();
run_loop(process, initial_module, Some(registry), &empty)
}
pub fn run_with_timer_services(
process: &mut Process,
initial_module: &Module,
timers: Arc<Mutex<TimerWheel>>,
) -> Result<ExecutionResult, ExecError> {
let services = NativeServices {
timers: Some(timers),
..NativeServices::default()
};
run_loop(process, initial_module, None, &services)
}
pub fn run_with_native_services(
process: &mut Process,
initial_module: &Module,
registry: &ModuleRegistry,
services: &NativeServices,
) -> Result<ExecutionResult, ExecError> {
run_loop(process, initial_module, Some(registry), services)
}
fn current_module_for_position(
process: &mut Process,
position: CodePosition,
initial_module: &Module,
registry: Option<&ModuleRegistry>,
) -> Result<Arc<Module>, ExecError> {
if let Some(current) = process.current_module()
&& current.name == position.module
{
return Ok(Arc::clone(current));
}
let module = registry
.and_then(|registry| registry.lookup(position.module))
.or_else(|| {
(initial_module.name == position.module).then(|| Arc::new(initial_module.clone()))
})
.ok_or(ExecError::InvalidOperand("code position module"))?;
process.set_current_module(Arc::clone(&module));
Ok(module)
}
fn run_loop(
process: &mut Process,
initial_module: &Module,
registry: Option<&ModuleRegistry>,
services: &NativeServices,
) -> Result<ExecutionResult, ExecError> {
if process.code_position().is_none() {
process.set_code_position(Some(CodePosition {
module: initial_module.name,
instruction_pointer: 0,
}));
}
loop {
let position = process
.code_position()
.ok_or(ExecError::InvalidOperand("code position"))?;
let module_arc = current_module_for_position(process, position, initial_module, registry)?;
let module = module_arc.as_ref();
let instruction = module
.code
.get(position.instruction_pointer)
.ok_or(ExecError::InvalidOperand("instruction pointer"))?;
let next_ip = position
.instruction_pointer
.checked_add(1)
.ok_or(ExecError::InvalidOperand("instruction pointer"))?;
{
static TRACE_IP: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
if *TRACE_IP.get_or_init(|| std::env::var_os("BEAMR_TRACE_IP").is_some()) {
eprintln!(
"trace {:?}@{} {:?}",
module.name, position.instruction_pointer, instruction
);
}
}
match opcodes::dispatch_with_services(
process,
module,
instruction,
next_ip,
services,
registry,
)? {
InstructionOutcome::Continue => process.set_code_position(Some(CodePosition {
module: module.name,
instruction_pointer: next_ip,
})),
InstructionOutcome::NativeContinuation => {}
InstructionOutcome::Jump(target) => process.set_code_position(Some(target)),
InstructionOutcome::Yield => return Ok(ExecutionResult::Yielded),
InstructionOutcome::Waiting => return Ok(ExecutionResult::Waiting),
InstructionOutcome::Exit(reason) => {
process.set_code_position(None);
process.clear_current_module();
return Ok(ExecutionResult::Exited(reason));
}
InstructionOutcome::OnLoadComplete => {
process.set_code_position(None);
process.clear_current_module();
return Ok(ExecutionResult::Exited(ExitReason::Normal));
}
InstructionOutcome::DirtyCall {
entry,
args,
module,
function,
arity,
kind,
} => {
return Ok(ExecutionResult::DirtyCall {
entry,
args,
module,
function,
arity,
kind,
});
}
}
}
}
#[cfg(test)]
mod tests;