diff --git a/crates/nyash_kernel/src/lib.rs b/crates/nyash_kernel/src/lib.rs index 0890cfec..2b21666a 100644 --- a/crates/nyash_kernel/src/lib.rs +++ b/crates/nyash_kernel/src/lib.rs @@ -779,10 +779,29 @@ pub extern "C" fn main() -> i32 { } // SAFETY: if not linked, calling will be an unresolved symbol at link-time; we rely on link step to include ny_main. let v = ny_main(); + let exit_code: i64 = { + use nyash_rust::{ + box_trait::{IntegerBox, NyashBox}, + runtime::host_handles as handles, + }; + if v > 0 { + if let Some(obj) = handles::get(v as u64) { + if let Some(ib) = obj.as_any().downcast_ref::() { + ib.value as i64 + } else { + 0 + } + } else { + v + } + } else { + v + } + }; // Print standardized result line for golden comparisons (can be silenced for tests) let silent = std::env::var("NYASH_NYRT_SILENT_RESULT").ok().as_deref() == Some("1"); if !silent { - println!("Result: {}", v); + println!("Result: {}", exit_code); } // Optional GC metrics after program completes let want_json = std::env::var("NYASH_GC_METRICS_JSON").ok().as_deref() == Some("1"); @@ -857,7 +876,7 @@ pub extern "C" fn main() -> i32 { // ✂️ REMOVED: Legacy JIT leak diagnostics - part of 42% deletable functions // Leak diagnostics functionality removed with JIT archival // handles::type_tally() no longer available in Plugin-First architecture - v as i32 + exit_code as i32 } } diff --git a/src/llvm_py/builders/function_lower.py b/src/llvm_py/builders/function_lower.py index a6b83a48..eeb95122 100644 --- a/src/llvm_py/builders/function_lower.py +++ b/src/llvm_py/builders/function_lower.py @@ -181,23 +181,38 @@ def lower_function(builder, func_data: Dict[str, Any]): if entry_bid is None and blocks: entry_bid = blocks[0].get("id", 0) - # Compute approx preds-first order - visited = set() - order: List[int] = [] + # Compute reverse-postorder over successors (SSOT): + # - Ensures a stable, mostly-forward lowering order (preds before succs) even with loops. + # - Avoids lowering a block before its dominating setup/copies when possible. + visited: set[int] = set() + post: List[int] = [] + try: + from cfg.utils import build_preds_succs as _build_preds_succs + _preds2, succs2 = _build_preds_succs(block_by_id) + except Exception: + succs2 = {} - def visit(bid: int): + def dfs(bid: int): if bid in visited: return visited.add(bid) - for p in builder.preds.get(bid, []): - visit(p) - order.append(bid) + try: + succ_list = list(succs2.get(bid, []) or []) + succ_list = [int(x) for x in succ_list] + succ_list.sort() + except Exception: + succ_list = [] + for s in succ_list: + dfs(s) + post.append(bid) if entry_bid is not None: - visit(entry_bid) - for bid in block_by_id.keys(): + dfs(int(entry_bid)) + # Include unreachable blocks deterministically + for bid in sorted(block_by_id.keys()): if bid not in visited: - visit(bid) + dfs(int(bid)) + order: List[int] = list(reversed(post)) # Prepass: collect PHI metadata and placeholders _setup_phi_placeholders(builder, blocks) diff --git a/src/llvm_py/instructions/boxcall.py b/src/llvm_py/instructions/boxcall.py index 36c46566..df48c3ab 100644 --- a/src/llvm_py/instructions/boxcall.py +++ b/src/llvm_py/instructions/boxcall.py @@ -9,6 +9,7 @@ from instructions.safepoint import insert_automatic_safepoint from naming_helper import encode_static_method from console_bridge import emit_console_call # Phase 133: Console 箱化モジュール from instructions.stringbox import emit_stringbox_call # Phase 134-B: StringBox 箱化モジュール +from utils.values import resolve_i64_strict def _declare(module: ir.Module, name: str, ret, args): for f in module.functions: @@ -102,9 +103,10 @@ def lower_boxcall( except Exception: pass def _res_i64(vid: int): - if r is not None and p is not None and bev is not None and bbm is not None: + # SSOT: Use the common resolver policy (prefer local SSA, then global vmap, then PHI-localize). + if r is not None and p is not None and bev is not None: try: - return r.resolve_i64(vid, builder.block, p, bev, vmap, bbm) + return resolve_i64_strict(r, vid, builder.block, p, bev, vmap, bbm) except Exception: return None return vmap.get(vid) diff --git a/src/llvm_py/instructions/copy.py b/src/llvm_py/instructions/copy.py index 3f26143e..7aa16cac 100644 --- a/src/llvm_py/instructions/copy.py +++ b/src/llvm_py/instructions/copy.py @@ -48,3 +48,13 @@ def lower_copy( if os.environ.get('NYASH_LLVM_VMAP_TRACE') == '1': print(f"[vmap/id] copy dst={dst} src={src} vmap id={id(vmap)} before_write", file=sys.stderr) safe_vmap_write(vmap, dst, val, "copy", resolver=resolver) + + # TypeFacts propagation (SSOT): preserve "stringish" tagging across Copy. + # Many MIR patterns materialize a temp then Copy into a local; without this, + # string equality/concat may incorrectly fall back to integer/handle ops. + try: + if resolver is not None and hasattr(resolver, "is_stringish") and resolver.is_stringish(src): + if hasattr(resolver, "mark_string"): + resolver.mark_string(dst) + except Exception: + pass diff --git a/src/llvm_py/instructions/mir_call/__init__.py b/src/llvm_py/instructions/mir_call/__init__.py index d65e4917..3298d2b2 100644 --- a/src/llvm_py/instructions/mir_call/__init__.py +++ b/src/llvm_py/instructions/mir_call/__init__.py @@ -117,10 +117,20 @@ def lower_mir_call(owner, builder: ir.IRBuilder, mir_call: Dict[str, Any], dst_v method = callee.get("name") box_name = callee.get("box_name") receiver = callee.get("receiver") - # v1 JSON: receiver is implicit as first arg if missing - if receiver is None and args: - receiver = args[0] - args = args[1:] # Remove receiver from args + certainty = callee.get("certainty") + + # SSOT: Method calls split into two routes: + # - Static method (receiver=null, certainty=Known): lower as direct function call `Box.method/$arity` + # - Instance method (receiver omitted in v1 JSON): receiver is implicit as first arg + if receiver is None: + if certainty == "Known" and box_name and method: + func_name = f"{box_name}.{method}/{len(args)}" + lower_global_call(builder, owner.module, func_name, args, dst_vid, vmap, resolver, owner) + return + if args: + receiver = args[0] + args = args[1:] # Remove receiver from args + lower_method_call(builder, owner.module, box_name, method, receiver, args, dst_vid, vmap, resolver, owner) elif callee_type == "Constructor": diff --git a/src/llvm_py/instructions/mir_call/global_call.py b/src/llvm_py/instructions/mir_call/global_call.py index a3911404..63e8da26 100644 --- a/src/llvm_py/instructions/mir_call/global_call.py +++ b/src/llvm_py/instructions/mir_call/global_call.py @@ -82,19 +82,42 @@ def lower_global_call(builder, module, func_name, args, dst_vid, vmap, resolver, if i < len(func.args): expected_type = func.args[i].type if expected_type.is_pointer and isinstance(arg_val.type, ir.IntType): - # Phase 131-15-P1: Convert i64 handle to i8* for C ABI functions - # Use nyash.string.to_i8p_h for proper handle-to-pointer conversion + # Convert i64 to i8* for C ABI-style functions (print/panic/error). + # + # IMPORTANT: `print()` in AOT must not interpret unboxed integers as handles + # (handle collision prints wrong strings). Use type facts to decide: + # - stringish -> treat as handle and bridge via nyash.string.to_i8p_h + # - otherwise -> box integer (nyash.box.from_i64) then bridge if arg_val.type.width == 64: + i8p = ir.IntType(8).as_pointer() to_i8p = None for f in module.functions: if f.name == "nyash.string.to_i8p_h": to_i8p = f break if not to_i8p: - i8p = ir.IntType(8).as_pointer() to_i8p_type = ir.FunctionType(i8p, [ir.IntType(64)]) to_i8p = ir.Function(module, to_i8p_type, name="nyash.string.to_i8p_h") - arg_val = builder.call(to_i8p, [arg_val], name=f"global_h2p_{i}") + + is_stringish = False + try: + if resolver is not None and hasattr(resolver, "is_stringish") and resolver.is_stringish(int(arg_id)): + is_stringish = True + except Exception: + is_stringish = False + + v_to_print = arg_val + if func_name == "print" and not is_stringish: + boxer = None + for f in module.functions: + if f.name == "nyash.box.from_i64": + boxer = f + break + if boxer is None: + boxer = ir.Function(module, ir.FunctionType(ir.IntType(64), [ir.IntType(64)]), name="nyash.box.from_i64") + v_to_print = builder.call(boxer, [arg_val], name=f"global_box_i64_{i}") + + arg_val = builder.call(to_i8p, [v_to_print], name=f"global_h2p_{i}") else: arg_val = builder.inttoptr(arg_val, expected_type, name=f"global_i2p_{i}") elif isinstance(expected_type, ir.IntType) and arg_val.type.is_pointer: diff --git a/src/llvm_py/instructions/mir_call/method_call.py b/src/llvm_py/instructions/mir_call/method_call.py index 9370bffe..2ca5db48 100644 --- a/src/llvm_py/instructions/mir_call/method_call.py +++ b/src/llvm_py/instructions/mir_call/method_call.py @@ -64,13 +64,20 @@ def lower_method_call(builder, module, box_name, method, receiver, args, dst_vid # Resolve receiver and arguments def _resolve_arg(vid: int): + # Prefer same-block SSA (including function params) from vmap. + try: + v = vmap.get(vid) + if v is not None: + return v + except Exception: + pass if resolver and hasattr(resolver, 'resolve_i64'): try: return resolver.resolve_i64(vid, builder.block, owner.preds, owner.block_end_values, vmap, owner.bb_map) except: pass - return vmap.get(vid) + return None recv_val = _resolve_arg(receiver) if recv_val is None: @@ -154,9 +161,17 @@ def lower_method_call(builder, module, box_name, method, receiver, args, dst_vid else: # Generic plugin method invocation method_str = method.encode('utf-8') + b'\0' - method_global = ir.GlobalVariable(module, ir.ArrayType(i8, len(method_str)), name=f"unified_method_{method}") - method_global.initializer = ir.Constant(ir.ArrayType(i8, len(method_str)), bytearray(method_str)) - method_global.global_constant = True + method_gname = f"unified_method_{method}" + if method_gname in module.globals: + method_global = module.get_global(method_gname) + else: + method_global = ir.GlobalVariable( + module, ir.ArrayType(i8, len(method_str)), name=method_gname + ) + method_global.initializer = ir.Constant( + ir.ArrayType(i8, len(method_str)), bytearray(method_str) + ) + method_global.global_constant = True mptr = builder.gep(method_global, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)]) argc = ir.Constant(i64, len(args)) diff --git a/src/llvm_py/phi_manager.py b/src/llvm_py/phi_manager.py index 4b59f259..d9d40469 100644 --- a/src/llvm_py/phi_manager.py +++ b/src/llvm_py/phi_manager.py @@ -29,11 +29,8 @@ class PhiManager: """ result = {} for vid, val in vmap.items(): - if hasattr(val, 'add_incoming'): # Is PHI? - if self.is_phi_owned(target_bid, vid): - result[vid] = val - else: - result[vid] = val + # PHIs are valid SSA values across dominated blocks; keep them in the per-block view. + result[vid] = val # Phase 132-P0: Add PHIs from predeclared that aren't in vmap yet for (bid, vid), phi_val in self.predeclared.items(): diff --git a/src/llvm_py/phi_wiring/tagging.py b/src/llvm_py/phi_wiring/tagging.py index 237ce595..6f521470 100644 --- a/src/llvm_py/phi_wiring/tagging.py +++ b/src/llvm_py/phi_wiring/tagging.py @@ -103,8 +103,13 @@ def setup_phi_placeholders(builder, blocks: List[Dict[str, Any]]): dst_type0 = inst.get("dst_type") mark_str = ( isinstance(dst_type0, dict) - and dst_type0.get("kind") == "handle" - and dst_type0.get("box_type") == "StringBox" + and ( + dst_type0.get("kind") == "string" + or ( + dst_type0.get("kind") == "handle" + and dst_type0.get("box_type") == "StringBox" + ) + ) ) if not mark_str: # JSON v0 incoming pairs are (value, block) diff --git a/src/llvm_py/phi_wiring/wiring.py b/src/llvm_py/phi_wiring/wiring.py index 3d74372e..a24ce11a 100644 --- a/src/llvm_py/phi_wiring/wiring.py +++ b/src/llvm_py/phi_wiring/wiring.py @@ -267,5 +267,22 @@ def finalize_phis(builder, context): total_dsts += 1 wired = wire_incomings(builder, int(block_id), int(dst_vid), incoming, context=context) total_wired += int(wired or 0) + # TypeFacts propagation (SSOT): if any incoming source is stringish, mark dst stringish. + # This prevents string PHIs from falling back to integer/handle paths (e.g., out += ch). + try: + if ( + hasattr(builder, "resolver") + and hasattr(builder.resolver, "is_stringish") + and hasattr(builder.resolver, "mark_string") + ): + for (_decl_b, v_src) in (incoming or []): + try: + if builder.resolver.is_stringish(int(v_src)): + builder.resolver.mark_string(int(dst_vid)) + break + except Exception: + continue + except Exception: + pass trace({"phi": "finalize", "block": int(block_id), "dst": int(dst_vid), "wired": int(wired or 0)}) trace({"phi": "finalize_summary", "blocks": int(total_blocks), "dsts": int(total_dsts), "incoming_wired": int(total_wired)}) diff --git a/src/llvm_py/resolver.py b/src/llvm_py/resolver.py index f1428ffc..116efa6d 100644 --- a/src/llvm_py/resolver.py +++ b/src/llvm_py/resolver.py @@ -97,7 +97,24 @@ class Resolver: def mark_string(self, value_id: int) -> None: try: - self.string_ids.add(int(value_id)) + vid = int(value_id) + self.string_ids.add(vid) + # TypeFacts SSOT: keep value_types in sync so downstream decisions + # (e.g., '+' concat tag checks) can treat this as a StringBox handle. + try: + if not hasattr(self, 'value_types') or self.value_types is None: + self.value_types = {} + cur = self.value_types.get(vid) if isinstance(self.value_types, dict) else None + is_already_string = False + if isinstance(cur, dict): + if cur.get('kind') == 'string': + is_already_string = True + if cur.get('kind') == 'handle' and cur.get('box_type') == 'StringBox': + is_already_string = True + if not is_already_string and isinstance(self.value_types, dict): + self.value_types[vid] = {'kind': 'handle', 'box_type': 'StringBox'} + except Exception: + pass except Exception: pass @@ -478,16 +495,11 @@ class Resolver: except Exception: pass if is_phi_val: - # Accept PHI only when it belongs to the same block (dominates end-of-block). - try: - belongs_here = (getattr(getattr(val, 'basic_block', None), 'name', b'').decode() if hasattr(getattr(val, 'basic_block', None), 'name') else str(getattr(getattr(val, 'basic_block', None), 'name', ''))) == f"bb{block_id}" - except Exception: - belongs_here = False - if belongs_here: - coerced = self._coerce_in_block_to_i64(val, block_id, bb_map) - self._end_i64_cache[key] = coerced - return coerced - # Otherwise, PHI from wrong block -> treat as miss + # PHIs are valid SSA values to carry through snapshots: a PHI defined at a + # dominating block head can be used at the end of successor blocks. + coerced = self._coerce_in_block_to_i64(val, block_id, bb_map) + self._end_i64_cache[key] = coerced + return coerced else: coerced = self._coerce_in_block_to_i64(val, block_id, bb_map) self._end_i64_cache[key] = coerced diff --git a/src/runtime/plugin_loader_v2/enabled/loader/library.rs b/src/runtime/plugin_loader_v2/enabled/loader/library.rs index 52ece2aa..14b82870 100644 --- a/src/runtime/plugin_loader_v2/enabled/loader/library.rs +++ b/src/runtime/plugin_loader_v2/enabled/loader/library.rs @@ -2,6 +2,7 @@ use super::specs; use super::util::dbg_on; use super::PluginLoaderV2; use crate::bid::{BidError, BidResult}; +use crate::config::env::env_bool; use crate::config::nyash_toml_v2::LibraryDefinition; use crate::runtime::get_global_ring0; use libloading::{Library, Symbol}; @@ -11,6 +12,10 @@ use std::sync::Arc; pub(super) fn load_all_plugins(loader: &PluginLoaderV2) -> BidResult<()> { let config = loader.config.as_ref().ok_or(BidError::PluginError)?; + // Strict mode policy (SSOT): reuse JoinIR strict flag to avoid env-var sprawl. + // - strict=0: Phase 134 best-effort load (continue on failure) + // - strict=1: Fail-Fast on first plugin/library load error + let strict = env_bool("HAKO_JOINIR_STRICT"); // Phase 134 P0: Best-effort loading // Failures don't stop the entire load process @@ -24,8 +29,11 @@ pub(super) fn load_all_plugins(loader: &PluginLoaderV2) -> BidResult<()> { for (lib_name, lib_def) in lib_items { match load_plugin(loader, lib_name, lib_def) { Ok(()) => loaded_count += 1, - Err(_) => { + Err(e) => { failed_count += 1; + if strict { + return Err(e); + } // Log already printed by load_plugin, continue } } @@ -38,8 +46,11 @@ pub(super) fn load_all_plugins(loader: &PluginLoaderV2) -> BidResult<()> { for (plugin_name, root) in plugin_items { match load_plugin_from_root(loader, plugin_name, root) { Ok(()) => loaded_count += 1, - Err(_) => { + Err(e) => { failed_count += 1; + if strict { + return Err(e); + } // Log already printed by load_plugin_from_root, continue } } @@ -82,7 +93,24 @@ pub(super) fn load_plugin( } } } - let lib_path = lib_path.unwrap_or_else(|| base.to_path_buf()); + let lib_path = match lib_path { + Some(path) => path, + None => { + let mut attempted = candidates + .iter() + .map(|p| p.display().to_string()) + .collect::>(); + attempted.sort(); + attempted.dedup(); + get_global_ring0().log.error(&format!( + "[plugin/missing] {}: no existing file for configured path='{}' (attempted={})", + lib_name, + base.display(), + attempted.join(", ") + )); + return Err(BidError::PluginError); + } + }; if dbg_on() { get_global_ring0().log.debug(&format!( "[PluginLoaderV2] load_plugin: lib='{}' path='{}'", diff --git a/tools/smokes/v2/profiles/integration/apps/phase97_json_loader_escape_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase97_json_loader_escape_llvm_exe.sh index f1ac5e9d..758023ef 100644 --- a/tools/smokes/v2/profiles/integration/apps/phase97_json_loader_escape_llvm_exe.sh +++ b/tools/smokes/v2/profiles/integration/apps/phase97_json_loader_escape_llvm_exe.sh @@ -20,8 +20,33 @@ fi FILEBOX_SO="$NYASH_ROOT/plugins/nyash-filebox-plugin/libnyash_filebox_plugin.so" MAPBOX_SO="$NYASH_ROOT/plugins/nyash-map-plugin/libnyash_map_plugin.so" + +# Phase 98 P0: Ensure required dynamic plugin artifacts exist and are loadable. +echo "[INFO] Ensuring plugin artifacts (FileBox/MapBox)" +if ! bash "$NYASH_ROOT/tools/plugins/build-all.sh" nyash-filebox-plugin nyash-map-plugin >/dev/null; then + echo "[FAIL] tools/plugins/build-all.sh failed for FileBox/MapBox" + exit 1 +fi if [ ! -f "$FILEBOX_SO" ] || [ ! -f "$MAPBOX_SO" ]; then - test_skip "Required plugins not found (FileBox/MapBox)"; exit 0 + echo "[FAIL] Required plugin artifacts still missing after build-all (FileBox/MapBox)" + echo "[INFO] FileBox: $FILEBOX_SO" + echo "[INFO] MapBox: $MAPBOX_SO" + exit 1 +fi +if ! python3 - </dev/null; then +import ctypes +ctypes.CDLL(r"$FILEBOX_SO") +ctypes.CDLL(r"$MAPBOX_SO") +print("OK") +PY + echo "[FAIL] Plugin dlopen check failed for FileBox/MapBox" + python3 - <&1 | tail -n 80 +import ctypes +ctypes.CDLL(r"$FILEBOX_SO") +ctypes.CDLL(r"$MAPBOX_SO") +print("OK") +PY + exit 1 fi mkdir -p "$NYASH_ROOT/tmp" @@ -32,7 +57,7 @@ OUTPUT_EXE="$NYASH_ROOT/tmp/phase97_json_loader_escape_llvm_exe" echo "[INFO] Building: $INPUT_HAKO → $OUTPUT_EXE" BUILD_LOG="/tmp/phase97_json_loader_escape_build.log" -if ! env NYASH_DISABLE_PLUGINS=1 "$NYASH_ROOT/tools/build_llvm.sh" "$INPUT_HAKO" -o "$OUTPUT_EXE" 2>&1 | tee "$BUILD_LOG"; then +if ! env NYASH_DISABLE_PLUGINS=0 "$NYASH_ROOT/tools/build_llvm.sh" "$INPUT_HAKO" -o "$OUTPUT_EXE" 2>&1 | tee "$BUILD_LOG"; then echo "[FAIL] build_llvm.sh failed" tail -n 80 "$BUILD_LOG" exit 1 @@ -47,7 +72,7 @@ fi echo "[INFO] Executing: $OUTPUT_EXE" set +e -OUTPUT=$(timeout "${RUN_TIMEOUT_SECS:-10}" env NYASH_DISABLE_PLUGINS=1 "$OUTPUT_EXE" 2>&1) +OUTPUT=$(timeout "${RUN_TIMEOUT_SECS:-10}" env NYASH_DISABLE_PLUGINS=0 "$OUTPUT_EXE" 2>&1) EXIT_CODE=$? set -e diff --git a/tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh index 671c1131..3fab667f 100644 --- a/tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh +++ b/tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh @@ -20,8 +20,33 @@ fi FILEBOX_SO="$NYASH_ROOT/plugins/nyash-filebox-plugin/libnyash_filebox_plugin.so" MAPBOX_SO="$NYASH_ROOT/plugins/nyash-map-plugin/libnyash_map_plugin.so" + +# Phase 98 P0: Ensure required dynamic plugin artifacts exist and are loadable. +echo "[INFO] Ensuring plugin artifacts (FileBox/MapBox)" +if ! bash "$NYASH_ROOT/tools/plugins/build-all.sh" nyash-filebox-plugin nyash-map-plugin >/dev/null; then + echo "[FAIL] tools/plugins/build-all.sh failed for FileBox/MapBox" + exit 1 +fi if [ ! -f "$FILEBOX_SO" ] || [ ! -f "$MAPBOX_SO" ]; then - test_skip "Required plugins not found (FileBox/MapBox)"; exit 0 + echo "[FAIL] Required plugin artifacts still missing after build-all (FileBox/MapBox)" + echo "[INFO] FileBox: $FILEBOX_SO" + echo "[INFO] MapBox: $MAPBOX_SO" + exit 1 +fi +if ! python3 - </dev/null; then +import ctypes +ctypes.CDLL(r"$FILEBOX_SO") +ctypes.CDLL(r"$MAPBOX_SO") +print("OK") +PY + echo "[FAIL] Plugin dlopen check failed for FileBox/MapBox" + python3 - <&1 | tail -n 80 +import ctypes +ctypes.CDLL(r"$FILEBOX_SO") +ctypes.CDLL(r"$MAPBOX_SO") +print("OK") +PY + exit 1 fi mkdir -p "$NYASH_ROOT/tmp" @@ -32,7 +57,7 @@ OUTPUT_EXE="$NYASH_ROOT/tmp/phase97_next_non_ws_llvm_exe" echo "[INFO] Building: $INPUT_HAKO → $OUTPUT_EXE" BUILD_LOG="/tmp/phase97_next_non_ws_build.log" -if ! env NYASH_DISABLE_PLUGINS=1 "$NYASH_ROOT/tools/build_llvm.sh" "$INPUT_HAKO" -o "$OUTPUT_EXE" 2>&1 | tee "$BUILD_LOG"; then +if ! env NYASH_DISABLE_PLUGINS=0 "$NYASH_ROOT/tools/build_llvm.sh" "$INPUT_HAKO" -o "$OUTPUT_EXE" 2>&1 | tee "$BUILD_LOG"; then echo "[FAIL] build_llvm.sh failed" tail -n 80 "$BUILD_LOG" exit 1 @@ -47,7 +72,7 @@ fi echo "[INFO] Executing: $OUTPUT_EXE" set +e -OUTPUT=$(timeout "${RUN_TIMEOUT_SECS:-10}" env NYASH_DISABLE_PLUGINS=1 "$OUTPUT_EXE" 2>&1) +OUTPUT=$(timeout "${RUN_TIMEOUT_SECS:-10}" env NYASH_DISABLE_PLUGINS=0 "$OUTPUT_EXE" 2>&1) EXIT_CODE=$? set -e