197 lines
7.8 KiB
Plaintext
197 lines
7.8 KiB
Plaintext
// BoxCallHandlerBox - Handle boxcall instruction (Box method calls)
|
|
// Single Responsibility: Dispatch dynamic Box method calls
|
|
|
|
using selfhost.vm.boxes.result_box as Result
|
|
using selfhost.shared.common.string_ops as StringOps
|
|
using selfhost.shared.common.string_helpers as StringHelpers
|
|
using selfhost.vm.hakorune-vm.json_field_extractor as JsonFieldExtractor
|
|
using selfhost.vm.hakorune-vm.inst_field_extractor as InstFieldExtractor
|
|
using selfhost.vm.hakorune-vm.args_extractor as ArgsExtractorBox
|
|
using selfhost.vm.hakorune-vm.args_guard as ArgsGuardBox
|
|
using selfhost.vm.hakorune-vm.value_manager as ValueManagerBox
|
|
using selfhost.vm.hakorune-vm.receiver_guard as ReceiverGuardBox
|
|
|
|
static box BoxCallHandlerBox {
|
|
// Handle boxcall instruction
|
|
// inst_json: {"op":"boxcall","box":X,"method":"name","args":[...],"dst":Y}
|
|
// regs: register MapBox
|
|
// Returns: Result.Ok(0) or Result.Err(message)
|
|
handle(inst_json, regs) {
|
|
// Extract box (receiver) ValueId
|
|
local box_id = JsonFieldExtractor.extract_int(inst_json, "box")
|
|
if box_id == null {
|
|
return Result.Err("boxcall: box field not found")
|
|
}
|
|
|
|
// Extract method name
|
|
local method_name = JsonFieldExtractor.extract_string(inst_json, "method")
|
|
if method_name == null {
|
|
return Result.Err("boxcall: method field not found")
|
|
}
|
|
|
|
// Extract arguments (construct fake mir_call JSON for ArgsExtractorBox)
|
|
// We need to extract args array from inst_json
|
|
local args_array = me._extract_args(inst_json, regs)
|
|
|
|
// Special-case: methodRef — tolerate missing/empty arity by defaulting to 0 and return early
|
|
if method_name == "methodRef" {
|
|
local name = args_array.get(0)
|
|
local arity = args_array.get(1)
|
|
if arity == null { arity = 0 }
|
|
local dst_reg = JsonFieldExtractor.extract_int(inst_json, "dst")
|
|
// Load receiver early for direct dispatch
|
|
local receiver = ValueManagerBox.get(regs, box_id)
|
|
if receiver == null {
|
|
return Result.Err("boxcall: receiver v%" + StringHelpers.int_to_str(box_id) + " is unset (method=methodRef)")
|
|
}
|
|
local result_val = receiver.methodRef(name, arity)
|
|
if dst_reg != null { ValueManagerBox.set(regs, dst_reg, result_val) }
|
|
return Result.Ok(0)
|
|
}
|
|
|
|
|
|
// Guard arguments (no nulls)
|
|
local _g = ArgsGuardBox.ensure_no_nulls(args_array, method_name + "/" + args_array.length())
|
|
if _g.is_Err() { return _g }
|
|
|
|
// Extract destination register
|
|
local dst_reg = JsonFieldExtractor.extract_int(inst_json, "dst")
|
|
|
|
// Load receiver object
|
|
local receiver = ValueManagerBox.get(regs, box_id)
|
|
if receiver == null {
|
|
return Result.Err("boxcall: receiver v%" + StringHelpers.int_to_str(box_id) + " is unset (method=" + method_name + ")")
|
|
}
|
|
|
|
// Prepare method signature for dispatch
|
|
local arg_count = args_array.length()
|
|
local method_sig = method_name + "/" + arg_count
|
|
|
|
// Known methods dispatch table
|
|
local result_val = null
|
|
|
|
// StringBox methods (using Rust VM available methods)
|
|
if method_sig == "upper/0" {
|
|
result_val = receiver.to_upper()
|
|
} else if method_sig == "lower/0" {
|
|
result_val = receiver.to_lower()
|
|
} else if method_sig == "size/0" {
|
|
result_val = receiver.length()
|
|
} else if method_sig == "length/0" {
|
|
result_val = receiver.length()
|
|
} else if method_sig == "isEmpty/0" {
|
|
result_val = receiver.isEmpty()
|
|
} else if method_sig == "substring/1" {
|
|
result_val = receiver.substring(args_array.get(0))
|
|
} else if method_sig == "substring/2" {
|
|
result_val = receiver.substring(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "charAt/1" {
|
|
result_val = receiver.charAt(args_array.get(0))
|
|
} else if method_sig == "indexOf/1" {
|
|
result_val = receiver.indexOf(args_array.get(0))
|
|
} else if method_sig == "indexOf/2" {
|
|
result_val = receiver.indexOf(args_array.get(0), args_array.get(1))
|
|
}
|
|
|
|
// ArrayBox methods
|
|
else if method_sig == "push/1" {
|
|
result_val = receiver.push(args_array.get(0))
|
|
} else if method_sig == "get/1" {
|
|
result_val = receiver.get(args_array.get(0))
|
|
} else if method_sig == "set/2" {
|
|
result_val = receiver.set(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "length/0" {
|
|
result_val = receiver.length()
|
|
} else if method_sig == "size/0" {
|
|
result_val = receiver.length()
|
|
} else if method_sig == "isEmpty/0" {
|
|
result_val = receiver.isEmpty()
|
|
}
|
|
|
|
// MapBox methods
|
|
else if method_sig == "get/1" {
|
|
result_val = receiver.get(args_array.get(0))
|
|
} else if method_sig == "set/2" {
|
|
result_val = receiver.set(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "has/1" {
|
|
result_val = receiver.has(args_array.get(0))
|
|
} else if method_sig == "size/0" {
|
|
result_val = receiver.length()
|
|
} else if method_sig == "isEmpty/0" {
|
|
result_val = receiver.isEmpty()
|
|
} else if method_sig == "delete/1" {
|
|
result_val = receiver.delete(args_array.get(0))
|
|
} else if method_sig == "keys/0" {
|
|
result_val = receiver.keys()
|
|
} else if method_sig == "values/0" {
|
|
result_val = receiver.values()
|
|
} else if method_sig == "methodRef/2" {
|
|
// slot 113: Array.methodRef(method_name, arity) -> CallableBox
|
|
result_val = receiver.methodRef(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "call/1" {
|
|
// slot 501: CallableBox.call(args_array) -> any
|
|
// Flatten: pass args_array.get(0) (the actual args ArrayBox) to CallableBox.call
|
|
// CallableBox.call will flatten the ArrayBox into individual arguments
|
|
result_val = receiver.call(args_array.get(0))
|
|
} else if method_sig == "arity/0" {
|
|
// slot 500: CallableBox.arity() -> Integer
|
|
result_val = receiver.arity()
|
|
} else if method_sig == "call/2" {
|
|
// slot 210: Map.call(key, args_array) -> any
|
|
result_val = receiver.call(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "callAsync/2" {
|
|
// slot 211: Map.callAsync(key, args_array) -> FutureBox
|
|
result_val = receiver.callAsync(args_array.get(0), args_array.get(1))
|
|
} else if method_sig == "callAsync/1" {
|
|
// slot 502: CallableBox.callAsync(args_array) -> FutureBox
|
|
// Flatten: pass args_array.get(0) (the actual args ArrayBox) to CallableBox.callAsync
|
|
// CallableBox.callAsync will flatten the ArrayBox into individual arguments
|
|
result_val = receiver.callAsync(args_array.get(0))
|
|
}
|
|
|
|
// Fallback: unknown method
|
|
// Note: Method resolution depends on Rust VM's plugin switching mechanism
|
|
// (NYASH_VM_DISABLE_STRING_HANDLERS, NYASH_USE_PLUGIN_BUILTINS, etc.)
|
|
// Future: Add Selfhost VM's own plugin resolution here
|
|
else {
|
|
return Result.Err("boxcall: unknown method: " + method_sig)
|
|
}
|
|
|
|
// Store result in destination register
|
|
if dst_reg != null {
|
|
ValueManagerBox.set(regs, dst_reg, result_val)
|
|
}
|
|
|
|
return Result.Ok(0)
|
|
}
|
|
|
|
// Extract args array from boxcall inst_json
|
|
// inst_json: {"op":"boxcall","args":[...],...}
|
|
// Returns: ArrayBox of argument values
|
|
_extract_args(inst_json, regs) {
|
|
// Find "args" field
|
|
local args_key = "\"args\":["
|
|
local args_start = inst_json.indexOf(args_key)
|
|
if args_start < 0 {
|
|
// No args field, return empty array
|
|
return new ArrayBox()
|
|
}
|
|
args_start = args_start + args_key.length()
|
|
|
|
// Find array end
|
|
local args_end = StringOps.index_of_from(inst_json, "]", args_start)
|
|
if args_end < 0 {
|
|
return new ArrayBox()
|
|
}
|
|
|
|
// Extract args array content
|
|
local args_content = inst_json.substring(args_start, args_end)
|
|
|
|
// Create fake mir_call JSON for ArgsExtractorBox
|
|
local fake_mir_call = "{\"args\":[" + args_content + "]}"
|
|
|
|
// Use ArgsExtractorBox to parse and load args
|
|
return ArgsExtractorBox.extract_and_load(fake_mir_call, regs)
|
|
}
|
|
}
|