// Private resumable tree-walker adapter for the closed ordinary UserFunc
// subset admitted below. The executor-neutral coordinator remains the only
// owner of nested calls and activation lifecycle.
///|
priv struct TreeExecutorPlannedCall {
callee : Value
this_value : Value
}
///|
fn TreeExecutorPlannedCall::TreeExecutorPlannedCall(
callee~ : Value,
this_value~ : Value,
) -> TreeExecutorPlannedCall {
{ callee, this_value }
}
///|
priv struct TreeActivationPlan {
body : Array[@ast.Stmt]
calls : Map[String, TreeExecutorPlannedCall]
}
///|
fn TreeActivationPlan::TreeActivationPlan(
body : Array[@ast.Stmt],
calls : Map[String, TreeExecutorPlannedCall],
) -> TreeActivationPlan {
{ body: body.copy(), calls: calls.copy() }
}
///|
priv struct TreeExecutorCode {
plan : TreeActivationPlan
}
///|
fn TreeExecutorCode::TreeExecutorCode(
plan : TreeActivationPlan,
) -> TreeExecutorCode {
{ plan, }
}
///|
priv enum TreeExecutorWork {
TreeExecuteStatements(Array[@ast.Stmt], Int)
TreeExecuteStatement(@ast.Stmt)
TreeEvaluateExpression(@ast.Expr)
TreeApplyBinary(@ast.BinOp)
TreeSelectBranch(@ast.Stmt, @ast.Stmt?)
TreeDiscardValue
TreeReturnValue
TreeFinishCall(Int, @token.Loc)
}
///|
priv struct TreeExecutorFrame {
env : Environment
ctx : ExecContext
calls : Map[String, TreeExecutorPlannedCall]
work : Array[TreeExecutorWork]
values : Array[Value]
mut awaiting_child : Bool
mut completed : Bool
}
///|
fn TreeExecutorFrame::TreeExecutorFrame(
body : Array[@ast.Stmt],
env : Environment,
ctx : ExecContext,
calls : Map[String, TreeExecutorPlannedCall],
) -> TreeExecutorFrame {
{
env,
ctx,
calls,
work: [TreeExecuteStatements(body, 0)],
values: [],
awaiting_child: false,
completed: false,
}
}
///|
fn TreeExecutorFrame::pop_value(
self : TreeExecutorFrame,
context : String,
) -> Value raise Error {
match self.values.pop() {
Some(value) => value
None =>
raise @errors.InternalError(
message="tree executor value stack underflow while " + context,
)
}
}
///|
fn tree_executor_has_object_identity(
objects : Array[ObjectData],
candidate : ObjectData,
) -> Bool {
for object in objects {
if physical_equal(object, candidate) {
return true
}
}
false
}
///|
fn tree_executor_resolve_callee(closure : Environment, name : String) -> Value? {
match callback_free_binding_in_plain_chain(closure, name) {
CallbackFreeBindingPresent(value) => Some(value)
CallbackFreeBindingMissing | CallbackFreeBindingUnsafe => None
}
}
///|
fn tree_executor_call_key(
requirement : ExecutorActivationDirectCallRequirement,
) -> String? {
match requirement.target {
ExecutorActivationDirectBinding(_) =>
Some(executor_activation_call_name(requirement))
ExecutorActivationStaticOwnDataMember(receiver, _) =>
Some(receiver + "." + executor_activation_call_name(requirement))
}
}
///|
fn tree_executor_resolve_call(
closure : Environment,
requirement : ExecutorActivationDirectCallRequirement,
) -> (String, Value, Value)? {
let key = match tree_executor_call_key(requirement) {
Some(key) => key
None => return None
}
match requirement.target {
ExecutorActivationDirectBinding(name) =>
match tree_executor_resolve_callee(closure, name) {
Some(target) => Some((key, target, Undefined))
None => None
}
ExecutorActivationStaticOwnDataMember(receiver_name, property_name) => {
let receiver = match
tree_executor_resolve_callee(closure, receiver_name) {
Some(receiver) => receiver
None => return None
}
guard receiver is Object(data) &&
data.class_name == "Object" &&
data.callable is None &&
data.arraybuffer_state is None &&
data.bag.internal_slots.is_empty() &&
data.bag.host_slots.is_empty() else {
return None
}
match data.bag.descriptors.get(property_name) {
Some(descriptor) if descriptor.is_accessor => return None
_ => ()
}
match data.bag.properties.get(property_name) {
Some(target) => Some((key, target, receiver))
None => None
}
}
}
}
///|
priv enum TreeExecutorGraphCallable {
TreeExecutorGraphUserFunc(FuncData)
TreeExecutorGraphExecutorCallable(ExecutorCallableData)
}
///|
fn tree_executor_graph_copy_func_data(data : FuncData) -> FuncData {
{
name: data.name,
params: data.params.copy(),
body: data.body.copy(),
closure: data.closure,
strict: data.strict,
has_name_binding: data.has_name_binding,
is_method: data.is_method,
source_text: data.source_text,
}
}
///|
fn tree_executor_graph_copy_capability_summary(
summary : ExecutorActivationCapabilitySummary,
) -> ExecutorActivationCapabilitySummary {
ExecutorActivationCapabilitySummary(
parameter_count=summary.parameter_count,
direct_calls=summary.direct_calls,
)
}
///|
fn tree_executor_graph_copy_executor_data(
data : ExecutorCallableData,
) -> ExecutorCallableData {
let activation_capability_summary = match data.activation_capability_summary {
Some(summary) => Some(tree_executor_graph_copy_capability_summary(summary))
None => None
}
{
name: data.name,
params: data.params.copy(),
closure: data.closure,
strict: data.strict,
code: data.code,
rest_param: data.rest_param,
constructable: data.constructable,
self_name: data.self_name,
define_arguments_object: data.define_arguments_object,
kind: data.kind,
activation_capability_summary,
}
}
///|
priv struct TreeExecutorCallableProof {
callable : TreeExecutorGraphCallable
object_data : ObjectData
summary : ExecutorActivationCapabilitySummary
parameter_count : Int
closure : Environment
}
///|
fn TreeExecutorCallableProof::TreeExecutorCallableProof(
callable~ : TreeExecutorGraphCallable,
object_data~ : ObjectData,
summary~ : ExecutorActivationCapabilitySummary,
parameter_count~ : Int,
closure~ : Environment,
) -> TreeExecutorCallableProof {
{ callable, object_data, summary, parameter_count, closure }
}
///|
priv struct TreeExecutorGraphEdge {
call_key : String
target : Value
this_value : Value
target_object_data : ObjectData
argument_count : Int
target_node_index : Int
}
///|
fn TreeExecutorGraphEdge::TreeExecutorGraphEdge(
call_key~ : String,
target~ : Value,
this_value~ : Value,
target_object_data~ : ObjectData,
argument_count~ : Int,
target_node_index~ : Int,
) -> TreeExecutorGraphEdge {
{
call_key,
target,
this_value,
target_object_data,
argument_count,
target_node_index,
}
}
///|
priv struct TreeExecutorGraphBuildNode {
callable : TreeExecutorGraphCallable
object_data : ObjectData
parameter_count : Int
mut edges : Array[TreeExecutorGraphEdge]
}
///|
fn TreeExecutorGraphBuildNode::TreeExecutorGraphBuildNode(
callable~ : TreeExecutorGraphCallable,
object_data~ : ObjectData,
parameter_count~ : Int,
) -> TreeExecutorGraphBuildNode {
{ callable, object_data, parameter_count, edges: [] }
}
///|
priv struct TreeExecutorGraphNode {
callable : TreeExecutorGraphCallable
object_data : ObjectData
parameter_count : Int
edges : Array[TreeExecutorGraphEdge]
}
///|
fn TreeExecutorGraphNode::TreeExecutorGraphNode(
callable~ : TreeExecutorGraphCallable,
object_data~ : ObjectData,
parameter_count~ : Int,
edges~ : Array[TreeExecutorGraphEdge],
) -> TreeExecutorGraphNode {
{ callable, object_data, parameter_count, edges: edges.copy() }
}
///|
priv struct TreeExecutorGraphArena {
nodes : Array[TreeExecutorGraphNode]
}
///|
fn TreeExecutorGraphArena::TreeExecutorGraphArena(
build_nodes : Array[TreeExecutorGraphBuildNode],
) -> TreeExecutorGraphArena {
let nodes : Array[TreeExecutorGraphNode] = []
for node in build_nodes {
nodes.push(
TreeExecutorGraphNode(
callable=node.callable,
object_data=node.object_data,
parameter_count=node.parameter_count,
edges=node.edges,
),
)
}
{ nodes, }
}
///|
priv struct TreeExecutorGraphCursor {
arena : TreeExecutorGraphArena
node_index : Int
}
///|
fn TreeExecutorGraphCursor::TreeExecutorGraphCursor(
arena~ : TreeExecutorGraphArena,
node_index~ : Int,
) -> TreeExecutorGraphCursor {
{ arena, node_index }
}
///|
priv struct TreeExecutorGraphAdmission {
arena : TreeExecutorGraphArena
root_node_index : Int
}
///|
fn TreeExecutorGraphAdmission::TreeExecutorGraphAdmission(
arena~ : TreeExecutorGraphArena,
root_node_index~ : Int,
) -> TreeExecutorGraphAdmission {
{ arena, root_node_index }
}
///|
fn tree_executor_callable_proof(callable : Value) -> TreeExecutorCallableProof? {
guard callable is Object(object_data) else { return None }
match object_data.callable {
Some(UserFunc(data)) =>
match executor_activation_capability_summary(data.params, data.body) {
Some(summary) if summary.parameter_count == data.params.length() =>
Some(
TreeExecutorCallableProof(
callable=TreeExecutorGraphUserFunc(
tree_executor_graph_copy_func_data(data),
),
object_data~,
summary~,
parameter_count=data.params.length(),
closure=data.closure,
),
)
_ => None
}
Some(ExecutorCallable(data)) =>
match data.activation_capability_summary {
Some(summary) if summary.parameter_count == data.params.length() =>
Some(
TreeExecutorCallableProof(
callable=TreeExecutorGraphExecutorCallable(
tree_executor_graph_copy_executor_data(data),
),
object_data~,
summary~,
parameter_count=data.params.length(),
closure=data.closure,
),
)
_ => None
}
_ => None
}
}
///|
priv enum TreeExecutorGraphAdmissionNodeDecision {
TreeExecutorGraphAdmissionRejected
TreeExecutorGraphAdmissionAlreadyVisited
TreeExecutorGraphAdmissionLeaf
TreeExecutorGraphAdmissionFollow(
Array[ExecutorActivationDirectCallRequirement]
)
}
///|
fn tree_executor_graph_admission_node_decision(
proof : TreeExecutorCallableProof,
arg_count : Int,
visited : Array[ObjectData],
) -> TreeExecutorGraphAdmissionNodeDecision {
guard proof.summary.parameter_count == proof.parameter_count &&
arg_count == proof.summary.parameter_count else {
return TreeExecutorGraphAdmissionRejected
}
guard !tree_executor_has_object_identity(visited, proof.object_data) else {
return TreeExecutorGraphAdmissionAlreadyVisited
}
if proof.summary.direct_calls.is_empty() {
TreeExecutorGraphAdmissionLeaf
} else {
TreeExecutorGraphAdmissionFollow(proof.summary.direct_calls.copy())
}
}
///|
priv struct TreeExecutorGraphAdmissionWork {
proof : TreeExecutorCallableProof
arg_count : Int
node_index : Int
}
///|
fn TreeExecutorGraphAdmissionWork::TreeExecutorGraphAdmissionWork(
proof~ : TreeExecutorCallableProof,
arg_count~ : Int,
node_index~ : Int,
) -> TreeExecutorGraphAdmissionWork {
{ proof, arg_count, node_index }
}
///|
fn tree_executor_graph_find_node(
nodes : Array[TreeExecutorGraphBuildNode],
object_data : ObjectData,
) -> Int? {
for index = 0; index < nodes.length(); index = index + 1 {
if physical_equal(nodes[index].object_data, object_data) {
return Some(index)
}
}
None
}
///|
fn tree_executor_graph_append_node(
nodes : Array[TreeExecutorGraphBuildNode],
scheduled : Array[Bool],
proof : TreeExecutorCallableProof,
) -> Int {
let node_index = nodes.length()
nodes.push(
TreeExecutorGraphBuildNode(
callable=proof.callable,
object_data=proof.object_data,
parameter_count=proof.parameter_count,
),
)
scheduled.push(false)
node_index
}
///|
// Validate and freeze the complete callable graph without host recursion or
// source-AST inspection for an ExecutorCallable child. UserFunc children
// derive their summary once here; executor children contribute only stored
// proof facts. The local build arrays are published only after every edge has
// resolved, validated, and received a stable node index.
fn tree_executor_callable_graph_admission(
proof : TreeExecutorCallableProof,
arg_count : Int,
) -> TreeExecutorGraphAdmission? {
let visited : Array[ObjectData] = []
let nodes : Array[TreeExecutorGraphBuildNode] = []
let scheduled : Array[Bool] = []
let root_node_index = tree_executor_graph_append_node(nodes, scheduled, proof)
scheduled[root_node_index] = true
let work = [
TreeExecutorGraphAdmissionWork(
proof~,
arg_count~,
node_index=root_node_index,
),
]
while !work.is_empty() {
let current = work.pop().unwrap()
match
tree_executor_graph_admission_node_decision(
current.proof,
current.arg_count,
visited,
) {
TreeExecutorGraphAdmissionRejected => return None
TreeExecutorGraphAdmissionAlreadyVisited => ()
TreeExecutorGraphAdmissionLeaf => {
visited.push(current.proof.object_data)
nodes[current.node_index].edges = []
}
TreeExecutorGraphAdmissionFollow(edges) => {
visited.push(current.proof.object_data)
let next_work : Array[TreeExecutorGraphAdmissionWork] = []
let resolved_edges : Array[TreeExecutorGraphEdge] = []
for edge in edges {
let (call_key, target, this_value) = match
tree_executor_resolve_call(current.proof.closure, edge) {
Some(resolved) => resolved
None => return None
}
let target_proof = match tree_executor_callable_proof(target) {
Some(proof) => proof
None => return None
}
guard edge.argument_count == target_proof.parameter_count else {
return None
}
let target_node_index = match
tree_executor_graph_find_node(nodes, target_proof.object_data) {
Some(index) => {
guard nodes[index].parameter_count == target_proof.parameter_count else {
return None
}
index
}
None =>
tree_executor_graph_append_node(nodes, scheduled, target_proof)
}
resolved_edges.push(
TreeExecutorGraphEdge(
call_key~,
target~,
this_value~,
target_object_data=target_proof.object_data,
argument_count=edge.argument_count,
target_node_index~,
),
)
if !tree_executor_has_object_identity(
visited,
target_proof.object_data,
) &&
!scheduled[target_node_index] {
scheduled[target_node_index] = true
next_work.push(
TreeExecutorGraphAdmissionWork(
proof=target_proof,
arg_count=edge.argument_count,
node_index=target_node_index,
),
)
}
}
nodes[current.node_index].edges = resolved_edges
for next in next_work.rev_iter() {
work.push(next)
}
}
}
}
Some(
TreeExecutorGraphAdmission(
arena=TreeExecutorGraphArena(nodes),
root_node_index~,
),
)
}
///|
fn tree_executor_graph_select_edge(
cursor : TreeExecutorGraphCursor,
callee : Value,
argument_count : Int,
) -> TreeExecutorGraphEdge? {
guard callee is Object(object_data) else { return None }
guard cursor.node_index >= 0 &&
cursor.node_index < cursor.arena.nodes.length() else {
return None
}
let node = cursor.arena.nodes[cursor.node_index]
for edge in node.edges {
if edge.argument_count == argument_count &&
physical_equal(edge.target_object_data, object_data) {
return Some(edge)
}
}
None
}
///|
fn tree_executor_graph_node_executable(
node : TreeExecutorGraphNode,
) -> ExecutorCallableData? {
match node.callable {
TreeExecutorGraphUserFunc(data) => {
let calls : Map[String, TreeExecutorPlannedCall] = Map([])
for edge in node.edges {
calls[edge.call_key] = TreeExecutorPlannedCall(
callee=edge.target,
this_value=edge.this_value,
)
}
let code = TreeExecutorCode(TreeActivationPlan(data.body, calls))
Some({
name: data.name.unwrap_or(""),
params: data.params.copy(),
closure: data.closure,
strict: data.strict,
code: code as &ExecutorCode,
rest_param: None,
constructable: !data.is_method,
self_name: if data.has_name_binding {
data.name
} else {
None
},
define_arguments_object: false,
kind: OrdinaryExecutorCallable,
activation_capability_summary: None,
})
}
TreeExecutorGraphExecutorCallable(executable) => Some(executable)
}
}
///|
priv struct TreeExecutorAdmission {
executable : ExecutorCallableData
cursor : TreeExecutorGraphCursor
}
///|
fn TreeExecutorAdmission::TreeExecutorAdmission(
executable~ : ExecutorCallableData,
cursor~ : TreeExecutorGraphCursor,
) -> TreeExecutorAdmission {
{ executable, cursor }
}
///|
fn tree_executor_callable_admission(
callable : Value,
data : FuncData,
args : Array[Value],
) -> TreeExecutorAdmission? {
guard args.length() == data.params.length() else { return None }
for arg in args {
guard arg is Number(_) else { return None }
}
let proof = match tree_executor_callable_proof(callable) {
Some(proof) => proof
None => return None
}
let admission = match
tree_executor_callable_graph_admission(proof, args.length()) {
Some(admission) => admission
None => return None
}
let cursor = TreeExecutorGraphCursor(
arena=admission.arena,
node_index=admission.root_node_index,
)
let root_node = admission.arena.nodes[admission.root_node_index]
let executable = match tree_executor_graph_node_executable(root_node) {
Some(executable) => executable
None => return None
}
Some(TreeExecutorAdmission(executable~, cursor~))
}
///|
fn tree_executor_graph_target(
cursor : TreeExecutorGraphCursor,
edge : TreeExecutorGraphEdge,
) -> TreeExecutorAdmission? {
guard edge.target_node_index >= 0 &&
edge.target_node_index < cursor.arena.nodes.length() else {
return None
}
let target_cursor = TreeExecutorGraphCursor(
arena=cursor.arena,
node_index=edge.target_node_index,
)
let target_node = cursor.arena.nodes[edge.target_node_index]
guard physical_equal(target_node.object_data, edge.target_object_data) &&
target_node.parameter_count == edge.argument_count else {
return None
}
let executable = match tree_executor_graph_node_executable(target_node) {
Some(executable) => executable
None => return None
}
Some(TreeExecutorAdmission(executable~, cursor=target_cursor))
}
///|
impl ExecutorCode for TreeExecutorCode with fn start(self, _interp, prepared) {
TreeExecutorFrame(
self.plan.body,
prepared.environment(),
prepared.context(),
self.plan.calls,
)
as &ExecutorActivationFrame
}
///|
impl ExecutorActivationFrame for TreeExecutorFrame with fn step(self, interp) {
if self.awaiting_child {
raise @errors.InternalError(
message="tree executor frame stepped while awaiting a child completion",
)
}
if self.completed {
raise @errors.InternalError(
message="tree executor frame stepped after completion",
)
}
while !self.work.is_empty() {
let work = self.work.pop().unwrap()
match work {
TreeExecuteStatements(stmts, index) =>
if index < stmts.length() {
self.work.push(TreeExecuteStatements(stmts, index + 1))
self.work.push(TreeExecuteStatement(stmts[index]))
}
TreeExecuteStatement(stmt) => {
interp.observe_execution_step()
match stmt {
ExprStmt(expr, _) => {
self.work.push(TreeDiscardValue)
self.work.push(TreeEvaluateExpression(expr))
}
Block(stmts, _) | StmtList(stmts, _) =>
self.work.push(TreeExecuteStatements(stmts, 0))
IfStmt(condition, then_branch, else_branch, _) => {
self.work.push(TreeSelectBranch(then_branch, else_branch))
self.work.push(TreeEvaluateExpression(condition))
}
ReturnStmt(Some(expr), _) => {
self.work.push(TreeReturnValue)
self.work.push(TreeEvaluateExpression(expr))
}
_ =>
raise @errors.InternalError(
message="unsupported statement entered tree executor frame",
)
}
}
TreeEvaluateExpression(expr) => {
interp.observe_execution_step()
match expr {
NumberLit(number, _, _) => self.values.push(Number(number))
Ident(name, _) =>
self.values.push(
interp.eval_identifier_reference(self.ctx, name, self.env),
)
Grouping(inner, _) => self.work.push(TreeEvaluateExpression(inner))
Binary(op, left, right, _) => {
self.work.push(TreeApplyBinary(op))
self.work.push(TreeEvaluateExpression(right))
self.work.push(TreeEvaluateExpression(left))
}
Call(callee, args, loc) => {
guard executor_activation_call_requirement(callee, args.length())
is Some(requirement) &&
tree_executor_call_key(requirement) is Some(key) &&
self.calls.get(key) is Some(call) else {
raise @errors.InternalError(
message="tree executor call target was absent from its activation plan",
)
}
self.work.push(TreeFinishCall(args.length(), loc))
for arg in args.rev_iter() {
self.work.push(TreeEvaluateExpression(arg))
}
self.values.push(call.callee)
self.values.push(call.this_value)
}
_ =>
raise @errors.InternalError(
message="unsupported expression entered tree executor frame",
)
}
}
TreeApplyBinary(op) => {
let right = self.pop_value("reading a binary right operand")
let left = self.pop_value("reading a binary left operand")
let result = match (op, left, right) {
(Add, Number(a), Number(b)) => Number(a + b)
(Sub, Number(a), Number(b)) => Number(a - b)
(EqEqEq, a, b) => Bool(strict_equal(a, b))
_ =>
raise @errors.InternalError(
message="tree executor numeric proof was violated",
)
}
self.values.push(result)
}
TreeSelectBranch(then_branch, else_branch) => {
let condition = self.pop_value("selecting an if branch")
if is_truthy(condition) {
self.work.push(TreeExecuteStatement(then_branch))
} else {
match else_branch {
Some(branch) => self.work.push(TreeExecuteStatement(branch))
None => ()
}
}
}
TreeDiscardValue => {
let _ = self.pop_value("discarding an expression result")
}
TreeReturnValue => {
let value = self.pop_value("returning from a function")
self.completed = true
return executor_activation_return(value)
}
TreeFinishCall(arg_count, loc) => {
guard self.values.length() >= arg_count + 2 else {
raise @errors.InternalError(
message="tree executor value stack underflow while preparing a call",
)
}
let base = self.values.length() - arg_count - 2
let callee = self.values[base]
let this_value = self.values[base + 1]
let args = Array::makei(arg_count, i => self.values[base + i + 2])
self.values.truncate(base)
self.awaiting_child = true
return executor_activation_call(callee, this_value, args, loc)
}
}
}
self.completed = true
executor_activation_normal(Undefined)
}
///|
impl ExecutorActivationFrame for TreeExecutorFrame with fn deliver_activation_completion(
self,
completion,
) {
guard self.awaiting_child else {
raise @errors.InternalError(
message="tree executor completion has no pending consumer",
)
}
self.awaiting_child = false
match completion {
ExecutorActivationCompletionNormal(value) => self.values.push(value)
ExecutorActivationCompletionAbrupt(error) => raise error
}
}