fix(mir): PHI検証panic修正 - update_cfg()を検証前に呼び出し

A案実装: debug_verify_phi_inputs呼び出し前にCFG predecessorを更新

修正箇所(7箇所):
- src/mir/builder/phi.rs:50, 73, 132, 143
- src/mir/builder/ops.rs:273, 328, 351

根本原因:
- Branch/Jump命令でsuccessorは即座に更新
- predecessorはupdate_cfg()で遅延再構築
- PHI検証が先に実行されてpredecessor未更新でpanic

解決策:
- 各debug_verify_phi_inputs呼び出し前に
  if let Some(func) = self.current_function.as_mut() {
      func.update_cfg();
  }
  を挿入してCFGを同期

影響: if/else文、論理演算子(&&/||)のPHI生成が正常動作
This commit is contained in:
nyash-codex
2025-11-01 13:28:56 +09:00
parent 149ec61d4d
commit 6a452b2dca
174 changed files with 2432 additions and 1014 deletions

View File

@ -9,21 +9,21 @@ static box ArithmeticBox {
_to_dec_str(x) {
local s = "" + x
local i = 0
loop(i < s.size()) { local ch = s.substring(i,i+1) if ch=="+" || ch==" " { i=i+1 } else { break } }
s = s.substring(i, s.size())
loop(i < s.length()) { local ch = s.substring(i,i+1) if ch=="+" || ch==" " { i=i+1 } else { break } }
s = s.substring(i, s.length())
i = 0
loop(i < s.size() && s.substring(i,i+1)=="0") { i = i + 1 }
if i >= s.size() { return "0" }
return s.substring(i, s.size())
loop(i < s.length() && s.substring(i,i+1)=="0") { i = i + 1 }
if i >= s.length() { return "0" }
return s.substring(i, s.length())
}
_cmp_dec(a, b) {
local sa = me._to_dec_str(a)
local sb = me._to_dec_str(b)
if sa.size() < sb.size() { return -1 }
if sa.size() > sb.size() { return 1 }
if sa.length() < sb.length() { return -1 }
if sa.length() > sb.length() { return 1 }
local i = 0
loop(i < sa.size()) {
loop(i < sa.length()) {
local ca = sa.substring(i,i+1)
local cb = sb.substring(i,i+1)
if ca != cb { if ca < cb { return -1 } else { return 1 } }
@ -35,8 +35,8 @@ static box ArithmeticBox {
_add_dec(a, b) {
local sa = me._to_dec_str(a)
local sb = me._to_dec_str(b)
local i = sa.size() - 1
local j = sb.size() - 1
local i = sa.length() - 1
local j = sb.length() - 1
local carry = 0
local out = new ArrayBox()
loop(i >= 0 || j >= 0 || carry > 0) {
@ -49,7 +49,7 @@ static box ArithmeticBox {
local d = s % 10
out.push(("0123456789").substring(d, d+1))
}
local k = out.size()
local k = out.length()
local res = ""
loop(k > 0) { k = k - 1 res = res + (""+out.get(k)) }
return res
@ -62,8 +62,8 @@ static box ArithmeticBox {
local c = me._cmp_dec(sa, sb)
if c == 0 { return "0" }
if c < 0 { return "-" + me._sub_dec(sb, sa) }
local i = sa.size() - 1
local j = sb.size() - 1
local i = sa.length() - 1
local j = sb.length() - 1
local borrow = 0
local out = new ArrayBox()
loop(i >= 0) {
@ -75,7 +75,7 @@ static box ArithmeticBox {
out.push(("0123456789").substring(d, d+1))
i = i - 1
}
local k = out.size() - 1
local k = out.length() - 1
loop(true) { if k > 0 && out.get(k) == "0" { k = k - 1 } else { break } }
local res = ""
loop(k >= 0) { res = res + (""+out.get(k)) k = k - 1 }
@ -86,8 +86,8 @@ static box ArithmeticBox {
local sa = me._to_dec_str(a)
local sb = me._to_dec_str(b)
if sa == "0" || sb == "0" { return "0" }
local na = sa.size()
local nb = sb.size()
local na = sa.length()
local nb = sb.length()
local res = new ArrayBox()
local t = 0
loop(t < na+nb) { res.push(0) t = t + 1 }
@ -108,9 +108,9 @@ static box ArithmeticBox {
ia = ia - 1
}
local k = 0
loop(k < res.size() && res.get(k) == 0) { k = k + 1 }
loop(k < res.length() && res.get(k) == 0) { k = k + 1 }
local out = ""
loop(k < res.size()) { out = out + ("0123456789").substring(res.get(k), res.get(k)+1) k = k + 1 }
loop(k < res.length()) { out = out + ("0123456789").substring(res.get(k), res.get(k)+1) k = k + 1 }
if out == "" { return "0" } else { return out }
}

View File

@ -18,7 +18,7 @@ static box CompareScanBox {
local sym = JsonFragBox.get_str(seg, "operation")
if sym != "" { kind = CompareOpsBox.map_symbol(sym) } else { kind = "Eq" }
}
return map({ dst: dst, lhs: lhs, rhs: rhs, kind: kind })
return { dst: dst, lhs: lhs, rhs: rhs, kind: kind }
}
}

View File

@ -11,7 +11,7 @@ using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box FlowDebugBox {
// ユーティリティ — 文字列検索
_index_of_from(hay, needle, pos) { if pos < 0 { pos = 0 } local n = hay.size() if pos > n { return -1 } local i = pos local m = needle.size() if m <= 0 { return pos } local limit = n - m loop(i <= limit) { if hay.substring(i, i+m) == needle { return i } i = i + 1 } return -1 }
_index_of_from(hay, needle, pos) { if pos < 0 { pos = 0 } local n = hay.length() if pos > n { return -1 } local i = pos local m = needle.length() if m <= 0 { return pos } local limit = n - m loop(i <= limit) { if hay.substring(i, i+m) == needle { return i } i = i + 1 } return -1 }
_read_digits(text, pos) { return StringHelpers.read_digits(text, pos) }
_int_to_str(n) { return StringHelpers.int_to_str(n) }
@ -34,7 +34,7 @@ static box FlowDebugBox {
if limit == null { limit = 50 }
local ops = new ArrayBox()
local pos = 0
loop(ops.size() < limit) {
loop(ops.length() < limit) {
local p = me._index_of_from(mjson, "\"op\":\"", pos)
if p < 0 { break }
local q = me._index_of_from(mjson, "\"", p + 6)
@ -50,9 +50,9 @@ static box FlowDebugBox {
validate_cf_targets(mjson) {
local ids = me.collect_block_ids(mjson)
// Set 風マップ化
local idset = map({})
local idset = {}
local i = 0
loop(i < ids.size()) { idset.set(ids.get(i), 1) i = i + 1 }
loop(i < ids.length()) { idset.set(ids.get(i), 1) i = i + 1 }
local errs = new ArrayBox()
local pos = 0
@ -78,20 +78,20 @@ static box FlowDebugBox {
}
// レポート
if errs.size() == 0 { print("{\"kind\":\"flow_debug\",\"ok\":true,\"blocks\":" + (""+ids.size()) + "}") }
if errs.length() == 0 { print("{\"kind\":\"flow_debug\",\"ok\":true,\"blocks\":" + (""+ids.length()) + "}") }
else {
local k = 0
loop(k < errs.size()) { print("{\"kind\":\"flow_debug\",\"ok\":false,\"msg\":\"" + errs.get(k) + "\"}") k = k + 1 }
loop(k < errs.length()) { print("{\"kind\":\"flow_debug\",\"ok\":false,\"msg\":\"" + errs.get(k) + "\"}") k = k + 1 }
}
return errs.size()
return errs.length()
}
// 要約: 先頭の op を列挙
summarize_ops(mjson, limit) {
local ops = me.collect_ops(mjson, limit)
local i = 0
loop(i < ops.size()) { print("{\"kind\":\"flow_ops\",\"op\":\"" + ops.get(i) + "\"}") i = i + 1 }
return ops.size()
loop(i < ops.length()) { print("{\"kind\":\"flow_ops\",\"op\":\"" + ops.get(i) + "\"}") i = i + 1 }
return ops.length()
}
main(args) { return 0 }

View File

@ -5,7 +5,7 @@ using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
using "lang/src/vm/boxes/cfg_navigator.hako" as CfgNavigatorBox
static box InstructionScannerBox {
_tprint(msg) { if call("String.indexOf/2", msg, "[ERROR]") >= 0 { print(msg) } }
_tprint(msg) { if msg.indexOf("[ERROR]") >= 0 { print(msg) } }
index_of_from(hay, needle, pos) { return CfgNavigatorBox.index_of_from(hay, needle, pos) }
@ -16,7 +16,7 @@ static box InstructionScannerBox {
if seg == null { return "" }
local out = ""
local i = 0
local n = seg.size()
local n = seg.length()
loop (i < n) {
local ch = seg.substring(i, i+1)
if i+2 <= n {
@ -36,7 +36,7 @@ static box InstructionScannerBox {
local k1 = "\"op\":\""
local p1 = obj.indexOf(k1)
if p1 >= 0 {
local i = p1 + k1.size() // start of value (right after opening quote)
local i = p1 + k1.length() // start of value (right after opening quote)
local j = JsonCursorBox.scan_string_end(obj, i - 1)
if j > i { return obj.substring(i, j) }
}
@ -44,7 +44,7 @@ static box InstructionScannerBox {
local kk = "\"kind\":\""
local pk = obj.indexOf(kk)
if pk >= 0 {
local i2 = pk + kk.size()
local i2 = pk + kk.length()
local j2 = JsonCursorBox.scan_string_end(obj, i2 - 1)
if j2 > i2 {
local k = obj.substring(i2, j2)
@ -97,7 +97,7 @@ static box InstructionScannerBox {
endp = endp + 1
local obj = seg.substring(start, endp)
local op = me._extract_op(obj)
return map({ start: start, end: endp, op: op })
return { start: start, end: endp, op: op }
}
// MiniVM friendly variant: return "start,end,op" to avoid MapBox dependency

View File

@ -5,7 +5,7 @@ static box MiniJsonCur {
// Skip whitespace from pos; return first non-ws index or -1
next_non_ws(s, pos) {
local i = pos
local n = s.size()
local n = s.length()
loop (i < n) {
local ch = s.substring(i, i+1)
if ch != " " && ch != "\n" && ch != "\r" && ch != "\t" { return i }
@ -19,7 +19,7 @@ static box MiniJsonCur {
if s.substring(i, i+1) != "\"" { return "" }
i = i + 1
local out = ""
local n = s.size()
local n = s.length()
loop (i < n) {
local ch = s.substring(i, i+1)
if ch == "\"" { break }

View File

@ -30,7 +30,7 @@ box MiniArray {
local idx = 0
if si != "" {
local i = 0
loop(i < si.size()) { 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()
if idx < 0 || idx >= n { print("[ERROR] MiniArray.at: index out of range: " + (""+idx) + "/" + (""+n)) return 0 }
@ -45,7 +45,7 @@ box MiniArray {
cur = cur + 1
}
local endp = s.indexOf(",", pos)
if endp < 0 { endp = s.size() }
if endp < 0 { endp = s.length() }
return s.substring(pos, endp)
}
}
@ -91,7 +91,7 @@ box MiniMap2 {
local eq = line.indexOf("=")
if eq >= 0 {
local k = line.substring(0, eq)
if k == key { return line.substring(eq + 1, line.size()) }
if k == key { return line.substring(eq + 1, line.length()) }
}
pos = nl + 1
}
@ -110,7 +110,7 @@ box MiniMap2 {
if s == "" { return 0 }
// naive contains of 'key=' at line start or after \n
local needle = key + "="
if s.substring(0, needle.size()) == needle { return 1 }
if s.substring(0, needle.length()) == needle { return 1 }
local p = s.indexOf("\n" + needle)
if p >= 0 { return 1 }
return 0

View File

@ -6,7 +6,7 @@ static box MiniVmEntryBox {
run_trace(j) {
if j.substring(0,1) == "{" {
local payload = j.substring(1, j.size())
local payload = j.substring(1, j.length())
local j2 = "{\"__trace__\":1," + payload
return MirVmMin.run_min(j2)
}

View File

@ -13,7 +13,7 @@ static box MiniVmPrints {
local k_val = "\"value\":\""
local s = scan.index_of_from(json, k_val, print_pos)
if s < 0 || s >= end { return -1 }
local i = s + k_val.size()
local i = s + k_val.length()
local j = scan.index_of_from(json, "\"", i)
if j <= 0 || j > end { return -1 }
print(json.substring(i, j))
@ -35,7 +35,7 @@ static box MiniVmPrints {
local k_val2 = "\"value\":"
local v2 = scan.index_of_from(json, k_val2, tpos)
if v2 <= 0 || v2 >= obj_end { return -1 }
local digits = scan.read_digits(json, v2 + k_val2.size())
local digits = scan.read_digits(json, v2 + k_val2.length())
if digits == "" { return -1 }
print(digits)
return obj_end + 1
@ -49,7 +49,7 @@ static box MiniVmPrints {
local k_name = "\"name\":\""
local npos = scan.index_of_from(json, k_name, fcp)
if npos <= 0 || npos >= end { return -1 }
local ni = npos + k_name.size()
local ni = npos + k_name.length()
local nj = scan.index_of_from(json, "\"", ni)
if nj <= 0 || nj > end { return -1 }
local fname = json.substring(ni, nj)
@ -76,7 +76,7 @@ static box MiniVmPrints {
if fname == "itoa" { print("0") return arr_end + 1 }
return -1
}
atpos = atpos + k_t.size()
atpos = atpos + k_t.length()
local at_end = scan.index_of_from(json, "\"", atpos)
if at_end <= 0 || at_end > arr_end { return -1 }
local aty = json.substring(atpos, at_end)
@ -84,7 +84,7 @@ static box MiniVmPrints {
local k_sval = "\"value\":\""
local svalp = scan.index_of_from(json, k_sval, at_end)
if svalp <= 0 || svalp >= arr_end { return -1 }
local si = svalp + k_sval.size()
local si = svalp + k_sval.length()
local sj = scan.index_of_from(json, "\"", si)
if sj <= 0 || sj > arr_end { return -1 }
local sval = json.substring(si, sj)
@ -95,8 +95,8 @@ static box MiniVmPrints {
local k_ival = "\"value\":"
local ivalp = scan.index_of_from(json, k_ival, at_end)
if ivalp <= 0 || ivalp >= arr_end { return -1 }
local digits = scan.read_digits(json, ivalp + k_ival.size())
if fname == "itoa" || fname == "echo" { print(digits) return ivalp + k_ival.size() }
local digits = scan.read_digits(json, ivalp + k_ival.length())
if fname == "itoa" || fname == "echo" { print(digits) return ivalp + k_ival.length() }
return -1
}
return -1

View File

@ -6,12 +6,12 @@ using "lang/src/vm/boxes/op_handlers.hako" as OpHandlersBox
static box MiniVmProbe {
probe_compare(mjson) {
local regs = map({})
local regs = {}
local seg = JsonFragBox.block0_segment(mjson)
if seg.size() == 0 { return map({}) }
if seg.length() == 0 { return {} }
local pos = 0
loop(true) {
if pos >= seg.size() { break }
if pos >= seg.length() { break }
// Use escape-aware scanner to get next instruction object
local mm = InstructionScannerBox.next(seg, pos)
if mm == null { break }
@ -39,13 +39,13 @@ static box MiniVmProbe {
"Ge" => { if a >= b { 1 } else { 0 } }
_ => 0
}
return map({ a: a, b: b, r: r })
return { a: a, b: b, r: r }
}
_ => {}
}
pos = i
}
return map({ a: 0, b: 0, r: 0 })
return { a: 0, b: 0, r: 0 }
}
}

View File

@ -9,7 +9,7 @@ static box MirVmM2 {
_find_int_in(seg, keypat) {
local p = seg.indexOf(keypat)
if p < 0 { return null }
p = p + keypat.size()
p = p + keypat.length()
local i = p
local out = ""
loop(true) {
@ -23,7 +23,7 @@ static box MirVmM2 {
_find_str_in(seg, keypat) {
local p = seg.indexOf(keypat)
if p < 0 { return "" }
p = p + keypat.size()
p = p + keypat.length()
local q = seg.indexOf(""", p)
if q < 0 { return "" }
return seg.substring(p, q)
@ -53,7 +53,7 @@ static box MirVmM2 {
if name_end < 0 { break }
local opname = json.substring(name_start, name_end)
local next_pos = StringOps.index_of_from(json, ""op":"", name_end)
if next_pos < 0 { next_pos = json.size() }
if next_pos < 0 { next_pos = json.length() }
local seg = json.substring(op_pos, next_pos)
if opname == "const" {
local dst = me._find_int_in(seg, ""dst":")

View File

@ -51,7 +51,7 @@ box MiniMap {
local eq = line.indexOf("=")
if eq >= 0 {
local k = line.substring(0, eq)
if k == key { last = line.substring(eq + 1, line.size()) }
if k == key { last = line.substring(eq + 1, line.length()) }
}
pos = nl + 1
}
@ -72,8 +72,8 @@ static box MirVmMin {
local key = '"callee":{"name":"'
local p = seg.indexOf(key)
if p < 0 { return "" }
p = p + key.size()
local rest = seg.substring(p, seg.size())
p = p + key.length()
local rest = seg.substring(p, seg.length())
local q = rest.indexOf('"')
if q < 0 { return "" }
return rest.substring(0, q)
@ -83,7 +83,7 @@ static box MirVmMin {
local key = '"args":'
local p = seg.indexOf(key)
if p < 0 { return null }
p = p + key.size()
p = p + key.length()
// find first digit or '-'
local i = p
loop(true) {
@ -144,7 +144,7 @@ static box MirVmMin {
run_thin(mjson) {
// Inject a lightweight marker into JSON to toggle thin mode inside _run_min
if mjson.substring(0,1) == "{" {
local payload = mjson.substring(1, mjson.size())
local payload = mjson.substring(1, mjson.length())
local j2 = "{\"__thin__\":1," + payload
local v = me._run_min(j2)
print(me._int_to_str(v))
@ -158,7 +158,7 @@ static box MirVmMin {
// helpers
_int_to_str(n) { return StringHelpers.int_to_str(n) }
_is_numeric_str(s){ if s==null {return 0} local n=s.size() if n==0 {return 0} local i=0 if s.substring(0,1)=="-" { if n==1 {return 0} i=1 } loop(i<n){ local ch=s.substring(i,i+1) if ch<"0"||ch>"9" {return 0} i=i+1 } return 1 }
_is_numeric_str(s){ if s==null {return 0} local n=s.length() if n==0 {return 0} local i=0 if s.substring(0,1)=="-" { if n==1 {return 0} i=1 } loop(i<n){ local ch=s.substring(i,i+1) if ch<"0"||ch>"9" {return 0} i=i+1 } return 1 }
_load_reg(regs,id){ local v=regs.getField(""+id) if v==null {return 0} local s=""+v if me._is_numeric_str(s)==1 { return JsonFragBox._str_to_int(s) } return 0 }
// block helpers
@ -195,7 +195,7 @@ static box MirVmMin {
local key = "\"value\":{\"type\":\"i64\",\"value\":"
local p = StringOps.index_of_from(b0, key, 0)
if p >= 0 {
local ds = b0.substring(p + key.size(), b0.size())
local ds = b0.substring(p + key.length(), b0.length())
// read consecutive digits
local i = 0
local out = ""
@ -234,13 +234,13 @@ static box MirVmMin {
me._d("[DEBUG] endp="+me._int_to_str(endp), trace)
if endp <= start { return 0 }
local inst_seg = mjson.substring(start, endp)
me._d("[DEBUG] seglen="+me._int_to_str(inst_seg.size()), trace)
me._d("[DEBUG] seglen="+me._int_to_str(inst_seg.length()), trace)
// scan objects in this block
local scan_pos = 0
local inst_count = 0
local moved = 0
loop(true){
if scan_pos >= inst_seg.size() { break }
if scan_pos >= inst_seg.length() { break }
local tup = InstructionScannerBox.next_tuple(inst_seg, scan_pos)
if tup == "" { break }
// parse "start,end,op"
@ -249,7 +249,7 @@ static box MirVmMin {
if c1 < 0 || c2 < 0 { break }
local obj_start = JsonFragBox._str_to_int(tup.substring(0, c1))
local obj_end = JsonFragBox._str_to_int(tup.substring(c1+1, c2))
local op = tup.substring(c2+1, tup.size())
local op = tup.substring(c2+1, tup.length())
local seg = inst_seg.substring(obj_start, obj_end)
if op == null { op = "" } if op == "null" { op = "" } if op == 0 { op = "" }
if op == "" {
@ -283,7 +283,7 @@ static box MirVmMin {
local krhs_fast = rec.get("rhs")
local kcmp_fast = rec.get("kind")
// Determine if a ret exists after this compare in the same block
local tail = inst_seg.substring(obj_end, inst_seg.size())
local tail = inst_seg.substring(obj_end, inst_seg.length())
local ridt = JsonFragBox.get_int(tail, "value")
if kdst_fast != null && klhs_fast != null && krhs_fast != null && ridt != null && ridt == kdst_fast {
local a = me._load_reg(regs, klhs_fast)
@ -378,7 +378,7 @@ else if op == "ret" {
// Prefer recent compare result when a ret exists targeting it (no recompute)
if inst_seg.indexOf("\"op\":\"ret\"") >= 0 {
local rstartX = inst_seg.indexOf("\"op\":\"ret\"")
local rsegX = inst_seg.substring(rstartX, inst_seg.size())
local rsegX = inst_seg.substring(rstartX, inst_seg.length())
local ridX = JsonFragBox.get_int(rsegX, "value")
if ridX != null { if ridX == last_cmp_dst { return last_cmp_val } }
}
@ -387,7 +387,7 @@ else if op == "ret" {
// Detect explicit ret in this block and resolve
if inst_seg.indexOf("\"op\":\"ret\"") >= 0 {
local rstart = inst_seg.indexOf("\"op\":\"ret\"")
local rseg = inst_seg.substring(rstart, inst_seg.size())
local rseg = inst_seg.substring(rstart, inst_seg.length())
local rid = JsonFragBox.get_int(rseg, "value")
if rid != null {
if rid == last_cmp_dst {
@ -410,7 +410,7 @@ else if op == "ret" {
loop(true) {
local k = StringOps.index_of_from(search, key, pos)
if k < 0 { break }
local ds = search.substring(k + key.size(), search.size())
local ds = search.substring(k + key.length(), search.length())
// read consecutive digits as number
local i = 0
local out = ""
@ -421,29 +421,29 @@ else if op == "ret" {
}
if out != "" { if first == "" { first = out } else { second = out } }
if second != "" { break }
pos = k + key.size() + i
pos = k + key.length() + i
}
if first != "" && second != "" {
local lv = 0
local rv = 0
// simple to_i64
local i0 = 0
loop(i0 < first.size()) { lv = lv * 10 + ("0123456789".indexOf(first.substring(i0,i0+1))) i0 = i0 + 1 }
loop(i0 < first.length()) { lv = lv * 10 + ("0123456789".indexOf(first.substring(i0,i0+1))) i0 = i0 + 1 }
local i1 = 0
loop(i1 < second.size()) { rv = rv * 10 + ("0123456789".indexOf(second.substring(i1,i1+1))) i1 = i1 + 1 }
loop(i1 < second.length()) { rv = rv * 10 + ("0123456789".indexOf(second.substring(i1,i1+1))) i1 = i1 + 1 }
// cmp: parse cmp op
local cmp_key = "\"cmp\":\""
local pk = inst_seg.indexOf(cmp_key)
local cmp = "Eq"
if pk >= 0 {
local i = pk + cmp_key.size()
local i = pk + cmp_key.length()
local j = StringOps.index_of_from(inst_seg, "\"", i)
if j > i { cmp = inst_seg.substring(i, j) }
}
local cv = CompareOpsBox.eval(cmp, lv, rv)
// ret id
local rstart4 = inst_seg.indexOf("\"op\":\"ret\"")
local rseg4 = inst_seg.substring(rstart4, inst_seg.size())
local rseg4 = inst_seg.substring(rstart4, inst_seg.length())
local rid4 = JsonFragBox.get_int(rseg4, "value")
if rid4 != null { return cv }
}

View File

@ -20,7 +20,7 @@ static box OpHandlersBox {
_find_int_in(seg, keypat) {
local p = seg.indexOf(keypat)
if p < 0 { return null }
p = p + keypat.size()
p = p + keypat.length()
local i = p
local out = ""
loop(true) {
@ -37,9 +37,9 @@ static box OpHandlersBox {
_find_str_in(seg, keypat) {
local p = seg.indexOf(keypat)
if p < 0 { return "" }
p = p + keypat.size()
p = p + keypat.length()
// Use substring to work around indexOf not supporting start position
local rest = seg.substring(p, seg.size())
local rest = seg.substring(p, seg.length())
local q = rest.indexOf("\"")
if q < 0 { return "" }
return rest.substring(0, q)
@ -77,7 +77,7 @@ static box OpHandlersBox {
local p = seg.indexOf(pat_i64)
if p >= 0 {
// Minimal digit read (inline)
local i = p + pat_i64.size()
local i = p + pat_i64.length()
local out = ""
loop(true) {
local ch = seg.substring(i, i+1)
@ -96,7 +96,7 @@ static box OpHandlersBox {
local pat_str = "\"value\":{\"type\":\"string\",\"value\":\""
local ps = seg.indexOf(pat_str)
if ps >= 0 {
local start = ps + pat_str.size()
local start = ps + pat_str.length()
// Use escape-aware scanner to find the string end at the matching quote
local vend = JsonCursorBox.scan_string_end(seg, start - 1)
if vend > start {
@ -111,7 +111,7 @@ static box OpHandlersBox {
local pat_v = "\"value\":\""
local pv = seg.indexOf(pat_v)
if pv >= 0 {
local start = pv + pat_v.size()
local start = pv + pat_v.length()
local vend = JsonCursorBox.scan_string_end(seg, start - 1)
if vend > start {
local s2 = seg.substring(start, vend)

View File

@ -36,10 +36,10 @@ static box PhiDecodeBox {
local key = me._dq()+"values"+me._dq()+":"+me._lsq()
local p = seg.indexOf(key)
if p < 0 { return null }
local arr_br = p + key.size() - 1 // points at '['
local arr_br = p + key.length() - 1 // points at '['
local i = arr_br + 1
local endp = JsonCursorBox.seek_array_end(seg, arr_br)
local n = seg.size()
local n = seg.length()
if endp >= 0 { n = endp }
local best_dst = JsonFragBox.get_int(seg, "dst")
if best_dst == null { return { type: "err", code: "phi:invalid-object:dst-missing" } }

View File

@ -14,7 +14,7 @@ box ReleaseManagerBox {
if arr == null { return 0 }
// Defensive: tolerate both ArrayBox and array-like values
local n = 0
if arr.length != null { n = arr.size() } else { return 0 }
if arr.length != null { n = arr.length() } else { return 0 }
local i = 0
loop(i < n) {
local v = arr.get(i)

View File

@ -20,7 +20,7 @@ static box RetResolveSimpleBox {
_resolve_explicit(inst_seg, regs, last_cmp_dst, last_cmp_val) {
local rpos = inst_seg.indexOf("\"op\":\"ret\"")
if rpos < 0 { return null }
local rseg = inst_seg.substring(rpos, inst_seg.size())
local rseg = inst_seg.substring(rpos, inst_seg.length())
local rid = JsonFragBox.get_int(rseg, "value")
if rid == null { return null }
if rid == last_cmp_dst { return last_cmp_val }
@ -30,7 +30,7 @@ static box RetResolveSimpleBox {
_resolve_kind(inst_seg, regs, last_cmp_dst, last_cmp_val) {
local kpos = inst_seg.indexOf("\"kind\":\"Ret\"")
if kpos < 0 { return null }
local kseg = inst_seg.substring(kpos, inst_seg.size())
local kseg = inst_seg.substring(kpos, inst_seg.length())
local rid = JsonFragBox.get_int(kseg, "value")
if rid == null { return null }
if rid == last_cmp_dst { return last_cmp_val }

View File

@ -26,12 +26,12 @@ static box RuneHostBox {
// else try simple integer literal (only digits)
local i = 0
local ok = 1
loop(i < code.size()) { local ch = code.substring(i,i+1) if !(ch == "0" or ch == "1" or ch == "2" or ch == "3" or ch == "4" or ch == "5" or ch == "6" or ch == "7" or ch == "8" or ch == "9") { ok = 0 } i = i + 1 }
loop(i < code.length()) { local ch = code.substring(i,i+1) if !(ch == "0" or ch == "1" or ch == "2" or ch == "3" or ch == "4" or ch == "5" or ch == "6" or ch == "7" or ch == "8" or ch == "9") { ok = 0 } i = i + 1 }
if ok == 1 {
// very rough: build integer by repeated add (limited)
local n = 0
i = 0
loop(i < code.size()) {
loop(i < code.length()) {
n = n * 10
local ch2 = code.substring(i,i+1)
if ch2 == "1" { n = n + 1 } else { if ch2 == "2" { n = n + 2 } else { if ch2 == "3" { n = n + 3 } else { if ch2 == "4" { n = n + 4 } else { if ch2 == "5" { n = n + 5 } else { if ch2 == "6" { n = n + 6 } else { if ch2 == "7" { n = n + 7 } else { if ch2 == "8" { n = n + 8 } else { if ch2 == "9" { n = n + 9 } } } } } } } }

View File

@ -11,7 +11,7 @@ box ScannerBox {
me._src = source
me._pos = 0
me._max = 0
if source != null { me._max = source.size() }
if source != null { me._max = source.length() }
}
at_end() {

View File

@ -13,7 +13,7 @@ static box SeamInspector {
@n = end
@delta = 0
loop (i < n) {
@ch = call("String.substring/2", text, i, i+1)
@ch = text.substring(i, i+1)
if ch == "\"" {
i = i + 1
loop (i < n) {
@ -32,13 +32,13 @@ static box SeamInspector {
}
_scan_boxes(text) {
@i = 0
@n = text.size()
@n = text.length()
@res = new ArrayBox()
@tok = "static box "
loop (i < n) {
@p = StringOps.index_of_from(text, tok, i)
if p < 0 { break }
@j = p + tok.size()
@j = p + tok.length()
@name = ""
loop (j < n) {
@c = text.substring(j, j+1)
@ -50,7 +50,7 @@ static box SeamInspector {
loop (j < n) { @c2 = text.substring(j, j+1) if c2 == "{" { break } j = j + 1 }
@end = MiniVmScan.find_balanced_object_end(text, j)
if end < 0 { end = j }
@obj = map({})
@obj = {}
obj.set("name", name)
obj.set("start", _int_to_str(p))
obj.set("end", _int_to_str(end))
@ -61,17 +61,17 @@ static box SeamInspector {
}
_report_duplicate_boxes(text) {
@boxes = _scan_boxes(text)
@cnt = map({})
@cnt = {}
@names = new ArrayBox()
@i = 0
loop (i < boxes.size()) {
loop (i < boxes.length()) {
@name = boxes.get(i).get("name")
@cur = cnt.get(name)
if cur == null { cnt.set(name, "1") names.push(name) } else { cnt.set(name, _int_to_str(_str_to_int(cur) + 1)) }
i = i + 1
}
@j = 0
loop (j < names.size()) {
loop (j < names.length()) {
@k = names.get(j)
@v = cnt.get(k)
if _str_to_int(v) > 1 { print("dup_box " + k + " x" + v) }
@ -82,9 +82,9 @@ static box SeamInspector {
_report_duplicate_functions_in_box(text, box_name) {
@boxes = _scan_boxes(text)
@i = 0
@fnmap = map({})
@fnmap = {}
@fnames = new ArrayBox()
loop (i < boxes.size()) {
loop (i < boxes.length()) {
@b = boxes.get(i)
if b.get("name") == box_name {
@s = _str_to_int(b.get("start"))
@ -142,7 +142,7 @@ static box SeamInspector {
i = i + 1
}
@x = 0
loop (x < fnames.size()) {
loop (x < fnames.length()) {
@nm = fnames.get(x)
@ct = fnmap.get(nm)
if _str_to_int(ct) > 1 { print("dup_fn " + box_name + "." + nm + " x" + ct) }

View File

@ -8,14 +8,14 @@ static box StepRunnerBox {
_index_of_from(hay, needle, pos) { return JsonCursorBox.index_of_from(hay, needle, pos) }
// Delegate digit scanning to JsonCursorBox for consistency
_read_digits(text, pos) { return JsonCursorBox.digits_from(text, pos) }
_int(s) { local t = "" + s if t == "" { return 0 } local i = 0 local neg = 0 if t.substring(0,1) == "-" { neg = 1 i = 1 } local acc = 0 loop(i < t.size()) { local ch = t.substring(i, i+1) if ch < "0" || ch > "9" { break } acc = acc * 10 + (ch == "0" ? 0 : ch == "1" ? 1 : ch == "2" ? 2 : ch == "3" ? 3 : ch == "4" ? 4 : ch == "5" ? 5 : ch == "6" ? 6 : ch == "7" ? 7 : ch == "8" ? 8 : 9) i = i + 1 } if neg == 1 { acc = 0 - acc } return acc }
_int(s) { local t = "" + s if t == "" { return 0 } local i = 0 local neg = 0 if t.substring(0,1) == "-" { neg = 1 i = 1 } local acc = 0 loop(i < t.length()) { local ch = t.substring(i, i+1) if ch < "0" || ch > "9" { break } acc = acc * 10 + (ch == "0" ? 0 : ch == "1" ? 1 : ch == "2" ? 2 : ch == "3" ? 3 : ch == "4" ? 4 : ch == "5" ? 5 : ch == "6" ? 6 : ch == "7" ? 7 : ch == "8" ? 8 : 9) i = i + 1 } if neg == 1 { acc = 0 - acc } return acc }
// block 0 の instructions セグメント抽出
_block0_segment(mjson) {
local key = "\"instructions\":["
local k = me._index_of_from(mjson, key, 0)
if k < 0 { return "" }
local lb = k + key.size() - 1
local lb = k + key.length() - 1
// Delegate to JsonScanBox for robust end detection (escape-aware)
local rb = JsonCursorBox.seek_array_end(mjson, lb)
if rb < 0 { return "" }
@ -25,7 +25,7 @@ static box StepRunnerBox {
// compare の (lhs,rhs,kind,dst) を抽出
parse_compare(seg) {
local p = JsonCursorBox.index_of_from(seg, "\"op\":\"compare\"", 0)
if p < 0 { return map({}) }
if p < 0 { return {} }
local lhs = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"lhs\":", p) + 6))
local rhs = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"rhs\":", p) + 6))
local dst = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"dst\":", p) + 6))
@ -33,17 +33,17 @@ static box StepRunnerBox {
local kpos = JsonCursorBox.index_of_from(seg, "\"cmp\":\"", p)
local kend = me._index_of_from(seg, "\"", kpos + 6)
local kind = seg.substring(kpos + 6, kend)
return map({ lhs: lhs, rhs: rhs, dst: dst, kind: kind })
return { lhs: lhs, rhs: rhs, dst: dst, kind: kind }
}
// branch の cond/then/else を抽出
parse_branch(seg) {
local p = JsonCursorBox.index_of_from(seg, "\"op\":\"branch\"", 0)
if p < 0 { return map({}) }
if p < 0 { return {} }
local cond = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"cond\":", p) + 7))
local then_id = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"then\":", p) + 7))
local else_id = me._int(me._read_digits(seg, JsonCursorBox.index_of_from(seg, "\"else\":", p) + 7))
return map({ cond: cond, then: then_id, else: else_id })
return { cond: cond, then: then_id, else: else_id }
}
// compare を評価し cond==dst のときに bool を返す