Bridge canonicalize: add compare/branch/jump diff tests; Map field bad-key stable tag with smoke; Update smokes README with diff canary policy
This commit is contained in:
@ -30,7 +30,13 @@ pub(super) fn try_handle_map_box(
|
||||
"MapBox.getField expects 1 arg".into(),
|
||||
));
|
||||
}
|
||||
let k = this.reg_load(args[0])?.to_nyash_box();
|
||||
let k_vm = this.reg_load(args[0])?;
|
||||
// Field access expects a String key; otherwise return a stable tag.
|
||||
if !matches!(k_vm, VMValue::String(_)) {
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); }
|
||||
return Ok(true);
|
||||
}
|
||||
let k = k_vm.to_nyash_box();
|
||||
let ret = mb.get(k);
|
||||
if let Some(d) = dst {
|
||||
this.regs.insert(d, VMValue::from_nyash_box(ret));
|
||||
@ -43,7 +49,12 @@ pub(super) fn try_handle_map_box(
|
||||
"MapBox.setField expects 2 args".into(),
|
||||
));
|
||||
}
|
||||
let k = this.reg_load(args[0])?.to_nyash_box();
|
||||
let k_vm = this.reg_load(args[0])?;
|
||||
if !matches!(k_vm, VMValue::String(_)) {
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); }
|
||||
return Ok(true);
|
||||
}
|
||||
let k = k_vm.to_nyash_box();
|
||||
let v = this.reg_load(args[1])?.to_nyash_box();
|
||||
let ret = mb.set(k, v);
|
||||
if let Some(d) = dst {
|
||||
|
||||
@ -40,6 +40,24 @@ pub(super) fn try_handle_object_fields(
|
||||
if args.len() != 1 {
|
||||
return Err(VMError::InvalidInstruction("getField expects 1 arg".into()));
|
||||
}
|
||||
// MapBox special-case: bridge to MapBox.get, with string-only key
|
||||
if let Ok(VMValue::BoxRef(bref)) = this.reg_load(box_val) {
|
||||
if bref.as_any().downcast_ref::<crate::boxes::map_box::MapBox>().is_some() {
|
||||
let key_vm = this.reg_load(args[0])?;
|
||||
if let VMValue::String(_) = key_vm {
|
||||
let k = key_vm.to_nyash_box();
|
||||
let map = bref.share_box();
|
||||
if let Some(mb) = map.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let ret = mb.get(k);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::from_nyash_box(ret)); }
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); }
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||
let rk = match this.reg_load(box_val) {
|
||||
Ok(VMValue::BoxRef(ref b)) => format!("BoxRef({})", b.type_name()),
|
||||
@ -282,6 +300,25 @@ pub(super) fn try_handle_object_fields(
|
||||
"setField expects 2 args".into(),
|
||||
));
|
||||
}
|
||||
// MapBox special-case: bridge to MapBox.set, with string-only key
|
||||
if let Ok(VMValue::BoxRef(bref)) = this.reg_load(box_val) {
|
||||
if bref.as_any().downcast_ref::<crate::boxes::map_box::MapBox>().is_some() {
|
||||
let key_vm = this.reg_load(args[0])?;
|
||||
if let VMValue::String(_) = key_vm {
|
||||
let k = key_vm.to_nyash_box();
|
||||
let v = this.reg_load(args[1])?.to_nyash_box();
|
||||
let map = bref.share_box();
|
||||
if let Some(mb) = map.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let _ = mb.set(k, v);
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::Void); }
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
if let Some(d) = dst { this.regs.insert(d, VMValue::String("[map/bad-key] field name must be string".to_string())); }
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
let fname = match this.reg_load(args[0])? {
|
||||
VMValue::String(s) => s,
|
||||
v => v.to_string(),
|
||||
|
||||
@ -82,6 +82,18 @@ tools/smokes/v2/
|
||||
- `SMOKES_ENABLE_CORE_CANARY=1` — Core interpreter canaries(emit→nyvm/core, Gate‑C Core)。
|
||||
- Stage‑B canaries are default‑ON in quick (`core/stageb/*`).
|
||||
- Selfhost Stage‑B helpers under `core/selfhost_stageb_*` remain opt‑in for dev.
|
||||
|
||||
Bridge canonicalize (diff canaries)
|
||||
- 目的: v1 JSON の ModuleFunction を Method へ決定的に正規化することを保証する。
|
||||
- ON/OFF/FAIL 規約:
|
||||
- ON: `HAKO_BRIDGE_INJECT_SINGLETON=1` で mutated JSON(dump)が生成され、`callee.type": "Method"` へ書き換わる。
|
||||
- OFF: 変異しない(dumpが生成されない or `ModuleFunction` のまま)。
|
||||
- FAIL: 無効な JSON/未対応のcallee などは Fail‑Fast(stderrに安定文言)。
|
||||
- 常時テスト(quick/core/bridge):
|
||||
- canonicalize_diff_on_off_vm.sh(LLVMPhiInstructionBox.lower_phi ベース)
|
||||
- canonicalize_array_len_on_off_vm.sh(ArrayBox.len → Method(ArrayBox.size))
|
||||
- canonicalize_map_len_on_off_vm.sh(MapBox.len → Method(MapBox.len))
|
||||
- canonicalize_static_lower_*(binop/compare/branch/jump/return)
|
||||
- `SMOKES_ENABLE_STAGEB_V1=1` — Stage‑B v1 互換カナリア(`selfhost_stageb_v1_compat_vm.sh`)。未配線時は SKIP。
|
||||
|
||||
## 🔧 テスト作成規約
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# canonicalize_static_lower_branch_on_off_vm.sh — Verify LLVMBranchInstructionBox.lower_branch rewrite
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
json_path="/tmp/ny_v1_lower_branch_$$.json"
|
||||
mut_on="/tmp/ny_v1_lower_branch_on_$$.json"
|
||||
mut_off="/tmp/ny_v1_lower_branch_off_$$.json"
|
||||
|
||||
cat >"$json_path" <<'JSON'
|
||||
{"schema_version":"1.0","functions":[{"name":"main","blocks":[{"id":0,"instructions":[{"op":"mir_call","mir_call":{"callee":{"type":"ModuleFunction","name":"LLVMBranchInstructionBox.lower_branch"},"args":[1]}},{"op":"ret"}]}]}]}
|
||||
JSON
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_BRIDGE_INJECT_SINGLETON=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_on" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ ! -f "$mut_on" ] || ! grep -q '"type":"Method"' "$mut_on" || ! grep -q '"box_name":"LLVMBranchInstructionBox"' "$mut_on" || ! grep -q '"method":"lower_branch"' "$mut_on"; then
|
||||
echo "[FAIL] canonicalize_static_lower_branch_on_off_vm (ON)" >&2; exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_off" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ -f "$mut_off" ] && ! grep -q '"type":"ModuleFunction"' "$mut_off"; then
|
||||
echo "[FAIL] canonicalize_static_lower_branch_on_off_vm (OFF)" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "[PASS] canonicalize_static_lower_branch_on_off_vm"
|
||||
rm -f "$json_path" "$mut_on" "$mut_off"
|
||||
exit 0
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# canonicalize_static_lower_compare_on_off_vm.sh — Verify LLVMCompareInstructionBox.lower_compare rewrite
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
json_path="/tmp/ny_v1_lower_compare_$$.json"
|
||||
mut_on="/tmp/ny_v1_lower_compare_on_$$.json"
|
||||
mut_off="/tmp/ny_v1_lower_compare_off_$$.json"
|
||||
|
||||
cat >"$json_path" <<'JSON'
|
||||
{"schema_version":"1.0","functions":[{"name":"main","blocks":[{"id":0,"instructions":[{"op":"mir_call","mir_call":{"callee":{"type":"ModuleFunction","name":"LLVMCompareInstructionBox.lower_compare"},"args":[1,2]}},{"op":"ret"}]}]}]}
|
||||
JSON
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_BRIDGE_INJECT_SINGLETON=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_on" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ ! -f "$mut_on" ] || ! grep -q '"type":"Method"' "$mut_on" || ! grep -q '"box_name":"LLVMCompareInstructionBox"' "$mut_on" || ! grep -q '"method":"lower_compare"' "$mut_on"; then
|
||||
echo "[FAIL] canonicalize_static_lower_compare_on_off_vm (ON)" >&2; exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_off" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ -f "$mut_off" ] && ! grep -q '"type":"ModuleFunction"' "$mut_off"; then
|
||||
echo "[FAIL] canonicalize_static_lower_compare_on_off_vm (OFF)" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "[PASS] canonicalize_static_lower_compare_on_off_vm"
|
||||
rm -f "$json_path" "$mut_on" "$mut_off"
|
||||
exit 0
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# canonicalize_static_lower_jump_on_off_vm.sh — Verify LLVMJumpInstructionBox.lower_jump rewrite
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
json_path="/tmp/ny_v1_lower_jump_$$.json"
|
||||
mut_on="/tmp/ny_v1_lower_jump_on_$$.json"
|
||||
mut_off="/tmp/ny_v1_lower_jump_off_$$.json"
|
||||
|
||||
cat >"$json_path" <<'JSON'
|
||||
{"schema_version":"1.0","functions":[{"name":"main","blocks":[{"id":0,"instructions":[{"op":"mir_call","mir_call":{"callee":{"type":"ModuleFunction","name":"LLVMJumpInstructionBox.lower_jump"},"args":[1]}},{"op":"ret"}]}]}]}
|
||||
JSON
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_BRIDGE_INJECT_SINGLETON=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_on" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ ! -f "$mut_on" ] || ! grep -q '"type":"Method"' "$mut_on" || ! grep -q '"box_name":"LLVMJumpInstructionBox"' "$mut_on" || ! grep -q '"method":"lower_jump"' "$mut_on"; then
|
||||
echo "[FAIL] canonicalize_static_lower_jump_on_off_vm (ON)" >&2; exit 1
|
||||
fi
|
||||
|
||||
set +e
|
||||
HAKO_NYVM_V1_DOWNCONVERT=1 HAKO_DEBUG_NYVM_BRIDGE_DUMP_MUT="$mut_off" \
|
||||
"$ROOT/target/release/nyash" --json-file "$json_path" >/dev/null 2>&1
|
||||
set -e || true
|
||||
if [ -f "$mut_off" ] && ! grep -q '"type":"ModuleFunction"' "$mut_off"; then
|
||||
echo "[FAIL] canonicalize_static_lower_jump_on_off_vm (OFF)" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "[PASS] canonicalize_static_lower_jump_on_off_vm"
|
||||
rm -f "$json_path" "$mut_on" "$mut_off"
|
||||
exit 0
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
# map_bad_key_field_vm.sh — Map.getField/ setField with non-string key returns [map/bad-key]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||
ROOT="$ROOT_GIT"
|
||||
else
|
||||
ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"
|
||||
fi
|
||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
code='static box Main { main() { local m=new MapBox(); print(m.getField(123)); m.setField(456, 1); return 0 } }'
|
||||
out=$(run_nyash_vm -c "$code")
|
||||
if echo "$out" | grep -q "\[map/bad-key\]"; then
|
||||
echo "[PASS] map_bad_key_field_vm"
|
||||
exit 0
|
||||
else
|
||||
echo "[FAIL] map_bad_key_field_vm" >&2
|
||||
echo "--- output ---" >&2
|
||||
echo "$out" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user