2025-09-24 09:30:42 +09:00
#!/bin/bash
# test_runner.sh - 中核実行器(強制使用)
# スモークテストv2の核心ライブラリ
# set -eは使わない( 個々のテストが失敗しても全体を続行するため)
set -uo pipefail
2025-09-24 21:45:27 +09:00
# ルート/バイナリ検出( CWDに依存しない実行を保証)
if [ -z " ${ NYASH_ROOT :- } " ] ; then
export NYASH_ROOT = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) /../../../.. " && pwd ) "
fi
2025-11-02 17:50:06 +09:00
# Prefer hakorune binary if exists; fallback to nyash for compatibility
if [ -z " ${ NYASH_BIN :- } " ] ; then
if [ -x " $NYASH_ROOT /target/release/hakorune " ] ; then
export NYASH_BIN = " $NYASH_ROOT /target/release/hakorune "
else
export NYASH_BIN = " $NYASH_ROOT /target/release/nyash "
fi
fi
2025-09-24 21:45:27 +09:00
2025-11-30 14:30:28 +09:00
# Stage-3 is default: prefer feature flag instead of legacy parser envs.
export NYASH_FEATURES = " ${ NYASH_FEATURES :- stage3 } "
2025-11-08 23:45:29 +09:00
# Debug convenience: HAKO_DEBUG=1 enables execution trace and log passthrough
if [ " ${ HAKO_DEBUG :- 0 } " = "1" ] ; then
export HAKO_TRACE_EXECUTION = 1
export HAKO_VERIFY_SHOW_LOGS = 1
fi
2025-11-13 16:40:58 +09:00
# Tag silence toggle (default: silent=1)
# - HAKO_SILENT_TAGS=1 → filter noisy tag lines (default)
# - HAKO_SILENT_TAGS=0 → show raw logs (no filtering)
export HAKO_SILENT_TAGS = " ${ HAKO_SILENT_TAGS :- 1 } "
2025-09-24 09:30:42 +09:00
# グローバル変数
export SMOKES_V2_LIB_LOADED = 1
export SMOKES_START_TIME = $( date +%s.%N)
export SMOKES_TEST_COUNT = 0
export SMOKES_PASS_COUNT = 0
export SMOKES_FAIL_COUNT = 0
2025-11-03 23:21:48 +09:00
export SMOKES_INCLUDE_SKIP_COUNT = 0
declare -a SMOKES_INCLUDE_SKIP_LIST = ( )
2025-09-24 09:30:42 +09:00
# 色定義(重複回避)
if [ -z " ${ RED :- } " ] ; then
readonly RED = '\033[0;31m'
readonly GREEN = '\033[0;32m'
readonly YELLOW = '\033[1;33m'
readonly BLUE = '\033[0;34m'
readonly NC = '\033[0m' # No Color
fi
# ログ関数
log_info( ) {
echo -e " ${ BLUE } [INFO] ${ NC } $* " >& 2
}
log_success( ) {
echo -e " ${ GREEN } [PASS] ${ NC } $* " >& 2
}
log_warn( ) {
echo -e " ${ YELLOW } [WARN] ${ NC } $* " >& 2
}
log_error( ) {
echo -e " ${ RED } [FAIL] ${ NC } $* " >& 2
}
2025-09-25 06:15:22 +09:00
# 共通ノ イズフィルタ( VM実行時の出力整形)
2025-11-10 23:17:46 +09:00
filter_noise( ) {
2025-11-13 16:40:58 +09:00
# Show raw logs (no filtering) to allow call traces / diagnostics
if [ " ${ HAKO_SHOW_CALL_LOGS :- 0 } " = "1" ] || [ " ${ HAKO_SILENT_TAGS } " = "0" ] ; then
2025-11-09 15:11:18 +09:00
cat
return
fi
2025-09-25 06:15:22 +09:00
# プラグイン初期化やメタログ、動的ローダの案内等を除去
grep -v "^\[UnifiedBoxRegistry\]" \
| grep -v "^\[FileBox\]" \
2025-11-09 15:11:18 +09:00
| grep -v "^\[provider-registry\]" \
2025-11-10 23:17:46 +09:00
| grep -v "^\[provider/select:" \
| grep -v "^\[deprecate/env\]" \
2025-11-09 15:11:18 +09:00
| grep -v "^\[plugin/missing\]" \
| grep -v "^\[plugin/hint\]" \
2025-09-25 06:15:22 +09:00
| grep -v "^Net plugin:" \
2025-11-09 15:11:18 +09:00
| grep -v "^\[.*\] Plugin" \
2025-09-25 06:15:22 +09:00
| grep -v "Using builtin StringBox" \
2025-09-26 03:30:59 +09:00
| grep -v "Using builtin ArrayBox" \
| grep -v "Using builtin MapBox" \
2025-09-26 14:34:42 +09:00
| grep -v "^\[using\]" \
| grep -v "^\[using/resolve\]" \
2025-11-09 15:11:18 +09:00
| grep -v "^\[using/text-merge\]" \
2025-09-26 14:34:42 +09:00
| grep -v "^\[builder\]" \
2025-09-27 08:45:25 +09:00
| grep -v "^\\[vm-trace\\]" \
2025-11-30 14:30:28 +09:00
| grep -v "^\\[DEBUG/" \
| grep -v "^\\[ssa-undef-debug\\]" \
2025-11-13 16:40:58 +09:00
| grep -v '^\[PluginBoxFactory\]' \
| grep -v '^\[using.dylib/autoload\]' \
2025-11-09 15:11:18 +09:00
| grep -v "^\[vm\] Stage-3" \
2025-11-01 17:39:36 +09:00
| grep -v "^\[DEBUG\]" \
2025-11-09 15:11:18 +09:00
| grep -v '^\{"ev":' \
2025-11-04 16:33:04 +09:00
| grep -v '^\[warn\]' \
| grep -v '^\[error\]' \
2025-11-10 23:17:46 +09:00
| grep -v '^RC: ' \
| grep -v '^\[mirbuilder/normalize:' \
2025-09-28 01:33:58 +09:00
| grep -v '^\[warn\] dev fallback: user instance BoxCall' \
2025-09-27 08:45:25 +09:00
| sed -E 's/^❌ VM fallback error: *//' \
2025-09-28 01:33:58 +09:00
| grep -v '^\[warn\] dev verify: NewBox ' \
| grep -v '^\[warn\] dev verify: NewBox→birth invariant warnings:' \
2025-11-02 15:43:43 +09:00
| grep -v '^\[ny-compiler\]' \
| grep -v '^\[using/cache\]' \
2025-09-26 03:30:59 +09:00
| grep -v "plugins/nyash-array-plugin" \
| grep -v "plugins/nyash-map-plugin" \
2025-09-25 06:15:22 +09:00
| grep -v "Phase 15.5: Everything is Plugin" \
| grep -v "cargo build -p nyash-string-plugin" \
| grep -v "^\[plugin-loader\] backend=" \
| grep -v "^\[using\] ctx:" \
| grep -v "^🔌 plugin host initialized" \
| grep -v "^✅ plugin host fully configured" \
| grep -v "Failed to load nyash.toml - plugins disabled" \
2025-11-06 15:41:52 +09:00
| grep -v "^⚠️ Failed to load plugin config (hakorune.toml/nyash.toml) - plugins disabled" \
2025-11-02 17:50:06 +09:00
| grep -v "^🚀 Nyash VM Backend - Executing file:" \
2025-11-30 14:30:28 +09:00
| grep -v "^🚀 Hakorune VM Backend - Executing file:" \
| grep -v "^[[]ControlForm::"
2025-09-25 06:15:22 +09:00
}
2025-09-24 09:30:42 +09:00
# 環境チェック(必須)
require_env( ) {
local required_tools = ( "cargo" "grep" "jq" )
local missing_tools = ( )
for tool in " ${ required_tools [@] } " ; do
if ! command -v " $tool " & > /dev/null; then
missing_tools += ( " $tool " )
fi
done
if [ ${# missing_tools [@] } -ne 0 ] ; then
log_error " Required tools missing: ${ missing_tools [*] } "
log_error "Please install missing tools and try again"
return 1
fi
# Nyash実行ファイル確認
2025-09-24 21:45:27 +09:00
if [ ! -f " $NYASH_BIN " ] ; then
log_error " Nyash executable not found at $NYASH_BIN "
log_error " Please run 'cargo build --release' first (in $NYASH_ROOT ) "
2025-09-24 09:30:42 +09:00
return 1
fi
log_info "Environment check passed"
return 0
}
# プラグイン整合性チェック(必須)
preflight_plugins( ) {
# プラグインマネージャーが存在する場合は実行
if [ -f " $( dirname " ${ BASH_SOURCE [0] } " ) /plugin_manager.sh " ] ; then
source " $( dirname " ${ BASH_SOURCE [0] } " ) /plugin_manager.sh "
check_plugin_integrity || return 1
else
log_warn "Plugin manager not found, skipping plugin checks"
fi
return 0
}
# テスト実行関数
run_test( ) {
local test_name = " $1 "
local test_func = " $2 "
( ( SMOKES_TEST_COUNT++) )
local start_time = $( date +%s.%N)
log_info " Running test: $test_name "
if $test_func ; then
local end_time = $( date +%s.%N)
local duration = $( echo " $end_time - $start_time " | bc -l)
log_success " $test_name ( ${ duration } s) "
( ( SMOKES_PASS_COUNT++) )
return 0
else
local end_time = $( date +%s.%N)
local duration = $( echo " $end_time - $start_time " | bc -l)
log_error " $test_name ( ${ duration } s) "
( ( SMOKES_FAIL_COUNT++) )
return 1
fi
}
# Nyash実行ヘルパー( Rust VM)
run_nyash_vm( ) {
local program = " $1 "
shift
2025-09-24 21:45:27 +09:00
local USE_PYVM = " ${ SMOKES_USE_PYVM :- 0 } "
2025-09-27 08:45:25 +09:00
local EXTRA_ARGS = ( )
if [ " ${ SMOKES_USE_DEV :- 0 } " = "1" ] ; then
EXTRA_ARGS += ( "--dev" )
fi
2025-09-28 01:33:58 +09:00
# Optional env sanitization between rapid invocations (default OFF)
# Enable with: SMOKES_CLEAN_ENV=1
local ENV_PREFIX = ( )
if [ " ${ SMOKES_CLEAN_ENV :- 0 } " = "1" ] ; then
2025-11-09 15:11:18 +09:00
# Preserve NYASH_JSON_ONLY to allow quiet JSON pipelines (e.g., v1 emitters)
2025-09-28 01:33:58 +09:00
ENV_PREFIX = ( env -u NYASH_DEBUG_ENABLE -u NYASH_DEBUG_KINDS -u NYASH_DEBUG_SINK \
-u NYASH_RESOLVE_FIX_BRACES -u NYASH_USING_AST \
-u NYASH_VM_TRACE -u NYASH_VM_VERIFY_MIR -u NYASH_VM_TOLERATE_VOID \
-u NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN)
fi
2025-09-24 09:30:42 +09:00
# -c オプションの場合は一時ファイル経由で実行
if [ " $program " = "-c" ] ; then
local code = " $1 "
shift
2025-11-06 15:41:52 +09:00
local tmpfile = " /tmp/nyash_test_ $$ .hako "
2025-09-24 09:30:42 +09:00
echo " $code " > " $tmpfile "
2025-11-04 16:33:04 +09:00
# (shim removed) provider tag shortcut — hv1 inline is stable now
2025-09-28 12:19:49 +09:00
# 軽量ASIFix( テスト用) : ブロック終端の余剰セミコロンを寛容に除去
if [ " ${ SMOKES_ASI_STRIP_SEMI :- 1 } " = "1" ] ; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' " $tmpfile " || true
fi
2025-09-24 09:30:42 +09:00
# プラグイン初期化メッセージを除外
2025-11-03 16:09:19 +09:00
# Optional preinclude for include-based code
local runfile = " $tmpfile "
if [ " ${ NYASH_PREINCLUDE :- 0 } " = "1" ] || [ " ${ HAKO_PREINCLUDE :- 0 } " = "1" ] ; then
2025-11-06 15:41:52 +09:00
local prefile = " /tmp/nyash_pre_ $$ .hako "
2025-11-03 16:09:19 +09:00
" $NYASH_ROOT /tools/dev/hako_preinclude.sh " " $tmpfile " " $prefile " >/dev/null || true
runfile = " $prefile "
fi
# Optional hint for include lines when preinclude is OFF
if grep -q '^include\s\"' " $tmpfile " 2>/dev/null && [ " ${ NYASH_PREINCLUDE :- 0 } " != "1" ] && [ " ${ HAKO_PREINCLUDE :- 0 } " != "1" ] ; then
echo "[WARN] VM backend does not support include. Prefer using+alias, or set NYASH_PREINCLUDE=1 for dev." >& 2
fi
2025-11-06 15:41:52 +09:00
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 \
2025-11-02 07:12:52 +09:00
NYASH_VM_USE_PY = " $USE_PYVM " NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 \
2025-11-02 15:43:43 +09:00
NYASH_DISABLE_NY_COMPILER = 1 HAKO_DISABLE_NY_COMPILER = 1 \
2025-11-30 14:30:28 +09:00
NYASH_FEATURES = stage3 NYASH_PARSER_ALLOW_SEMICOLON = 1 \
2025-11-07 19:32:44 +09:00
HAKO_ENABLE_USING = ${ HAKO_ENABLE_USING :- 1 } NYASH_ENABLE_USING = ${ NYASH_ENABLE_USING :- 1 } \
NYASH_USING_AST = 1 NYASH_PARSER_SEAM_TOLERANT = 1 \
2025-11-02 07:12:52 +09:00
" ${ ENV_PREFIX [@] } " \
2025-11-03 16:09:19 +09:00
" $NYASH_BIN " --backend vm " $runfile " " ${ EXTRA_ARGS [@] } " " $@ " 2>& 1 | filter_noise
2025-09-24 09:30:42 +09:00
local exit_code = ${ PIPESTATUS [0] }
2025-11-03 16:09:19 +09:00
# prefile may be unset when preinclude is OFF; use default expansion to avoid set -u errors
rm -f " $tmpfile " " ${ prefile :- } " 2>/dev/null || true
2025-11-07 19:32:44 +09:00
if [ " ${ SMOKES_FORCE_ZERO :- 0 } " = "1" ] ; then
return 0
fi
2025-09-24 09:30:42 +09:00
return $exit_code
else
2025-09-26 14:34:42 +09:00
# 軽量ASIFix( テスト用) : ブロック終端の余剰セミコロンを寛容に除去
if [ " ${ SMOKES_ASI_STRIP_SEMI :- 1 } " = "1" ] && [ -f " $program " ] ; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' " $program " || true
fi
2025-09-24 09:30:42 +09:00
# プラグイン初期化メッセージを除外
2025-11-03 16:09:19 +09:00
# Optional preinclude
local runfile2 = " $program "
if [ " ${ NYASH_PREINCLUDE :- 0 } " = "1" ] || [ " ${ HAKO_PREINCLUDE :- 0 } " = "1" ] ; then
2025-11-06 15:41:52 +09:00
local prefile2 = " /tmp/nyash_pre_ $$ .hako "
2025-11-03 16:09:19 +09:00
" $NYASH_ROOT /tools/dev/hako_preinclude.sh " " $program " " $prefile2 " >/dev/null || true
runfile2 = " $prefile2 "
fi
# Optional hint for include lines when preinclude is OFF
if [ -f " $program " ] && grep -q '^include\s\"' " $program " 2>/dev/null && [ " ${ NYASH_PREINCLUDE :- 0 } " != "1" ] && [ " ${ HAKO_PREINCLUDE :- 0 } " != "1" ] ; then
2025-11-03 23:21:48 +09:00
# Policy: quick は SKIP 既定。それ以外は WARN( SMOKES_INCLUDE_POLICY で上書き可能)。
local policy = " ${ SMOKES_INCLUDE_POLICY :- } "
if [ -z " $policy " ] ; then
case " $program " in
2025-11-04 16:33:04 +09:00
*/profiles/quick/*) policy = "error" ; ;
2025-11-03 23:21:48 +09:00
*) policy = "warn" ; ;
esac
fi
if [ " $policy " = "skip" ] ; then
SMOKES_INCLUDE_SKIP_COUNT = $(( SMOKES_INCLUDE_SKIP_COUNT + 1 ))
local rel_path = " $program "
if [ [ " $program " = = " $NYASH_ROOT / " * ] ] ; then
rel_path = " ${ program # $NYASH_ROOT / } "
fi
SMOKES_INCLUDE_SKIP_LIST += ( " $rel_path " )
echo "[SKIP] include is deprecated in 20.36+ (quick). Prefer using+alias." >& 2
return 0
elif [ " $policy " = "error" ] ; then
echo "[ERROR] include is deprecated in 20.36+. Prefer using+alias." >& 2
return 2
else
echo "[WARN] include is deprecated in 20.36+. Prefer using+alias. Preinclude is dev-only (NYASH_PREINCLUDE=1)." >& 2
fi
2025-11-03 16:09:19 +09:00
fi
2025-11-06 15:41:52 +09:00
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 \
2025-11-02 07:12:52 +09:00
NYASH_VM_USE_PY = " $USE_PYVM " NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 \
2025-11-02 15:43:43 +09:00
NYASH_DISABLE_NY_COMPILER = 1 HAKO_DISABLE_NY_COMPILER = 1 \
2025-11-30 14:30:28 +09:00
NYASH_FEATURES = stage3 NYASH_PARSER_ALLOW_SEMICOLON = 1 \
2025-11-07 19:32:44 +09:00
HAKO_ENABLE_USING = ${ HAKO_ENABLE_USING :- 1 } NYASH_ENABLE_USING = ${ NYASH_ENABLE_USING :- 1 } \
NYASH_USING_AST = 1 NYASH_PARSER_SEAM_TOLERANT = 1 \
2025-11-02 07:12:52 +09:00
" ${ ENV_PREFIX [@] } " \
2025-11-03 16:09:19 +09:00
" $NYASH_BIN " --backend vm " $runfile2 " " ${ EXTRA_ARGS [@] } " " $@ " 2>& 1 | filter_noise
local exit_code = ${ PIPESTATUS [0] }
# prefile2 may be unset when preinclude is OFF
rm -f " ${ prefile2 :- } " 2>/dev/null || true
2025-11-07 19:32:44 +09:00
if [ " ${ SMOKES_FORCE_ZERO :- 0 } " = "1" ] ; then
return 0
fi
2025-11-03 16:09:19 +09:00
return $exit_code
fi
}
# Verify MIR JSON rc using selected primary (Core or Hakorune VM)
verify_mir_rc( ) {
local json_path = " $1 "
2025-11-03 23:21:48 +09:00
# 20.36: hakovm を primary 既定へ( Core は診断 fallback)
local primary = " ${ HAKO_VERIFY_PRIMARY :- hakovm } "
2025-11-03 16:09:19 +09:00
if [ " $primary " = "hakovm" ] ; then
2025-11-04 16:33:04 +09:00
# For MIR JSON v1, try Hakovm v1 dispatcher first (default ON), fallback to Core on failure.
# Allow forcing Core with HAKO_VERIFY_V1_FORCE_CORE=1
2025-11-03 23:21:48 +09:00
if grep -q '"schema_version"' " $json_path " 2>/dev/null; then
2025-11-04 16:33:04 +09:00
if [ " ${ HAKO_VERIFY_V1_FORCE_CORE :- 0 } " = "1" ] ; then
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: core (rust)" >& 2; fi
2025-11-04 16:33:04 +09:00
" $NYASH_BIN " --mir-json-file " $json_path " >/dev/null 2>& 1; return $?
fi
2025-11-06 15:41:52 +09:00
# hv1 直行( main.rs 早期経路)。成功時は rc を採用、失敗時は Core にフォールバック。
# ただしフロー検証( dispatcher flow / phi 実験)が有効な場合は Core を優先( hv1-inline は最小実装のため)。
if [ " ${ HAKO_VERIFY_V1_FORCE_HAKOVM :- 0 } " != "1" ] ; then
local hv1_rc; hv1_rc = $( verify_v1_inline_file " $json_path " || true )
if [ [ " $hv1_rc " = ~ ^-?[ 0-9] +$ ] ] ; then
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: hv1_inline (rust)" >& 2; fi
2025-11-06 15:41:52 +09:00
local n = $hv1_rc ; if [ $n -lt 0 ] ; then n = $(( ( n % 256 + 256 ) % 256 )) ; else n = $(( n % 256 )) ; fi ; return $n
fi
fi
# 強制 hv1( -c ラッパ): NyVmDispatcherV1Box.run を直接呼び出して rc を取得
if [ " ${ HAKO_VERIFY_V1_FORCE_HAKOVM :- 0 } " = "1" ] ; then
local hv1_rc_force; hv1_rc_force = $( verify_v1_inline_file " $json_path " || true )
if [ [ " $hv1_rc_force " = ~ ^-?[ 0-9] +$ ] ] ; then
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: hv1_inline (rust)" >& 2; fi
2025-11-06 15:41:52 +09:00
local n = $hv1_rc_force ; if [ $n -lt 0 ] ; then n = $(( ( n % 256 + 256 ) % 256 )) ; else n = $(( n % 256 )) ; fi ; return $n
fi
return 1
2025-11-03 23:21:48 +09:00
fi
2025-11-04 16:33:04 +09:00
# No include+preinclude fallback succeeded → Core にフォールバック
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: core (rust)" >& 2; fi
2025-11-03 23:21:48 +09:00
" $NYASH_BIN " --mir-json-file " $json_path " >/dev/null 2>& 1
return $?
fi
2025-11-03 16:09:19 +09:00
# Build a tiny driver to call MiniVmEntryBox.run_min with JSON literal embedded
2025-11-04 16:33:04 +09:00
if [ ! -f " $json_path " ] ; then
echo " [FAIL] verify_mir_rc: json not found: $json_path " >& 2
return 2
fi
2025-11-03 16:09:19 +09:00
# Escape JSON as a single string literal via jq -Rs (preserves newlines)
local json_literal
json_literal = " $( jq -Rs . < " $json_path " ) "
build_and_run_driver_alias( ) {
local header = " $1 "
local code = $( cat <<HCODE
$header
static box Main { method main( args) {
local j = __MIR_JSON__;
local rc = MiniVmEntryBox.run_min( j)
print( MiniVmEntryBox.int_to_str( rc) )
return rc
} }
HCODE
)
code = " ${ code /__MIR_JSON__/ $json_literal } "
2025-11-04 16:33:04 +09:00
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 NYASH_USING_AST = 1 NYASH_RESOLVE_FIX_BRACES = 1 run_nyash_vm -c " $code " 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}'
2025-11-03 16:09:19 +09:00
}
build_and_run_driver_include( ) {
local inc_path = " $1 "
local code = $( cat <<HCODE
include " $inc_path "
static box Main { method main( args) {
local j = __MIR_JSON__;
local rc = MiniVmEntryBox.run_min( j)
print( MiniVmEntryBox.int_to_str( rc) )
return rc
} }
HCODE
)
code = " ${ code /__MIR_JSON__/ $json_literal } "
2025-11-04 16:33:04 +09:00
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 NYASH_PREINCLUDE = 1 NYASH_RESOLVE_FIX_BRACES = 1 run_nyash_vm -c " $code " 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}'
2025-11-03 16:09:19 +09:00
}
# Try alias header first; fallback to dev-file header; final fallback: include+preinclude
local out
out = " $( build_and_run_driver_alias 'using selfhost.vm.entry as MiniVmEntryBox' ) "
if ! [ [ " $out " = ~ ^-?[ 0-9] +$ ] ] ; then
out = " $( build_and_run_driver_alias 'using "lang/src/vm/boxes/mini_vm_entry.hako" as MiniVmEntryBox' ) "
fi
if ! [ [ " $out " = ~ ^-?[ 0-9] +$ ] ] ; then
out = " $( build_and_run_driver_include 'lang/src/vm/boxes/mini_vm_entry.hako' ) "
fi
if [ [ " $out " = ~ ^-?[ 0-9] +$ ] ] ; then
local n = $out
# normalize into [0,255]
if [ $n -lt 0 ] ; then n = $(( ( n % 256 + 256 ) % 256 )) ; else n = $(( n % 256 )) ; fi
return $n
fi
# Fallback: core primary when MiniVM resolution is unavailable
2025-11-03 23:21:48 +09:00
if grep -q '"functions"' " $json_path " 2>/dev/null && grep -q '"blocks"' " $json_path " 2>/dev/null; then
local json_literal3; json_literal3 = " $( jq -Rs . < " $json_path " ) "
local code = $( cat <<HCODE
include "lang/src/vm/core/dispatcher.hako"
static box Main { method main( args) {
local j = __MIR_JSON__
local r = NyVmDispatcher.run( j)
print( "" + r)
return r
} }
HCODE
)
code = " ${ code /__MIR_JSON__/ $json_literal3 } "
2025-11-06 15:41:52 +09:00
local tmpwrap = " /tmp/hako_core_wrap_ $$ .hako "
2025-11-03 23:21:48 +09:00
echo " $code " > " $tmpwrap "
NYASH_PREINCLUDE = 1 run_nyash_vm " $tmpwrap " >/dev/null 2>& 1; local r = $? ; rm -f " $tmpwrap " ; return $r
fi
NYASH_GATE_C_CORE = 1 HAKO_GATE_C_CORE = 1 " $NYASH_BIN " --json-file " $json_path " >/dev/null 2>& 1; return $?
2025-11-03 16:09:19 +09:00
else
2025-11-03 23:21:48 +09:00
# Core primary: detect MIR(JSON) vs Program(JSON v0)
if grep -q '"functions"' " $json_path " 2>/dev/null && grep -q '"blocks"' " $json_path " 2>/dev/null; then
" $NYASH_BIN " --mir-json-file " $json_path " >/dev/null 2>& 1; return $?
fi
NYASH_GATE_C_CORE = 1 HAKO_GATE_C_CORE = 1 " $NYASH_BIN " --json-file " $json_path " >/dev/null 2>& 1; return $?
2025-09-24 09:30:42 +09:00
fi
}
2025-11-04 20:46:43 +09:00
# New function: verify_program_via_builder_to_core
# Purpose: Program(JSON v0) → MirBuilder(Hako) → MIR(JSON v0) → Core execution
# This is dev-only for testing builder output quality
verify_program_via_builder_to_core( ) {
local prog_json_path = " $1 "
2025-11-05 18:57:03 +09:00
# Step 1: Use minimal runner to convert Program → MIR( env経由でJSONを渡す)
2025-11-04 20:46:43 +09:00
local mir_json_path = " /tmp/builder_output_ $$ .json "
2025-11-05 18:57:03 +09:00
local builder_code_min = $( cat <<'HCODE'
using "hako.mir.builder.internal.runner_min" as BuilderRunnerMinBox
2025-11-04 20:46:43 +09:00
static box Main { method main( args) {
2025-11-05 18:57:03 +09:00
local prog_json = env.get( "HAKO_BUILDER_PROGRAM_JSON" )
2025-11-04 20:46:43 +09:00
if prog_json = = null { print( "Builder failed" ) ; return 1 }
2025-11-05 18:57:03 +09:00
local mir_out = BuilderRunnerMinBox.run( prog_json)
2025-11-04 20:46:43 +09:00
if mir_out = = null { print( "Builder failed" ) ; return 1 }
2025-11-05 18:57:03 +09:00
print( "[MIR_OUT_BEGIN]" )
2025-11-04 20:46:43 +09:00
print( "" + mir_out)
2025-11-05 18:57:03 +09:00
print( "[MIR_OUT_END]" )
2025-11-04 20:46:43 +09:00
return 0
} }
HCODE
)
# Read program JSON to env (avoid embedding/escaping)
local prog_json_raw
prog_json_raw = " $( cat " $prog_json_path " ) "
# Run builder with internal lowers enabled using v1 dispatcher
2025-11-05 18:57:03 +09:00
local mir_json = ""
2025-11-04 20:46:43 +09:00
local builder_stderr = " /tmp/builder_stderr_ $$ .log "
2025-11-05 18:57:03 +09:00
local builder_stdout = " /tmp/builder_stdout_ $$ .log "
# Try minimal runner first (fast path), unless HAKO_PREFER_MIRBUILDER=1
if [ " ${ HAKO_PREFER_MIRBUILDER :- 0 } " = "1" ] ; then
: # skip minimal runner
else
mir_json = $( HAKO_MIR_BUILDER_INTERNAL = 1 \
HAKO_MIR_RUNNER_MIN_NO_METHODS = 1 \
2025-11-04 20:46:43 +09:00
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 \
HAKO_ROUTE_HAKOVM = 1 \
2025-11-05 18:57:03 +09:00
NYASH_ENABLE_USING = 1 HAKO_ENABLE_USING = 1 \
2025-11-04 20:46:43 +09:00
NYASH_USING_AST = 1 \
NYASH_RESOLVE_FIX_BRACES = 1 \
NYASH_DISABLE_NY_COMPILER = 1 \
2025-11-30 14:30:28 +09:00
NYASH_FEATURES = " ${ NYASH_FEATURES :- stage3 } " \
2025-11-04 20:46:43 +09:00
NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 \
2025-11-05 18:57:03 +09:00
HAKO_BUILDER_PROGRAM_JSON = " $prog_json_raw " \
run_nyash_vm -c " $builder_code_min " 2>" $builder_stderr " | tee " $builder_stdout " | awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' )
fi
if [ " ${ HAKO_MIR_BUILDER_DEBUG :- 0 } " = "1" ] ; then
echo "[builder debug] stdout (tail):" >& 2
tail -n 60 " $builder_stdout " >& 2 || true
echo "[builder debug] stderr (tail):" >& 2
tail -n 60 " $builder_stderr " >& 2 || true
fi
# Fallback Option A: try full MirBuilderBox (emit) when minimal runner fails
if [ " $mir_json " = "Builder failed" ] || [ -z " $mir_json " ] ; then
local builder_code_full = $( cat <<'HCODE'
using "hako.mir.builder" as MirBuilderBox
static box Main { method main( args) {
local prog_json = env.get( "HAKO_BUILDER_PROGRAM_JSON" )
if prog_json = = null { print( "Builder failed" ) ; return 1 }
local mir_out = MirBuilderBox.emit_from_program_json_v0( prog_json, null)
if mir_out = = null { print( "Builder failed" ) ; return 1 }
print( "[MIR_OUT_BEGIN]" )
print( "" + mir_out)
print( "[MIR_OUT_END]" )
return 0
} }
HCODE
)
mir_json = $( HAKO_MIR_BUILDER_INTERNAL = 1 \
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 \
HAKO_ROUTE_HAKOVM = 1 \
NYASH_ENABLE_USING = 1 HAKO_ENABLE_USING = 1 \
NYASH_USING_AST = 1 NYASH_RESOLVE_FIX_BRACES = 1 \
2025-11-30 14:30:28 +09:00
NYASH_DISABLE_NY_COMPILER = 1 NYASH_FEATURES = stage3 \
2025-11-05 18:57:03 +09:00
NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 \
HAKO_BUILDER_PROGRAM_JSON = " $prog_json_raw " \
run_nyash_vm -c " $builder_code_full " 2>>" $builder_stderr " | tee -a " $builder_stdout " | awk '/\[MIR_OUT_BEGIN\]/{flag=1;next}/\[MIR_OUT_END\]/{flag=0}flag' )
fi
# PRIMARY no-fallback: if requested, do not fall back to Rust CLI builder
if [ " ${ HAKO_PRIMARY_NO_FALLBACK :- 0 } " = "1" ] ; then
if [ " $mir_json " = "Builder failed" ] || [ -z " $mir_json " ] ; then
return 1
fi
fi
2025-11-04 20:46:43 +09:00
2025-11-05 18:57:03 +09:00
# Fallback Option B: use Rust CLI builder when Hako builder fails
2025-11-04 20:46:43 +09:00
if [ " $mir_json " = "Builder failed" ] || [ -z " $mir_json " ] ; then
if [ " ${ HAKO_MIR_BUILDER_DEBUG :- 0 } " = "1" ] && [ -f " $builder_stderr " ] ; then
echo "[builder debug] Hako builder failed, falling back to Rust CLI" >& 2
cat " $builder_stderr " >& 2
cp " $builder_stderr " /tmp/builder_last_error.log
fi
rm -f " $builder_stderr "
local tmp_mir = " /tmp/ny_builder_conv_ $$ .json "
if " $NYASH_BIN " --program-json-to-mir " $tmp_mir " --json-file " $prog_json_path " >/dev/null 2>& 1; then
2025-11-05 18:57:03 +09:00
if [ " ${ HAKO_VERIFY_BUILDER_ONLY :- 0 } " = "1" ] ; then
# Builder-only: check structure only
if grep -q '"functions"' " $tmp_mir " && grep -q '"blocks"' " $tmp_mir " ; then
rm -f " $tmp_mir " ; return 0
else
rm -f " $tmp_mir " ; return 1
fi
else
" $NYASH_BIN " --mir-json-file " $tmp_mir " >/dev/null 2>& 1
local rc = $?
rm -f " $tmp_mir "
return $rc
fi
2025-11-04 20:46:43 +09:00
else
return 1
fi
fi
2025-11-05 18:57:03 +09:00
rm -f " $builder_stderr " " $builder_stdout "
2025-11-04 20:46:43 +09:00
2025-11-05 18:57:03 +09:00
# Validate builder output looks like MIR JSON; otherwise fallback to Rust CLI (unless PRIMARY no-fallback)
2025-11-04 20:46:43 +09:00
if ! echo " $mir_json " | grep -q '"functions"' || ! echo " $mir_json " | grep -q '"blocks"' ; then
2025-11-05 18:57:03 +09:00
if [ " ${ HAKO_PRIMARY_NO_FALLBACK :- 0 } " = "1" ] ; then
return 1
fi
2025-11-04 20:46:43 +09:00
# fallback: Rust CLI builder
local tmp_mir = " /tmp/ny_builder_conv_ $$ .json "
if " $NYASH_BIN " --program-json-to-mir " $tmp_mir " --json-file " $prog_json_path " >/dev/null 2>& 1; then
" $NYASH_BIN " --mir-json-file " $tmp_mir " >/dev/null 2>& 1
local rc = $?
rm -f " $tmp_mir "
return $rc
else
return 1
fi
fi
2025-11-05 18:57:03 +09:00
# Route: if builder output contains v1 hints, run hv1 dispatcher inline.
if echo " $mir_json " | grep -q '"schema_version"' || echo " $mir_json " | grep -q '"op"\s*:\s*"mir_call"' ; then
local hv1_rc
local mir_literal; mir_literal = " $( printf '%s' " $mir_json " | jq -Rs .) "
hv1_rc = $( run_hv1_inline_alias_wrapper " $mir_literal " )
if [ [ " $hv1_rc " = ~ ^-?[ 0-9] +$ ] ] ; then
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: hakovm (hako)" >& 2; fi
2025-11-05 18:57:03 +09:00
local n = $hv1_rc ; if [ $n -lt 0 ] ; then n = $(( ( n % 256 + 256 ) % 256 )) ; else n = $(( n % 256 )) ; fi
return $n
fi
fi
# Route: if builder output is v0 but contains unified-only ops (newbox/boxcall),
# execute via Hako Core dispatcher (NyVmDispatcher.run) which supports extended v0.
if echo " $mir_json " | grep -q '"op"\s*:\s*"newbox"' || echo " $mir_json " | grep -q '"op"\s*:\s*"boxcall"' ; then
local mir_literal2; mir_literal2 = " $( printf '%s' " $mir_json " | jq -Rs .) "
local code = $( cat <<'HCODE'
include "lang/src/vm/core/dispatcher.hako"
static box Main { method main( args) {
local j = env.get( "NYASH_VERIFY_JSON" )
local r = NyVmDispatcher.run( j)
print( "" + r)
return r
} }
HCODE
)
local out; out = $( NYASH_VERIFY_JSON = " $mir_literal2 " NYASH_PREINCLUDE = 1 run_nyash_vm -c " $code " 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}' )
if [ [ " $out " = ~ ^-?[ 0-9] +$ ] ] ; then
local n = $out ; if [ $n -lt 0 ] ; then n = $(( ( n % 256 + 256 ) % 256 )) ; else n = $(( n % 256 )) ; fi
return $n
fi
fi
# Optional: structure-only check (builder only) for fast mode
if [ " ${ HAKO_VERIFY_BUILDER_ONLY :- 0 } " = "1" ] ; then
if echo " $mir_json " | grep -q '"functions"' && echo " $mir_json " | grep -q '"blocks"' ; then
return 0
else
return 1
fi
fi
# Write MIR JSON to temp file and execute via Core
2025-11-04 20:46:43 +09:00
echo " $mir_json " > " $mir_json_path "
2025-11-08 23:45:29 +09:00
if [ " ${ HAKO_TRACE_EXECUTION :- 0 } " = "1" ] ; then echo "[trace] executor: core (rust)" >& 2; fi
2025-11-04 20:46:43 +09:00
" $NYASH_BIN " --mir-json-file " $mir_json_path " >/dev/null 2>& 1
local rc = $?
rm -f " $mir_json_path "
return $rc
}
2025-11-05 18:57:03 +09:00
# hv1 inline alias-only wrapper (env JSON → hv1 dispatcher)
# Usage: run_hv1_inline_alias_wrapper "$json_literal" → prints rc line; returns rc
run_hv1_inline_alias_wrapper( ) {
local json_literal = " $1 "
local code = $( cat <<'HCODE'
using "selfhost.vm.hv1.dispatch" as NyVm
static box Main { method main( args) {
local j = env.get( "NYASH_VERIFY_JSON" )
local r = NyVm.NyVmDispatcherV1Box.run( j)
print( "" + r)
return r
} }
HCODE
)
HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM = 0 NYASH_USING_AST = 1 NYASH_RESOLVE_FIX_BRACES = 1 \
NYASH_VERIFY_JSON = " $json_literal " run_nyash_vm -c " $code " 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}'
}
2025-09-24 09:30:42 +09:00
# Nyash実行ヘルパー( LLVM)
run_nyash_llvm( ) {
local program = " $1 "
shift
2025-09-28 12:19:49 +09:00
# Allow developer to force LLVM run (env guarantees availability)
if [ " ${ SMOKES_FORCE_LLVM :- 0 } " != "1" ] ; then
# Skip gracefully when LLVM backend is not available in this build
# Primary check: version string advertises features
if ! " $NYASH_BIN " --version 2>/dev/null | grep -q "features.*llvm" ; then
# Fallback check: binary contains LLVM harness symbols (ny-llvmc / NYASH_LLVM_USE_HARNESS)
if ! strings " $NYASH_BIN " 2>/dev/null | grep -E -q 'ny-llvmc|NYASH_LLVM_USE_HARNESS' ; then
log_warn "LLVM backend not available in this build; skipping LLVM run"
log_info "Hint: build ny-llvmc + enable harness: cargo build --release -p nyash-llvm-compiler && cargo build --release --features llvm"
return 0
fi
fi
2025-09-27 08:45:25 +09:00
fi
2025-09-24 09:30:42 +09:00
# -c オプションの場合は一時ファイル経由で実行
if [ " $program " = "-c" ] ; then
local code = " $1 "
shift
2025-11-06 15:41:52 +09:00
local tmpfile = " /tmp/nyash_test_ $$ .hako "
2025-09-24 09:30:42 +09:00
echo " $code " > " $tmpfile "
2025-09-28 12:19:49 +09:00
# 軽量ASIFix( テスト用) : ブロック終端の余剰セミコロンを寛容に除去
if [ " ${ SMOKES_ASI_STRIP_SEMI :- 1 } " = "1" ] ; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' " $tmpfile " || true
fi
# 軽量ASIFix( テスト用) : ブロック終端の余剰セミコロンを寛容に除去
if [ " ${ SMOKES_ASI_STRIP_SEMI :- 1 } " = "1" ] && [ -f " $program " ] ; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' " $program " || true
fi
2025-09-24 09:30:42 +09:00
# プラグイン初期化メッセージを除外
2025-11-30 14:30:28 +09:00
PYTHONPATH = " ${ PYTHONPATH :- $NYASH_ROOT } " NYASH_NY_LLVM_COMPILER = " $NYASH_ROOT /target/release/ny-llvmc " NYASH_LLVM_USE_HARNESS = 1 NYASH_EMIT_EXE_NYRT = " $NYASH_ROOT /target/release " NYASH_VM_USE_PY = 0 NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 NYASH_FEATURES = stage3 NYASH_PARSER_ALLOW_SEMICOLON = 1 NYASH_DISABLE_NY_COMPILER = 1 HAKO_DISABLE_NY_COMPILER = 1 " $NYASH_BIN " --backend llvm " $tmpfile " " $@ " 2>& 1 | \
2025-09-28 12:19:49 +09:00
grep -v "^\[UnifiedBoxRegistry\]" | grep -v "^\[FileBox\]" | grep -v "^Net plugin:" | grep -v "^\[.*\] Plugin" | \
2025-11-02 17:50:06 +09:00
grep -v '^\[plugin-loader\] backend=' | \
grep -v '^🔌 plugin host initialized' | grep -v '^✅ plugin host fully configured' | \
grep -v '^⚡ Hakorune LLVM Backend' | \
2025-09-28 12:19:49 +09:00
grep -v '^✅ LLVM (harness) execution completed' | grep -v '^📊 MIR Module compiled successfully' | grep -v '^📊 Functions:' | grep -v 'JSON Parse Errors:' | grep -v 'Parsing errors' | grep -v 'No parsing errors' | grep -v 'Error at line ' | \
2025-11-02 17:50:06 +09:00
grep -v '^\[using\]' | grep -v '^\[using/resolve\]' | grep -v '^\[using/cache\]' | \
2025-09-28 12:19:49 +09:00
grep -v '^\[ny-llvmc\]' | grep -v '^\[harness\]' | grep -v '^Compiled to ' | grep -v '^/usr/bin/ld:'
2025-09-24 09:30:42 +09:00
local exit_code = ${ PIPESTATUS [0] }
rm -f " $tmpfile "
return $exit_code
else
2025-09-28 12:19:49 +09:00
# 軽量ASIFix( テスト用) : ブロック終端の余剰セミコロンを寛容に除去
if [ " ${ SMOKES_ASI_STRIP_SEMI :- 1 } " = "1" ] && [ -f " $program " ] ; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' " $program " || true
fi
2025-09-24 09:30:42 +09:00
# プラグイン初期化メッセージを除外
2025-11-30 14:30:28 +09:00
PYTHONPATH = " ${ PYTHONPATH :- $NYASH_ROOT } " NYASH_NY_LLVM_COMPILER = " $NYASH_ROOT /target/release/ny-llvmc " NYASH_LLVM_USE_HARNESS = 1 NYASH_EMIT_EXE_NYRT = " $NYASH_ROOT /target/release " NYASH_VM_USE_PY = 0 NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN = 1 NYASH_FEATURES = stage3 NYASH_PARSER_ALLOW_SEMICOLON = 1 NYASH_DISABLE_NY_COMPILER = 1 HAKO_DISABLE_NY_COMPILER = 1 " $NYASH_BIN " --backend llvm " $program " " $@ " 2>& 1 | \
2025-09-28 12:19:49 +09:00
grep -v "^\[UnifiedBoxRegistry\]" | grep -v "^\[FileBox\]" | grep -v "^Net plugin:" | grep -v "^\[.*\] Plugin" | \
2025-11-02 17:50:06 +09:00
grep -v '^\[plugin-loader\] backend=' | \
grep -v '^🔌 plugin host initialized' | grep -v '^✅ plugin host fully configured' | \
grep -v '^⚡ Hakorune LLVM Backend' | \
2025-09-28 12:19:49 +09:00
grep -v '^✅ LLVM (harness) execution completed' | grep -v '^📊 MIR Module compiled successfully' | grep -v '^📊 Functions:' | grep -v 'JSON Parse Errors:' | grep -v 'Parsing errors' | grep -v 'No parsing errors' | grep -v 'Error at line ' | \
2025-11-02 17:50:06 +09:00
grep -v '^\[using\]' | grep -v '^\[using/resolve\]' | grep -v '^\[using/cache\]' | \
2025-09-28 12:19:49 +09:00
grep -v '^\[ny-llvmc\]' | grep -v '^\[harness\]' | grep -v '^Compiled to ' | grep -v '^/usr/bin/ld:'
2025-09-24 09:30:42 +09:00
return ${ PIPESTATUS [0] }
fi
}
2025-09-24 21:45:27 +09:00
# シンプルテスト補助(スクリプト互換)
test_pass( ) { log_success " $1 " ; return 0; }
test_fail( ) { log_error " $1 ${ 2 :- } " ; return 1; }
test_skip( ) { log_warn " SKIP $1 ${ 2 :- } " ; return 0; }
2025-09-24 09:30:42 +09:00
# 出力比較ヘルパー
compare_outputs( ) {
local expected = " $1 "
local actual = " $2 "
local test_name = " $3 "
if [ " $expected " = " $actual " ] ; then
return 0
else
log_error " $test_name output mismatch: "
log_error " Expected: $expected "
log_error " Actual: $actual "
return 1
fi
}
# 結果サマリー出力
print_summary( ) {
local end_time = $( date +%s.%N)
local total_duration = $( echo " $end_time - $SMOKES_START_TIME " | bc -l)
echo ""
echo "==============================================="
echo "Smoke Test Summary"
echo "==============================================="
echo " Total tests: $SMOKES_TEST_COUNT "
echo " Passed: $SMOKES_PASS_COUNT "
echo " Failed: $SMOKES_FAIL_COUNT "
echo " Duration: ${ total_duration } s "
echo ""
2025-11-03 23:21:48 +09:00
if [ " ${ SMOKES_INCLUDE_SKIP_COUNT :- 0 } " -gt 0 ] ; then
echo " Include SKIPs: $SMOKES_INCLUDE_SKIP_COUNT "
for entry in " ${ SMOKES_INCLUDE_SKIP_LIST [@] } " ; do
echo " - $entry "
done
echo ""
fi
2025-09-24 09:30:42 +09:00
if [ $SMOKES_FAIL_COUNT -eq 0 ] ; then
log_success "All tests passed! ✨"
return 0
else
log_error " $SMOKES_FAIL_COUNT test(s) failed "
return 1
fi
}
# JSON出力関数
output_json( ) {
local profile = " ${ 1 :- unknown } "
local end_time = $( date +%s.%N)
local total_duration = $( echo " $end_time - $SMOKES_START_TIME " | bc -l)
cat << EOF
{
"profile" : " $profile " ,
"total" : $SMOKES_TEST_COUNT ,
"passed" : $SMOKES_PASS_COUNT ,
"failed" : $SMOKES_FAIL_COUNT ,
"duration" : $total_duration ,
"timestamp" : " $( date -Iseconds) " ,
"success" : $( [ $SMOKES_FAIL_COUNT -eq 0 ] && echo "true" || echo "false" )
}
EOF
}
# JUnit XML出力関数
output_junit( ) {
local profile = " ${ 1 :- unknown } "
local end_time = $( date +%s.%N)
local total_duration = $( echo " $end_time - $SMOKES_START_TIME " | bc -l)
cat << EOF
<?xml version = "1.0" encoding = "UTF-8" ?>
<testsuite name = " smokes_ $profile " tests = " $SMOKES_TEST_COUNT " failures = " $SMOKES_FAIL_COUNT " time = " $total_duration " >
<!-- Individual test cases would be added by specific test scripts -->
</testsuite>
EOF
2025-09-24 21:45:27 +09:00
}
2025-11-06 15:41:52 +09:00
# v1 JSON 正規化(オブジェクトキー順序ソート、配列順は保持)→ SHA256 ハッシュ
v1_normalized_hash( ) {
local json_path = " $1 "
if [ ! -f " $json_path " ] ; then
echo " [FAIL] v1_normalized_hash: json not found: $json_path " >& 2
return 2
fi
if ! command -v jq >/dev/null 2>& 1; then
echo "[FAIL] v1_normalized_hash: jq required" >& 2
return 2
fi
2025-11-09 15:11:18 +09:00
# Extract JSON object line defensively (strip any leading noise like 'RC: N' or logs)
# Extract JSON object block from first '{' to EOF (handles pretty JSON)
local raw_json
raw_json = $( awk 'BEGIN{on=0} { if(on){print} else if($0 ~ /^[[:space:]]*\{/){ on=1; print } }' " $json_path " )
if [ -z " $raw_json " ] ; then
return 1
fi
2025-11-06 15:41:52 +09:00
local canon
2025-11-09 15:11:18 +09:00
canon = $( printf '%s' " $raw_json " | jq -S -c .) || return 1
2025-11-06 15:41:52 +09:00
printf "%s" " $canon " | sha256sum | awk '{print $1}'
}
# 2つの v1 JSON ファイルの正規化ハッシュを比較( 等しければ0)
compare_v1_hash( ) {
local a = " $1 " ; local b = " $2 "
local ha hb
ha = $( v1_normalized_hash " $a " || true )
hb = $( v1_normalized_hash " $b " || true )
if [ -z " $ha " ] || [ -z " $hb " ] ; then
return 2
fi
[ " $ha " = " $hb " ]
}
# Run hv1 inline (early route) and return numeric rc (0-255). Returns non-zero exit on failure to execute.
verify_v1_inline_file( ) {
local json_path = " $1 "
if [ ! -f " $json_path " ] ; then
echo " [FAIL] verify_v1_inline_file: json not found: $json_path " >& 2
return 2
fi
local out
2025-11-08 23:45:29 +09:00
# Optional: show full logs for debugging (default OFF)
if [ " ${ HAKO_VERIFY_SHOW_LOGS :- 0 } " = "1" ] ; then
2025-11-09 15:11:18 +09:00
# Show all output to stderr, then extract numeric rc (env-sanitized for determinism)
env -i PATH = " $PATH " \
HAKO_TRACE_EXECUTION = " ${ HAKO_TRACE_EXECUTION :- 0 } " HAKO_VERIFY_SHOW_LOGS = " ${ HAKO_VERIFY_SHOW_LOGS :- 0 } " \
HAKO_ROUTE_HAKOVM = 1 HAKO_VERIFY_V1_FORCE_HAKOVM = 1 \
NYASH_VERIFY_JSON = " $( cat " $json_path " ) " \
2025-11-08 23:45:29 +09:00
" $NYASH_BIN " --backend vm /dev/null 2>& 1 | tr -d '\r' | tee /tmp/hv1_debug.log >& 2
out = $( awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}' /tmp/hv1_debug.log)
else
2025-11-09 15:11:18 +09:00
out = $( env -i PATH = " $PATH " \
HAKO_TRACE_EXECUTION = " ${ HAKO_TRACE_EXECUTION :- 0 } " \
HAKO_ROUTE_HAKOVM = 1 HAKO_VERIFY_V1_FORCE_HAKOVM = 1 \
NYASH_VERIFY_JSON = " $( cat " $json_path " ) " \
" $NYASH_BIN " --backend vm /dev/null 2>/dev/null | tr -d '\r' | awk '/^-?[0-9]+$/{n=$0} END{if(n!="") print n}' )
2025-11-08 23:45:29 +09:00
fi
2025-11-06 15:41:52 +09:00
if [ [ " $out " = ~ ^-?[ 0-9] +$ ] ] ; then
# echo numeric rc and return success; caller normalizes/returns as exit code
echo " $out "
return 0
fi
return 1
}
2025-11-10 19:42:42 +09:00
# Dev profile helpers (centralize bring-up toggles for MirBuilder)
# Usage: call enable_mirbuilder_dev_env in canaries that need it.
enable_mirbuilder_dev_env( ) {
# Avoid ny-compiler inline path during VM bring-up
export NYASH_USE_NY_COMPILER = 0
# Allow FileBox provider fallback (dev only)
export NYASH_FAIL_FAST = ${ NYASH_FAIL_FAST :- 0 }
# Enable using resolution in VM
export NYASH_ENABLE_USING = 1
export HAKO_ENABLE_USING = 1
# Allow file-based using in dev (e.g., using "hako.mir.builder")
export NYASH_ALLOW_USING_FILE = 1
export HAKO_ALLOW_USING_FILE = 1
# Optional: preinclude heavy using segments for legacy/prelude-heavy paths (default OFF)
if [ " ${ SMOKES_DEV_PREINCLUDE :- 0 } " = "1" ] ; then
export HAKO_PREINCLUDE = 1
fi
2025-11-10 23:17:46 +09:00
# Optional: enable JsonFrag Normalizer for builder/min paths (default OFF)
# Use only in targeted canaries; keep OFF for general runs
if [ " ${ SMOKES_DEV_NORMALIZE :- 0 } " = "1" ] ; then
export HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE = 1
fi
# Profile-based injection example (commented; enable when needed):
# if [ "${SMOKES_ENABLE_NORMALIZE_FOR_QUICK:-0}" = "1" ] && [ "${SMOKES_CURRENT_PROFILE:-}" = "quick" ]; then
# export HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1
# export HAKO_MIR_BUILDER_NORMALIZE_TAG=1 # optional: show tags in logs for diagnostics
# fi
2025-11-10 19:42:42 +09:00
}
2025-11-11 02:07:12 +09:00
# Dev profile helpers (EXE/AOT bring-up)
# Sets environment defaults for LLVM crate backend and EXE link paths.
# Usage: call enable_exe_dev_env in EXE canaries.
enable_exe_dev_env( ) {
# Prefer crate backend when available
export NYASH_LLVM_BACKEND = ${ NYASH_LLVM_BACKEND :- crate }
# Tool locations (override when cross)
export NYASH_NY_LLVM_COMPILER = ${ NYASH_NY_LLVM_COMPILER :- " $NYASH_ROOT /target/release/ny-llvmc " }
# NyRT (kernel) lib search path for linking EXEs
export NYASH_EMIT_EXE_NYRT = ${ NYASH_EMIT_EXE_NYRT :- " $NYASH_ROOT /target/release " }
# Optional verification toggles (kept ON by default only for canaries that opt-in)
export NYASH_LLVM_VERIFY = ${ NYASH_LLVM_VERIFY :- 0 }
export NYASH_LLVM_VERIFY_IR = ${ NYASH_LLVM_VERIFY_IR :- 0 }
}