diff --git a/lang/src/llvm_ir/boxes/aot_prep.hako b/lang/src/llvm_ir/boxes/aot_prep.hako index e2ed06ff..2c25583b 100644 --- a/lang/src/llvm_ir/boxes/aot_prep.hako +++ b/lang/src/llvm_ir/boxes/aot_prep.hako @@ -5,14 +5,15 @@ // - JSON(MIR v0) の軽量正規化(キー順/冗長キー削除)と安全な const/binop(+,-,*)/ret の単一ブロック畳み込み // - 既定ではパススルー(Rust 側 maybe_prepare_mir_json が実体)。段階的にこちらへ移管する -using selfhost.shared.mir.io as MirIoBox using selfhost.shared.common.string_helpers as StringHelpers using selfhost.shared.json.utils.json_frag as JsonFragBox using selfhost.llvm.ir.aot_prep.helpers.common as AotPrepHelpers -// Modular normalizers (opt-in, default OFF) -using selfhost.llvm.ir.normalize.print as NormalizePrintBox -using selfhost.llvm.ir.normalize.ref as NormalizeRefBox -using selfhost.llvm.ir.normalize.array_legacy as NormalizeArrayLegacyBox +// Modular normalizers(opt‑in, default OFF) +// Note: To minimize parser surface, provide identity stubs here. +// Real normalizers can be wired via using in higher layers when needed. +static box NormalizePrintBox { run(json) { return json } } +static box NormalizeRefBox { run(json) { return json } } +static box NormalizeArrayLegacyBox { run(json) { return json } } // Externalized core passes (Stage-1: wrappers; impl move is incremental) using selfhost.llvm.ir.aot_prep.passes.strlen as AotPrepStrlenBox using selfhost.llvm.ir.aot_prep.passes.loop_hoist as AotPrepLoopHoistBox @@ -42,8 +43,8 @@ static box AotPrepBox { if !src { return json_in_path } // Phase‑1: 文字列正規化(安定化) - // いまは canonicalize は恒等(将来はHostBridgeでキー順安定化) - local canon = MirIoBox.normalize(src) + // 現状は恒等(将来はHostBridgeでキー順安定化) + local canon = "" + src // Phase‑2: 安全な単一ブロック const/binop(+,-,*)/ret の畳み込み(最小実装) // 備考: まずは main 関数を優先対象とし、成立時のみ最小 JSON に置換(今後は in‑place 置換へ段階移行)。 @@ -102,7 +103,7 @@ static box AotPrepBox { local needle = "\"dst\":" + vid local pos = JsonFragBox.index_of_from(json, needle, 0) while pos >= 0 { - local start = json.lastIndexOf("{", pos) + local start = json.substring(0, pos).lastIndexOf("{") if start < 0 { break } local end = AotPrepBox._seek_object_end(json, start) if end < 0 { break } @@ -128,7 +129,7 @@ static box AotPrepBox { local needle = "\"dst\":" + vid local pos = JsonFragBox.index_of_from(json, needle, 0) while pos >= 0 { - local start = json.lastIndexOf("{", pos) + local start = json.substring(0, pos).lastIndexOf("{") if start < 0 { break } local end = AotPrepBox._seek_object_end(json, start) if end < 0 { break } @@ -171,223 +172,124 @@ static box AotPrepBox { return false } - // ループ内 mod/div/compare の定数をブロック先頭へ hoist(JSON文字列ベース、構造は変えない) - local read_field = fun(text, key) { - local needle = "\"" + key + "\":\"" - local idx = text.indexOf(needle) - if idx < 0 { return "" } - return JsonFragBox.read_string_after(text, idx + needle.length()) - } - local read_digits_field = fun(text, key) { - local needle = "\"" + key + "\":" - local idx = text.indexOf(needle) - if idx < 0 { return "" } - return StringHelpers.read_digits(text, idx + needle.length()) - } - local read_const_value = fun(text) { - local needle = "\"value\":{\"type\":\"i64\",\"value\":" - local idx = text.indexOf(needle) - if idx < 0 { return "" } - return StringHelpers.read_digits(text, idx + needle.length()) - } - local ref_fields = ["lhs", "rhs", "cond", "target"] - loop(true) { - local key = "\"instructions\":[" - local kinst = out.indexOf(key, pos) - if kinst < 0 { break } - local lb = out.indexOf("[", kinst) - if lb < 0 { break } - local rb = JsonFragBox._seek_array_end(out, lb) - if rb < 0 { break } - local body = out.substring(lb+1, rb) - local insts = build_items(body) - local const_defs = {} - local const_vals = {} - for inst in insts { - local op = read_field(inst, "op") - if op == "const" { - local dst = read_digits_field(inst, "dst") - local val = read_const_value(inst) - if dst != "" && val != "" { - const_defs[dst] = inst - const_vals[dst] = val - } - } - } - - local folded = true - while folded { - folded = false - for inst in insts { - local op = read_field(inst, "op") - if op != "binop" { continue } - local dst = read_digits_field(inst, "dst") - if dst == "" || const_vals.contains(dst) { continue } - local lhs = read_digits_field(inst, "lhs") - local rhs = read_digits_field(inst, "rhs") - local operation = read_field(inst, "operation") - if lhs == "" || rhs == "" || operation == "" { continue } - local lhs_val = const_vals.contains(lhs) ? const_vals[lhs] : "" - local rhs_val = const_vals.contains(rhs) ? const_vals[rhs] : "" - if lhs_val == "" || rhs_val == "" { continue } - local computed = AotPrepHelpers.evaluate_binop_constant(operation, lhs_val, rhs_val) - if computed == "" { continue } - const_defs[dst] = inst - const_vals[dst] = computed - folded = true - } - } - - local needed = {} - for inst in insts { - local op = read_field(inst, "op") - if op == "const" { continue } - for field in ref_fields { - local ref = read_digits_field(inst, field) - if ref != "" && const_defs.contains(ref) { - needed[ref] = true - } - } - } - - if needed.size() == 0 { - pos = rb + 1 - continue - } - - local hoist_items = [] - local keep_items = [] - for inst in insts { - local dst = read_digits_field(inst, "dst") - if dst != "" && needed.contains(dst) && const_defs.contains(dst) { - hoist_items.push(inst) - continue - } - keep_items.push(inst) - } - - if hoist_items.size() == 0 { - pos = rb + 1 - continue - } - - local merged = "" - local first = 1 - local append_item = fun(item) { - if first == 0 { merged = merged + "," } - merged = merged + item - first = 0 - } - for item in hoist_items { append_item(item) } - for item in keep_items { append_item(item) } - - out = out.substring(0, lb+1) + merged + out.substring(rb, out.length()) - pos = lb + merged.length() + 1 - } - return out - } - // evaluate_binop_constant is provided by AotPrepHelpers // 内部: 最小の安全畳み込み(JSON文字列ベース) + _find_instr_span_from(s, start_from) { + local key = "\"instructions\":[" + local pos = s.indexOf(key, start_from) + if pos < 0 { return "" } + local ls = s.indexOf("[", pos) + if ls < 0 { return "" } + local depth = 0 + local i = ls + local L = s.length() + local rs = -1 + loop(i < L) { + local ch = s.substring(i, i+1) + if ch == "[" { depth = depth + 1 } + if ch == "]" { depth = depth - 1 if depth == 0 { rs = i break } } + i = i + 1 + } + if rs < 0 { return "" } + return StringHelpers.int_to_str(ls) + ":" + StringHelpers.int_to_str(rs) + } + + _parse_dst(ss, pos) { + local k = "\"dst\":" + local i = ss.indexOf(k, pos) + if i < 0 { return -1 } + i = i + k.length() + local digs = StringHelpers.read_digits(ss, i) + if digs == "" { return -1 } + return StringHelpers.to_i64(digs) + } + + _parse_val(ss, pos) { + local k = "\"value\":{\"type\":\"i64\",\"value\":" + local i = ss.indexOf(k, pos) + if i < 0 { return null } + i = i + k.length() + local digs = StringHelpers.read_digits(ss, i) + if digs == "" { return null } + return StringHelpers.to_i64(digs) + } + + _find_num(ss, key, pos) { + local k = key + local i = ss.indexOf(k, pos) + if i < 0 { return -1 } + i = i + k.length() + local digs = StringHelpers.read_digits(ss, i) + if digs == "" { return -1 } + return StringHelpers.to_i64(digs) + } + + _find_op(ss, pos) { + local k = "\"operation\":\"" + local i = ss.indexOf(k, pos) + if i < 0 { return "" } + i = i + k.length() + local j = ss.indexOf("\"", i) + if j < 0 { return "" } + return ss.substring(i, j) + } + + _try_fold_in_span(s, arr_start, arr_end) { + if arr_start < 0 || arr_end < 0 { return null } + local body = s.substring(arr_start, arr_end+1) + // Need two const, a binop, and a ret in this span + local p1 = body.indexOf("\"op\":\"const\"") + local p2 = body.indexOf("\"op\":\"const\"", (p1>=0 ? (p1+1) : 0)) + local pb = body.indexOf("\"op\":\"binop\"", (p2>=0 ? (p2+1) : 0)) + local pr = body.indexOf("\"op\":\"ret\"", (pb>=0 ? (pb+1) : 0)) + if p1 < 0 || p2 < 0 || pb < 0 || pr < 0 { return null } + // parse + local d1 = AotPrepBox._parse_dst(body, p1) + local a = AotPrepBox._parse_val(body, p1) + local d2 = AotPrepBox._parse_dst(body, p2) + local b = AotPrepBox._parse_val(body, p2) + if d1 < 0 || d2 < 0 || a == null || b == null { return null } + local lhs = AotPrepBox._find_num(body, "\"lhs\":", pb) + local rhs = AotPrepBox._find_num(body, "\"rhs\":", pb) + local bop = AotPrepBox._find_op(body, pb) + local d3 = AotPrepBox._find_num(body, "\"dst\":", pb) + if lhs != d1 || rhs != d2 || d3 < 0 { return null } + local rv = AotPrepBox._find_num(body, "\"value\":", pr) + if rv != d3 { return null } + // binop allowed: +,-,* only + local res = 0 + if bop == "+" { res = a + b } + else { if bop == "-" { res = a - b } else { if bop == "*" { res = a * b } else { return null } } } + // build new array and replace in-place + local new_insts = "[" + + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + StringHelpers.int_to_str(d1) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + StringHelpers.int_to_str(res) + "}}," + + "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":" + StringHelpers.int_to_str(d1) + "}]" + local head = s.substring(0, arr_start) + local tail = s.substring(arr_end + 1, s.length()) + return head + new_insts + tail + } + _try_fold_const_binop_ret(json) { if !json { return null } - // Helper: find the [ ... ] span of the first block's instructions for the function near `start_from`. - local find_instr_span_from = fun(s, start_from) { - local key = "\"instructions\":[" - local pos = s.indexOf(key, start_from) - if pos < 0 { return [-1, -1] } - local ls = s.indexOf("[", pos) - if ls < 0 { return [-1, -1] } - local depth = 0 - local i = ls - local L = s.length() - local rs = -1 - loop(i < L) { - local ch = s.substring(i, i+1) - if ch == "[" { depth = depth + 1 } - if ch == "]" { depth = depth - 1; if depth == 0 { rs = i; break } } - i = i + 1 - } - return [ls, rs] - } - // Helper: attempt to fold within a given [arr_start, arr_end] span; return replaced JSON on success - local try_fold_in_span = fun(s, arr_start, arr_end) { - if arr_start < 0 || arr_end < 0 { return null } - local body = s.substring(arr_start, arr_end+1) - // Need two const, a binop, and a ret in this span - local p1 = body.indexOf("\"op\":\"const\"") - local p2 = body.indexOf("\"op\":\"const\"", (p1>=0 ? (p1+1) : 0)) - local pb = body.indexOf("\"op\":\"binop\"", (p2>=0 ? (p2+1) : 0)) - local pr = body.indexOf("\"op\":\"ret\"", (pb>=0 ? (pb+1) : 0)) - if p1 < 0 || p2 < 0 || pb < 0 || pr < 0 { return null } - // parse helpers within body - local parse_dst = fun(ss, pos) { - local k = "\"dst\":" - local i = ss.indexOf(k, pos) - if i < 0 { return -1 } - i = i + k.length() - local digs = StringHelpers.read_digits(ss, i) - if digs == "" { return -1 } - return StringHelpers.to_i64(digs) - } - local parse_val = fun(ss, pos) { - local k = "\"value\":{\"type\":\"i64\",\"value\":" - local i = ss.indexOf(k, pos) - if i < 0 { return null } - i = i + k.length() - local digs = StringHelpers.read_digits(ss, i) - if digs == "" { return null } - return StringHelpers.to_i64(digs) - } - local d1 = parse_dst(body, p1) - local a = parse_val(body, p1) - local d2 = parse_dst(body, p2) - local b = parse_val(body, p2) - if d1 < 0 || d2 < 0 || a == null || b == null { return null } - local find_num = fun(ss, key, pos) { - local k = key - local i = ss.indexOf(k, pos) - if i < 0 { return -1 } - i = i + k.length() - local digs = StringHelpers.read_digits(ss, i) - if digs == "" { return -1 } - return StringHelpers.to_i64(digs) - } - local find_op = fun(ss, pos) { - local k = "\"operation\":\"" - local i = ss.indexOf(k, pos) - if i < 0 { return "" } - i = i + k.length() - local j = ss.indexOf("\"", i) - if j < 0 { return "" } - return ss.substring(i, j) - } - local lhs = find_num(body, "\"lhs\":", pb) - local rhs = find_num(body, "\"rhs\":", pb) - local bop = find_op(body, pb) - local d3 = find_num(body, "\"dst\":", pb) - if lhs != d1 || rhs != d2 || d3 < 0 { return null } - local rv = find_num(body, "\"value\":", pr) - if rv != d3 { return null } - // binop allowed: +,-,* only - local res = 0 - if bop == "+" { res = a + b } else { if bop == "-" { res = a - b } else { if bop == "*" { res = a * b } else { return null } } } - // build new array and replace in-place - local new_insts = "[" + - "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + StringHelpers.int_to_str(d1) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + StringHelpers.int_to_str(res) + "}}," + - "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":" + StringHelpers.int_to_str(d1) + "}]" - local head = s.substring(0, arr_start) - local tail = s.substring(arr_end + 1, s.length()) - return head + new_insts + tail - } // Pass 1: prefer name:"main" local fn_pos = json.indexOf("\"name\":\"main\"") if fn_pos >= 0 { - local span = find_instr_span_from(json, fn_pos) - local ls = span[0]; local rs = span[1] - local repl = try_fold_in_span(json, ls, rs) - if repl { return repl } + local span = AotPrepBox._find_instr_span_from(json, fn_pos) + local ls = -1; local rs = -1 + if span != null && span != "" { + local sep = span.indexOf(":") + if sep >= 0 { + local s_ls = span.substring(0, sep) + local s_rs = span.substring(sep+1, span.length()) + ls = StringHelpers.to_i64(s_ls) + rs = StringHelpers.to_i64(s_rs) + } + } + if ls != null && rs != null && ls >= 0 && rs >= 0 { + local repl = AotPrepBox._try_fold_in_span(json, ls, rs) + if repl { return repl } + } } // Pass 2: scan functions sequentially and attempt per function local froot = json.indexOf("\"functions\":[") @@ -397,10 +299,19 @@ static box AotPrepBox { loop(tries < 16) { local np = json.indexOf("\"name\":\"", scan+1) if np < 0 { break } - local span2 = find_instr_span_from(json, np) - local ls2 = span2[0]; local rs2 = span2[1] - if ls2 >= 0 && rs2 >= 0 { - local repl2 = try_fold_in_span(json, ls2, rs2) + local span2 = AotPrepBox._find_instr_span_from(json, np) + local ls2 = -1; local rs2 = -1 + if span2 != null && span2 != "" { + local sep2 = span2.indexOf(":") + if sep2 >= 0 { + local s_ls2 = span2.substring(0, sep2) + local s_rs2 = span2.substring(sep2+1, span2.length()) + ls2 = StringHelpers.to_i64(s_ls2) + rs2 = StringHelpers.to_i64(s_rs2) + } + } + if ls2 != null && rs2 != null && ls2 >= 0 && rs2 >= 0 { + local repl2 = AotPrepBox._try_fold_in_span(json, ls2, rs2) if repl2 { return repl2 } scan = rs2 + 1 } else { @@ -445,7 +356,7 @@ static box AotPrepBox { run_json(src_json) { if src_json == null { return null } // Phase‑1: 文字列正規化(安定化) - local out = MirIoBox.normalize("" + src_json) + local out = "" + src_json // Phase‑2: 安全な単一ブロック const/binop(+,-,*)/ret の畳み込み(最小実装) local tmp = AotPrepBox._try_fold_const_binop_ret(out) diff --git a/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako b/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako index 93d50b23..e89ec979 100644 --- a/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako +++ b/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako @@ -40,7 +40,7 @@ static box AotPrepHelpers { local needle = "\"dst\":" + vid local pos = JsonFragBox.index_of_from(json, needle, 0) while pos >= 0 { - local start = json.lastIndexOf("{", pos) + local start = json.substring(0, pos).lastIndexOf("{") if start < 0 { break } local end = me._seek_object_end(json, start) if end < 0 { break } @@ -67,7 +67,7 @@ static box AotPrepHelpers { local needle = "\"dst\":" + vid local pos = JsonFragBox.index_of_from(json, needle, 0) while pos >= 0 { - local start = json.lastIndexOf("{", pos) + local start = json.substring(0, pos).lastIndexOf("{") if start < 0 { break } local end = me._seek_object_end(json, start) if end < 0 { break } diff --git a/lang/src/llvm_ir/boxes/aot_prep/passes/collections_hot.hako b/lang/src/llvm_ir/boxes/aot_prep/passes/collections_hot.hako index 1e5fde6d..781a01b1 100644 --- a/lang/src/llvm_ir/boxes/aot_prep/passes/collections_hot.hako +++ b/lang/src/llvm_ir/boxes/aot_prep/passes/collections_hot.hako @@ -299,7 +299,8 @@ static box AotPrepCollectionsHotBox { } } } - p = slice.lastIndexOf("\"op\":\"boxcall\"", p-1) + local prefix = slice.substring(0, p) + p = prefix.lastIndexOf("\"op\":\"boxcall\"") } return "" } @@ -325,7 +326,8 @@ static box AotPrepCollectionsHotBox { } } } - p = slice.lastIndexOf("\"op\":\"boxcall\"", p-1) + local prefix = slice.substring(0, p) + p = prefix.lastIndexOf("\"op\":\"boxcall\"") } return "" } diff --git a/lang/src/llvm_ir/boxes/aot_prep/passes/strlen.hako b/lang/src/llvm_ir/boxes/aot_prep/passes/strlen.hako index 71b99745..82928499 100644 --- a/lang/src/llvm_ir/boxes/aot_prep/passes/strlen.hako +++ b/lang/src/llvm_ir/boxes/aot_prep/passes/strlen.hako @@ -7,7 +7,7 @@ static box AotPrepStrlenBox { run(json) { if json == null { return null } // 1) const string: vid -> byte length - local map_len = {} + local map_len = new MapBox() { local pos = 0 loop(true){ @@ -26,7 +26,7 @@ static box AotPrepStrlenBox { local lit = JsonFragBox.read_string_after(json, kv+8) if lit != null { local blen = (""+lit).length() - map_len[StringHelpers.int_to_str(StringHelpers.to_i64(kid))] = blen + map_len.set(StringHelpers.int_to_str(StringHelpers.to_i64(kid)), blen) } } } @@ -37,7 +37,7 @@ static box AotPrepStrlenBox { } if map_len.size() == 0 { return json } // 2) newbox(StringBox, ): dst_vid -> byte length - local recv_len = {} + local recv_len = new MapBox() { local pos2 = 0 loop(true){ @@ -50,8 +50,8 @@ static box AotPrepStrlenBox { local kargs = JsonFragBox.index_of_from(json, "\"args\":[", k) if dsts != "" && kargs >= 0 { local avid = StringHelpers.read_digits(json, kargs+8) - if avid != "" && map_len.contains(avid) { - recv_len[dsts] = map_len[avid] + if avid != "" && map_len.has(avid) { + recv_len.set(dsts, map_len.get(avid)) } } } @@ -72,16 +72,16 @@ static box AotPrepStrlenBox { local kbox = JsonFragBox.index_of_from(out, "\"box\":", k) if kbox < 0 { pos3 = k + 1; continue } local bvid = StringHelpers.read_digits(out, kbox+6) - if bvid == "" || !recv_len.contains(bvid) { pos3 = k + 1; continue } + if bvid == "" || !recv_len.has(bvid) { pos3 = k + 1; continue } local kdst = JsonFragBox.index_of_from(out, "\"dst\":", k) if kdst < 0 { pos3 = k + 1; continue } local dvid = StringHelpers.read_digits(out, kdst+6) if dvid == "" { pos3 = k + 1; continue } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos3 = k + 1; continue } local obj_end = AotPrepHelpers._seek_object_end(out, obj_start) if obj_end < 0 { pos3 = k + 1; continue } - local blen = recv_len[bvid] + local blen = recv_len.get(bvid) local repl = "{\"op\":\"const\",\"dst\":" + dvid + ",\"value\":{\"type\":\"i64\",\"value\":" + StringHelpers.int_to_str(blen) + "}}" out = out.substring(0, obj_start) + repl + out.substring(obj_end+1, out.length()) pos3 = obj_start + repl.length() diff --git a/lang/src/llvm_ir/boxes/normalize/normalize_array_legacy.hako b/lang/src/llvm_ir/boxes/normalize/normalize_array_legacy.hako index 0fb678c6..e895f662 100644 --- a/lang/src/llvm_ir/boxes/normalize/normalize_array_legacy.hako +++ b/lang/src/llvm_ir/boxes/normalize/normalize_array_legacy.hako @@ -36,7 +36,7 @@ static box NormalizeArrayLegacyBox { loop(true) { local k = JsonFragBox.index_of_from(out, "\"op\":\"array_get\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } @@ -61,7 +61,7 @@ static box NormalizeArrayLegacyBox { loop(true) { local k = JsonFragBox.index_of_from(out, "\"op\":\"array_set\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } @@ -85,7 +85,7 @@ static box NormalizeArrayLegacyBox { loop(true) { local k = JsonFragBox.index_of_from(out, "\"op\":\"map_get\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } @@ -107,7 +107,7 @@ static box NormalizeArrayLegacyBox { loop(true) { local k = JsonFragBox.index_of_from(out, "\"op\":\"map_set\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } diff --git a/lang/src/llvm_ir/boxes/normalize/normalize_print.hako b/lang/src/llvm_ir/boxes/normalize/normalize_print.hako index 331de212..98f94d6b 100644 --- a/lang/src/llvm_ir/boxes/normalize/normalize_print.hako +++ b/lang/src/llvm_ir/boxes/normalize/normalize_print.hako @@ -36,7 +36,7 @@ static box NormalizePrintBox { local k = JsonFragBox.index_of_from(out, "\"op\":\"print\"", pos) if k < 0 { break } // Find enclosing object span - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } diff --git a/lang/src/llvm_ir/boxes/normalize/normalize_ref.hako b/lang/src/llvm_ir/boxes/normalize/normalize_ref.hako index 7c43b576..21e124b2 100644 --- a/lang/src/llvm_ir/boxes/normalize/normalize_ref.hako +++ b/lang/src/llvm_ir/boxes/normalize/normalize_ref.hako @@ -35,7 +35,7 @@ static box NormalizeRefBox { // ref_get local k = JsonFragBox.index_of_from(out, "\"op\":\"ref_get\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue } @@ -65,7 +65,7 @@ static box NormalizeRefBox { loop(true) { local k = JsonFragBox.index_of_from(out, "\"op\":\"ref_set\"", pos) if k < 0 { break } - local obj_start = out.lastIndexOf("{", k) + local obj_start = out.substring(0, k).lastIndexOf("{") if obj_start < 0 { pos = k + 1; continue } local obj_end = me._seek_object_end(out, obj_start) if obj_end < 0 { pos = k + 1; continue }