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(),
|
"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);
|
let ret = mb.get(k);
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
this.regs.insert(d, VMValue::from_nyash_box(ret));
|
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(),
|
"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 v = this.reg_load(args[1])?.to_nyash_box();
|
||||||
let ret = mb.set(k, v);
|
let ret = mb.set(k, v);
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
|
|||||||
@ -40,6 +40,24 @@ pub(super) fn try_handle_object_fields(
|
|||||||
if args.len() != 1 {
|
if args.len() != 1 {
|
||||||
return Err(VMError::InvalidInstruction("getField expects 1 arg".into()));
|
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") {
|
if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
|
||||||
let rk = match this.reg_load(box_val) {
|
let rk = match this.reg_load(box_val) {
|
||||||
Ok(VMValue::BoxRef(ref b)) => format!("BoxRef({})", b.type_name()),
|
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(),
|
"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])? {
|
let fname = match this.reg_load(args[0])? {
|
||||||
VMValue::String(s) => s,
|
VMValue::String(s) => s,
|
||||||
v => v.to_string(),
|
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)。
|
- `SMOKES_ENABLE_CORE_CANARY=1` — Core interpreter canaries(emit→nyvm/core, Gate‑C Core)。
|
||||||
- Stage‑B canaries are default‑ON in quick (`core/stageb/*`).
|
- Stage‑B canaries are default‑ON in quick (`core/stageb/*`).
|
||||||
- Selfhost Stage‑B helpers under `core/selfhost_stageb_*` remain opt‑in for dev.
|
- 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。
|
- `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