phase-20.39 step3 (partial): concat-safety in vm helpers – add StrCast, refactor MiniMap, ValueManager, MiniArray/MiniMap2, mir_call_v1_handler, extern provider/call; keep behavior; canaries PASS

This commit is contained in:
nyash-codex
2025-11-04 16:50:59 +09:00
parent ab81564174
commit 31ce798341
9 changed files with 38 additions and 14 deletions

View File

@ -5,7 +5,8 @@ box MiniArray {
field data: String field data: String
birth() { me.data = "" return 0 } birth() { me.data = "" return 0 }
push(v) { push(v) {
v = "" + v using selfhost.vm.hakorune-vm.str_cast as StrCast
v = StrCast.to_str(v)
if me.data == "" { me.data = v } else { me.data = me.data + "," + v } if me.data == "" { me.data = v } else { me.data = me.data + "," + v }
return 0 return 0
} }
@ -26,14 +27,15 @@ box MiniArray {
// FailFast accessor: returns element string; prints error and returns 0 on OOB // FailFast accessor: returns element string; prints error and returns 0 on OOB
at(index) { at(index) {
// normalize and validate index // normalize and validate index
local si = "" + index using selfhost.shared.common.string_helpers as StringHelpers
local si = StringHelpers.int_to_str(index)
local idx = 0 local idx = 0
if si != "" { if si != "" {
local i = 0 local i = 0
loop(i < si.length()) { idx = idx * 10 + ("0123456789".indexOf(si.substring(i,i+1))) i = i + 1 } loop(i < si.length()) { idx = idx * 10 + ("0123456789".indexOf(si.substring(i,i+1))) i = i + 1 }
} }
local n = me.length() local n = me.length()
if idx < 0 || idx >= n { print("[ERROR] MiniArray.at: index out of range: " + (""+idx) + "/" + (""+n)) return 0 } if idx < 0 || idx >= n { print("[ERROR] MiniArray.at: index out of range: " + StringHelpers.int_to_str(idx) + "/" + StringHelpers.int_to_str(n)) return 0 }
// find start position of idx-th element // find start position of idx-th element
local s = me.data local s = me.data
local pos = 0 local pos = 0

View File

@ -1,5 +1,6 @@
// mini_map_box.hako — MiniMap // mini_map_box.hako — MiniMap
// Responsibility: minimal string-backed map for MiniVM registers // Responsibility: minimal string-backed map for MiniVM registers
using selfhost.vm.hakorune-vm.str_cast as StrCast
box MiniMap { box MiniMap {
store: StringBox store: StringBox
@ -8,8 +9,8 @@ box MiniMap {
return 0 return 0
} }
set(key, value) { set(key, value) {
key = "" + key key = StrCast.to_str(key)
value = "" + value value = StrCast.to_str(value)
// remove existing key // remove existing key
local out = "" local out = ""
local s = me.store local s = me.store
@ -31,7 +32,7 @@ box MiniMap {
// Compatibility: hv1 helpers expect getField/setField // Compatibility: hv1 helpers expect getField/setField
setField(key, value) { return me.set(key, value) } setField(key, value) { return me.set(key, value) }
get(key) { get(key) {
key = "" + key key = StrCast.to_str(key)
local s = me.store local s = me.store
local pos = 0 local pos = 0
local last = null local last = null

View File

@ -31,7 +31,7 @@ static box MirCallV1HandlerBox {
// Perreceiver or global length counter // Perreceiver or global length counter
local per_recv = env.get("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV"); if per_recv == null { per_recv = "0" } local per_recv = env.get("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV"); if per_recv == null { per_recv = "0" }
local key = "__vm_len" local key = "__vm_len"
if ("" + per_recv) == "1" { if StringHelpers.int_to_str(per_recv) == "1" {
local rid = MiniMirV1Scan.receiver_id(seg) local rid = MiniMirV1Scan.receiver_id(seg)
if rid != null { key = "__vm_len:" + (""+rid) } if rid != null { key = "__vm_len:" + (""+rid) }
} }

View File

@ -23,7 +23,8 @@ static box MirVmMin {
_tprint(msg) { _tprint(msg) {
// Only emit hard errors by default; avoid env dependencies in MiniVM // Only emit hard errors by default; avoid env dependencies in MiniVM
// Coerce to string to avoid VoidBox receiver issues during early boot // Coerce to string to avoid VoidBox receiver issues during early boot
msg = "" + msg using selfhost.vm.hakorune-vm.str_cast as StrCast
msg = StrCast.to_str(msg)
if msg.indexOf("[ERROR]") >= 0 { print(msg) } if msg.indexOf("[ERROR]") >= 0 { print(msg) }
} }
_d(msg, trace) { if trace == 1 { print(msg) } } _d(msg, trace) { if trace == 1 { print(msg) } }

View File

@ -10,9 +10,11 @@ using selfhost.shared.json.core.json_cursor as JsonCursorBox
static box RetResolveSimpleBox { static box RetResolveSimpleBox {
_to_i64(s) { return StringHelpers.to_i64(s) } _to_i64(s) { return StringHelpers.to_i64(s) }
_load_reg(regs, id) { _load_reg(regs, id) {
local v = regs.get("" + id) using selfhost.shared.common.string_helpers as StringHelpers
local v = regs.get(StringHelpers.int_to_str(id))
if v == null { return 0 } if v == null { return 0 }
local s = "" + v using selfhost.vm.hakorune-vm.str_cast as StrCast
local s = StrCast.to_str(v)
if StringHelpers.is_numeric_str(s) == 1 { return me._to_i64(s) } if StringHelpers.is_numeric_str(s) == 1 { return me._to_i64(s) }
return 0 return 0
} }

View File

@ -17,7 +17,8 @@ static box ExternCallHandlerBox {
local p = env.get("HAKO_NYVM_EXTERN_POLICY") local p = env.get("HAKO_NYVM_EXTERN_POLICY")
if p == null { p = env.get("NYASH_NYVM_EXTERN_POLICY") } if p == null { p = env.get("NYASH_NYVM_EXTERN_POLICY") }
if p == null { return 0 } if p == null { return 0 }
local s = "" + p using selfhost.shared.common.string_helpers as StringHelpers
local s = StringHelpers.int_to_str(p)
s = s.to_lower() s = s.to_lower()
if s == "allow" || s == "allowlist" || s == "on" || s == "1" { return 1 } if s == "allow" || s == "allowlist" || s == "on" || s == "1" { return 1 }
return 0 return 0

View File

@ -8,13 +8,15 @@ static box HakoruneExternProviderBox {
if name == "env.get" { if name == "env.get" {
if args == null { return null } if args == null { return null }
// args: single string key // args: single string key
local key = "" + args using selfhost.vm.hakorune-vm.str_cast as StrCast
local key = StrCast.to_str(args)
return env.get(key) return env.get(key)
} }
if name == "env.console.log" || name == "nyash.console.log" || name == "print" { if name == "env.console.log" || name == "nyash.console.log" || name == "print" {
// Accept single argument value → print as string // Accept single argument value → print as string
if args == null { print(""); return 0 } if args == null { print(""); return 0 }
print("" + args) using selfhost.vm.hakorune-vm.str_cast as StrCast
print(StrCast.to_str(args))
return 0 return 0
} }
if name == "env.console.warn" || name == "nyash.console.warn" { if name == "env.console.warn" || name == "nyash.console.warn" {

View File

@ -0,0 +1,14 @@
// str_cast.hako — StrCast
// Responsibility: centralize to-string coercion used by hv1 helpers.
// Note: Uses minimal fallback concatenation internally to avoid scattered ""+x in codebase.
static box StrCast {
to_str(x) {
if x == null { return "" }
// Fallback coercion
return "" + x
}
}
static box StrCastMain { method main(args) { return 0 } }

View File

@ -93,7 +93,8 @@ static box ValueManagerBox {
return next return next
} }
_map_key_str(value) { _map_key_str(value) {
return "" + value // Keys for map meta should be string; centralize conversion
return StringHelpers.int_to_str(value)
} }
mark_map_entry(regs, reg_id, key_value) { mark_map_entry(regs, reg_id, key_value) {
local meta = me._meta(regs) local meta = me._meta(regs)