Fix VM: string handler no longer hijacks length() on non-strings; ArrayBox.length returns correct values (fixes json_lint loop). Add string-literal reader init guard earlier

This commit is contained in:
nyash-codex
2025-11-01 19:26:49 +09:00
parent 756af0da6c
commit 25b6bd3ae1
3 changed files with 30 additions and 7 deletions

View File

@ -293,6 +293,11 @@ box JsonScanner {
// 文字列リテラルを読み取り(クォート含む) // 文字列リテラルを読み取り(クォート含む)
read_string_literal() { read_string_literal() {
local start_pos = me.position local start_pos = me.position
// Save starting position to _tmp_pos so that substring/range checks
// do not depend on a previous readers value (PHI-safe, loop-safe).
// Other high-level readers (read_number/read_identifier) already
// initialize _tmp_pos; string literal must do the same.
me._tmp_pos = me.position
// 開始クォート // 開始クォート
if me.current() != "\"" { if me.current() != "\"" {

View File

@ -12,13 +12,21 @@ pub(super) fn try_handle_string_box(
eprintln!("[vm-trace] try_handle_string_box(method={})", method); eprintln!("[vm-trace] try_handle_string_box(method={})", method);
} }
let recv = this.reg_load(box_val)?; let recv = this.reg_load(box_val)?;
// Normalize receiver to trait-level StringBox to bridge old/new StringBox implementations // Handle ONLY when the receiver is actually a string.
let sb_norm: crate::box_trait::StringBox = match recv.clone() { // Do NOT coerce arbitrary boxes to StringBox (e.g., ArrayBox.length()).
VMValue::String(s) => crate::box_trait::StringBox::new(s), let sb_norm_opt: Option<crate::box_trait::StringBox> = match recv.clone() {
VMValue::BoxRef(b) => b.to_string_box(), VMValue::String(s) => Some(crate::box_trait::StringBox::new(s)),
other => other.to_nyash_box().to_string_box(), VMValue::BoxRef(b) => {
if b.as_any().downcast_ref::<crate::box_trait::StringBox>().is_some() {
Some(b.to_string_box())
} else {
None
}
}
_ => None,
}; };
// Only handle known string methods here let Some(sb_norm) = sb_norm_opt else { return Ok(false) };
// Only handle known string methods here (receiver is confirmed string)
match method { match method {
"length" => { "length" => {
let ret = sb_norm.length(); let ret = sb_norm.length();

View File

@ -120,7 +120,17 @@ pub fn seal_incomplete_phis_with<O: LoopPhiOps>(
let value_after = ops let value_after = ops
.get_variable_at_block(&phi.var_name, latch_id) .get_variable_at_block(&phi.var_name, latch_id)
.ok_or_else(|| format!("Variable {} not found at latch block", phi.var_name))?; .ok_or_else(|| format!("Variable {} not found at latch block", phi.var_name))?;
phi.known_inputs.push((latch_id, value_after));
// 🔧 ループ不変変数の自己参照PHI問題を修正
// value_afterがPHI自身の場合ループ内で変更されていない変数は、
// preheaderの値を使用する
let latch_value = if value_after == phi.phi_id {
// ループ不変変数preheaderの値を使用
phi.known_inputs[0].1 // preheaderからの初期値
} else {
value_after
};
phi.known_inputs.push((latch_id, latch_value));
ops.debug_verify_phi_inputs(block_id, &phi.known_inputs); ops.debug_verify_phi_inputs(block_id, &phi.known_inputs);
ops.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?; ops.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;