///|
/// Public, immutable policy inputs for an explicitly bounded evaluation.
/// The fields remain private so callers can only obtain validated values.
pub(all) struct ExecutionPolicy {
priv step_budget_ : Int64
priv stack_depth_limit_ : Int64
priv interruption_ : InterruptionHandle
}
///|
/// Opaque validation details for an invalid bounded-evaluation policy.
pub(all) struct ExecutionPolicyError {
priv parameter_code_ : String
priv message_ : String
}
///|
/// A monotonic, shared host interruption request.
pub(all) struct InterruptionHandle {
priv request_ : ExecutionInterruptionRequest
}
///|
/// Private marker carried by an engine-created depth RangeError value. Host
/// slots are invisible to JavaScript property access and survive translation
/// into a catchable object without relying on user-controlled message text.
let engine_stack_depth_error_slot : HostSlotKey = HostSlotKey::reserve()
///|
pub fn InterruptionHandle::InterruptionHandle() -> InterruptionHandle {
{ request_: ExecutionInterruptionRequest() }
}
///|
pub fn InterruptionHandle::request(self : InterruptionHandle) -> Unit {
self.request_.request()
}
///|
pub fn InterruptionHandle::is_requested(self : InterruptionHandle) -> Bool {
self.request_.is_requested()
}
///|
fn execution_policy_error(
parameter_code : String,
message : String,
) -> ExecutionPolicyError {
{ parameter_code_: parameter_code, message_: message }
}
///|
pub fn ExecutionPolicy::new(
step_budget : Int64,
stack_depth_limit : Int64,
interruption : InterruptionHandle,
) -> Result[ExecutionPolicy, ExecutionPolicyError] {
guard step_budget >= 0L && step_budget <= MAX_PORTABLE_EXECUTION_COUNT else {
return Err(
execution_policy_error(
"step-budget", "step budget must be between 0 and 2147483647",
),
)
}
guard stack_depth_limit >= 0L &&
stack_depth_limit <= MAX_PORTABLE_EXECUTION_COUNT else {
return Err(
execution_policy_error(
"stack-depth", "stack depth limit must be between 0 and 2147483647",
),
)
}
Ok({
step_budget_: step_budget,
stack_depth_limit_: stack_depth_limit,
interruption_: interruption,
})
}
///|
pub fn ExecutionPolicyError::parameter_code(
self : ExecutionPolicyError,
) -> String {
self.parameter_code_
}
///|
pub fn ExecutionPolicyError::message(self : ExecutionPolicyError) -> String {
self.message_
}
///|
fn mark_engine_stack_depth_error(value : Value) -> Value {
match value {
Object(data) => {
data.bag.set_host_slot(engine_stack_depth_error_slot, Bool(true))
Object(data)
}
_ => value
}
}
///|
pub fn is_engine_stack_depth_error(value : Value) -> Bool {
match value {
Object(data) => data.bag.has_host_slot(engine_stack_depth_error_slot)
_ => false
}
}
///|
/// Translate private guardrail failures at the stable Engine boundary without
/// exposing the runtime error constructors through the root facade.
pub fn execution_control_failure_code(error : Error) -> String? {
match error {
ExecutionControlError(Interrupted) => Some("interrupted")
ExecutionControlError(StackDepthLimit) => Some("stack-depth-limit")
ExecutionControlError(ExecutionLimit) => Some("execution-limit")
_ => None
}
}
///|
pub fn stack_depth_limit_message() -> String {
STACK_DEPTH_LIMIT_MESSAGE
}
///|
fn ExecutionPolicy::to_execution_control(
self : ExecutionPolicy,
) -> ExecutionControl raise Error {
ExecutionControl(
remaining_steps=self.step_budget_,
maximum_active_depth=self.stack_depth_limit_,
interruption=self.interruption_.request_,
)
}
///|
/// Run parsed source under one operation-scoped control carrier. The carrier
/// is installed and restored by the imperative shell around the existing run
/// path, so unbounded compatibility entry points remain unchanged.
pub fn Interpreter::run_bounded(
self : Interpreter,
stmts : Array[@ast.Stmt],
policy : ExecutionPolicy,
) -> Value raise Error {
let control = policy.to_execution_control()
self.with_execution_control(control, () => self.run(stmts))
}
///|
/// Run one internal operation under a fresh execution-control carrier.
/// Stable root-facade operations use this shell to keep one policy active
/// across multiple runtime phases without exposing the private control type.
pub fn[T] Interpreter::with_execution_policy(
self : Interpreter,
policy : ExecutionPolicy,
action : () -> T raise Error,
) -> T raise Error {
let control = policy.to_execution_control()
self.with_execution_control(control, action)
}