python-plugin: RAII (PyOwned/PyBorrowed) + autodecode enum; crate: ny-llvmc --emit exe with NyRT link; tools: build_llvm.sh crate-exe path + crate_exe_smoke; CURRENT_TASK update
This commit is contained in:
100
CURRENT_TASK.md
100
CURRENT_TASK.md
@ -5,6 +5,55 @@ Summary
|
|||||||
- PyVM is the semantic reference engine; llvmlite is used for AOT and parity checks.
|
- PyVM is the semantic reference engine; llvmlite is used for AOT and parity checks.
|
||||||
- GC: user modes defined; controller実装(rc+cycle skeleton + metrics/diagnostics)に移行。LLVM safepoint輸出/NyRT配線と自動挿入(envゲートON)を完了。
|
- GC: user modes defined; controller実装(rc+cycle skeleton + metrics/diagnostics)に移行。LLVM safepoint輸出/NyRT配線と自動挿入(envゲートON)を完了。
|
||||||
|
|
||||||
|
Refactoring — Code Quality (High Priority, 2025‑09‑17 夜間)
|
||||||
|
- MIR instruction meta de‑boilerplate:
|
||||||
|
- Added `inst_meta!` macro and migrated major instructions (Unary/Compare/Load/Cast/TypeOp/Array{Get,Set}/Return/Branch/Jump/Print/Debug/Barrier*/Ref*/Weak*/Future*/Phi/NewBox).
|
||||||
|
- File `src/mir/instruction_kinds/mod.rs` shrank ~100 lines; behavior unchanged (introspection only).
|
||||||
|
- Tests safety pass (unwrap elimination):
|
||||||
|
- `src/tests/typebox_tlv_diff.rs` now uses `with_host` + `EnvGuard` + `inv_{ok,some,void}` helpers.
|
||||||
|
- Removed all `.unwrap()` from the file; failures carry context via `expect` or helper panic messages.
|
||||||
|
- Tokenizer duplication cut:
|
||||||
|
- Centralized single‑char token mapping via `single_char_token()`; kept longest‑match logic for multi‑char ops.
|
||||||
|
- Box operators de‑duplicate:
|
||||||
|
- Added `impl_static_numeric_ops!` for Integer/Float static ops (Add/Sub/Mul/Div), preserved zero‑division checks.
|
||||||
|
- Introduced `concat_result` and `can_repeat`; simplified Dynamic* impls and `OperatorResolver` via helper functions.
|
||||||
|
- Net plugin modularization (unsafe/TLV/state split):
|
||||||
|
- New: `plugins/nyash-net-plugin/src/ffi.rs` (CStr/ptr helpers), `tlv.rs` (encode/decode), `state.rs` (global maps/IDs).
|
||||||
|
- `lib.rs` delegates to these modules; unsafe is centralized, TLV logic unified, globals encapsulated.
|
||||||
|
— Python plugin refactor (Phase‑15)
|
||||||
|
- ffi 分離: `plugins/nyash-python-plugin/src/ffi.rs` に CPython ローダ/シンボル/`ensure_cpython()` を移設(挙動等価)。
|
||||||
|
- GILGuard 適用拡大: `eval/import/getattr/str/call/callKw` の全パスを `gil::GILGuard` で保護。手動 Ensure/Release を撤去。
|
||||||
|
- pytypes 導入: `plugins/nyash-python-plugin/src/pytypes.rs`
|
||||||
|
- TLV→Py 変換を集約: `count_tlv_args/tuple_from_tlv/kwargs_from_tlv`(内部 `fill_*` で unsafe を局在化)。
|
||||||
|
- `take_py_error_string` を移設し、lib 側からの呼び出しを置換。
|
||||||
|
- 参照ヘルパ(`incref/decref`)と CString/CStr ヘルパを用意(段階移行用)。
|
||||||
|
- 旧ロジックの削除/整理:
|
||||||
|
- `lib.rs` の `fill_kwargs_from_tlv` を削除(pytypes へ移行済み)。
|
||||||
|
- 旧 `tlv_count_args` と `fill_tuple_from_tlv` は廃止(コメント化→撤去予定)。
|
||||||
|
- ビルド: `cargo check -p nyash-python-plugin` 警告ゼロ、Net プラグインも警告ゼロを維持。
|
||||||
|
|
||||||
|
Done (2025‑09‑18)
|
||||||
|
- Python plugin refactor 完了
|
||||||
|
- RAII: `PyOwned`/`PyBorrowed` 導入+ `OBJ_PTRS` 撤去。参照は型で管理(Drop=DecRef)。
|
||||||
|
- autodecode を `pytypes` に移設(`DecodedValue`)。呼び出し側は TLV 書き戻しのみ。
|
||||||
|
- CString/CStr/bytes 変換を `pytypes` に統一。
|
||||||
|
- ログ出力を `NYASH_PY_LOG` でガードし既定静音化。
|
||||||
|
- クリーンアップ: 過剰 `allow(dead_code)` の縮小とコメント整理。
|
||||||
|
- ny-llvmc: EXE 出力対応
|
||||||
|
- `--emit exe/obj`、`--nyrt <dir>`、`--libs` を実装。`.o` 生成後に NyRT とリンクして実行可能に。
|
||||||
|
- 代表ケースで EXE 実行(exit code)を確認。
|
||||||
|
|
||||||
|
Next (Immediate)
|
||||||
|
- tools/build_llvm.sh の crate→EXE 統合
|
||||||
|
- `NYASH_LLVM_COMPILER=crate` かつ `NYASH_LLVM_EMIT=exe` の場合、`ny-llvmc --emit exe` を呼び出し、手動リンクをスキップ。
|
||||||
|
- `NYASH_LLVM_NYRT`/`NYASH_LLVM_LIBS` 環境変数でリンク先/追加フラグを指定可能に(Linux 既定は `-ldl -lpthread -lm`)。
|
||||||
|
- Self‑host/EXE スモークの整備
|
||||||
|
- 代表3ケース(const/binop/branch など)の JSON→ny-llvmc→EXE→実行をワンショットで検証するスクリプト(Linux)。
|
||||||
|
- 既存 `exe_first_smoke.sh`/`build_compiler_exe.sh` の補助として crate 直結経路を並行維持。
|
||||||
|
- CI 追補
|
||||||
|
- Linux ジョブに crate‑EXE ルートの最小スモークを追加(exit code 判定のみ)。
|
||||||
|
|
||||||
|
|
||||||
What Changed (recent)
|
What Changed (recent)
|
||||||
- MIR13 default enabled
|
- MIR13 default enabled
|
||||||
- `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`).
|
- `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`).
|
||||||
@ -173,28 +222,31 @@ Plugin ABI v2 updates (2025‑09‑17)
|
|||||||
- `tools/build_llvm.sh` に `NYASH_LLVM_COMPILER=crate|harness` を追加(`crate` は `ny-llvmc`。JSON は `NYASH_LLVM_MIR_JSON` 指定)
|
- `tools/build_llvm.sh` に `NYASH_LLVM_COMPILER=crate|harness` を追加(`crate` は `ny-llvmc`。JSON は `NYASH_LLVM_MIR_JSON` 指定)
|
||||||
- JSON スキーマ検証を可能なら実行(`tools/validate_mir_json.py`)
|
- JSON スキーマ検証を可能なら実行(`tools/validate_mir_json.py`)
|
||||||
|
|
||||||
Plugin ABI v2 updates (2025‑09‑17 — Python family + Net smoke + JSON emit)
|
Plugin ABI v2 updates — 完了報告(2025‑09‑17)
|
||||||
- v2 migration(Python 系 完了)
|
- v2 migration(全 first‑party 完了)
|
||||||
- `plugins/nyash-python-plugin`: `nyash_typebox_PyRuntimeBox` / `nyash_typebox_PyObjectBox` を追加(resolve/invoke_id 実装。既存 v1 は残存)
|
- Python 系: `PyRuntimeBox`/`PyObjectBox`/`PythonParserBox`/`PythonCompilerBox` を v2 化
|
||||||
- `plugins/nyash-python-parser-plugin`: `nyash_typebox_PythonParserBox` を追加(birth/parse/fini)
|
- 既存 first‑party(File/Path/Math/Time/Regex/Net/String/Array/Map/Integer/Console)を v2 化
|
||||||
- `plugins/nyash-python-compiler-plugin`: `nyash_typebox_PythonCompilerBox` を追加(birth/compile/fini)
|
- Encoding/TOML も v2 追加(`EncodingBox`/`TOMLBox`)し `nyash.toml` に登録
|
||||||
- `nyash.toml` に Python 系 3 ライブラリを登録(type_id: 40/41/60/61)
|
- Legacy 撤去
|
||||||
- Net 往復スモーク(最小)
|
- 旧ローダ(`src/runtime/plugin_loader_legacy.rs`)と旧C‑ABI FileBox を削除
|
||||||
- 追加: `apps/tests/net_roundtrip.nyash`(Server.start→Client.get→Server.accept/respond→Client.readBody)
|
- 全プラグインの v1 エクスポート(abi/init/invoke)を物理削除(v2専用化)
|
||||||
- 追加: `tools/plugin_v2_smoke.sh` に Net 機能スモークを条件付きで実行(CI常時ジョブに内包)
|
- スモーク/CI
|
||||||
- nyash → MIR JSON emit フラグ
|
- v2 ロード+機能(Regex/Response/Net往復)スモークを常時
|
||||||
- CLI `--emit-mir-json <path>` を追加(`src/cli.rs`)。`runner.execute_mir_module` でファイル出力→即終了を実装。
|
- `ny-llvmc`(crate)で .o 生成するCIジョブを追加(Linux)
|
||||||
- これにより `ny-llvmc` へ JSON を直結しやすくなった(次の CI 経路で使用予定)
|
- nyash → MIR JSON emit
|
||||||
|
- CLI `--emit-mir-json <path>` を追加し、`ny-llvmc` 直結導線を整備
|
||||||
|
|
||||||
Plan after restart(次の計画)
|
Next — Self‑Hosting/EXE(crate 直結)
|
||||||
- Python 系プラグインの v2 化(parser/compiler/python-plugin)
|
- ny-llvmc 機能拡張(.exe 出力)
|
||||||
- Docs 追記(Net/Regex のメソッド表、型/戻りTLVの簡易表)
|
- `ny-llvmc --emit exe --out <path>` を実装(`.o` + NyRT リンク)。`--nyrt <dir>`/`--libs <extra>` を受理
|
||||||
- スモーク強化
|
- 既存 `tools/build_llvm.sh` の crate 経路と統合(env: `NYASH_LLVM_COMPILER=crate`)
|
||||||
- Net: `ServerBox.start -> Client.get -> Request.respond -> Response.readBody` の往復最小ケースを追加
|
- Linux でのリンクフラグ最小化(`-Wl,--whole-archive -lnyrt -Wl,--no-whole-archive -ldl -lpthread -lm`)
|
||||||
- 主要 v2 Box の軽機能(String/Array/Map/Regex/Path/Math/Time)を 1 ジョブで走らせる
|
- CI 拡張
|
||||||
- LLVM 共通化
|
- `.o` 生成に加え、`.exe` 生成+実行(exit code 検証)ジョブを追加(Linux)
|
||||||
- `nyash` からの JSON emit コマンド/フラグ導入(`--emit-mir-json <path>` など)→ `ny-llvmc` 直結
|
- 代表3ケース(const/binop/branch)で EXE を起動し `0` 戻りを確認
|
||||||
- CI に `ny-llvmc` 実 JSON 経路を追加(Linux 常時)
|
- Self‑host pipeline 寄り
|
||||||
- NyRT 整理(軽)
|
- nyash CLI `--emit-mir-json` を EXE-first パスにも活用(JSON → ny-llvmc → exe → 実行)
|
||||||
- TLV/エラー定数を `include/nyash_abi.h` と整合させる(ヘッダ経由参照)
|
- 将来: PyVM/llvmlite パリティベンチ(小規模)→ EXE でも同値を継続確認
|
||||||
- (必要時)`nyrt_last_error()` の追加検討
|
- Docs/Guides 更新
|
||||||
|
- `docs/LLVM_HARNESS.md` に ny-llvmc の exe 出力手順を追記
|
||||||
|
- `docs/guides/selfhost-pilot.md` に crate 直結(.o/.exe)手順とトラブルシュート
|
||||||
|
|||||||
@ -7,13 +7,16 @@ use anyhow::{bail, Context, Result};
|
|||||||
use clap::{ArgAction, Parser};
|
use clap::{ArgAction, Parser};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "ny-llvmc", about = "Nyash LLVM compiler (llvmlite harness wrapper)")]
|
#[command(
|
||||||
|
name = "ny-llvmc",
|
||||||
|
about = "Nyash LLVM compiler (llvmlite harness wrapper)"
|
||||||
|
)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// MIR JSON input file path (use '-' to read from stdin). When omitted with --dummy, a dummy ny_main is emitted.
|
/// MIR JSON input file path (use '-' to read from stdin). When omitted with --dummy, a dummy ny_main is emitted.
|
||||||
#[arg(long = "in", value_name = "FILE", default_value = "-")]
|
#[arg(long = "in", value_name = "FILE", default_value = "-")]
|
||||||
infile: String,
|
infile: String,
|
||||||
|
|
||||||
/// Output object file (.o)
|
/// Output path. For `--emit obj`, this is an object (.o). For `--emit exe`, this is an executable path.
|
||||||
#[arg(long, value_name = "FILE")]
|
#[arg(long, value_name = "FILE")]
|
||||||
out: PathBuf,
|
out: PathBuf,
|
||||||
|
|
||||||
@ -24,6 +27,18 @@ struct Args {
|
|||||||
/// Path to Python harness script (defaults to tools/llvmlite_harness.py in CWD)
|
/// Path to Python harness script (defaults to tools/llvmlite_harness.py in CWD)
|
||||||
#[arg(long, value_name = "FILE")]
|
#[arg(long, value_name = "FILE")]
|
||||||
harness: Option<PathBuf>,
|
harness: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Emit kind: 'obj' (default) or 'exe'.
|
||||||
|
#[arg(long, value_name = "{obj|exe}", default_value = "obj")]
|
||||||
|
emit: String,
|
||||||
|
|
||||||
|
/// Path to directory containing libnyrt.a when emitting an executable. If omitted, searches target/release then crates/nyrt/target/release.
|
||||||
|
#[arg(long, value_name = "DIR")]
|
||||||
|
nyrt: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Extra linker libs/flags appended when emitting an executable (single string, space-separated).
|
||||||
|
#[arg(long, value_name = "FLAGS")]
|
||||||
|
libs: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@ -41,10 +56,27 @@ fn main() -> Result<()> {
|
|||||||
PathBuf::from("tools/llvmlite_harness.py")
|
PathBuf::from("tools/llvmlite_harness.py")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Determine emit kind
|
||||||
|
let emit_exe = matches!(args.emit.as_str(), "exe" | "EXE");
|
||||||
|
|
||||||
if args.dummy {
|
if args.dummy {
|
||||||
run_harness_dummy(&harness_path, &args.out)
|
// Dummy ny_main: always go through harness to produce an object then link if requested
|
||||||
|
let obj_path = if emit_exe {
|
||||||
|
// derive a temporary .o path next to output
|
||||||
|
let mut p = args.out.clone();
|
||||||
|
p.set_extension("o");
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
args.out.clone()
|
||||||
|
};
|
||||||
|
run_harness_dummy(&harness_path, &obj_path)
|
||||||
.with_context(|| "failed to run harness in dummy mode")?;
|
.with_context(|| "failed to run harness in dummy mode")?;
|
||||||
println!("[ny-llvmc] dummy object written: {}", args.out.display());
|
if emit_exe {
|
||||||
|
link_executable(&obj_path, &args.out, args.nyrt.as_ref(), args.libs.as_deref())?;
|
||||||
|
println!("[ny-llvmc] executable written: {}", args.out.display());
|
||||||
|
} else {
|
||||||
|
println!("[ny-llvmc] dummy object written: {}", obj_path.display());
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +88,8 @@ fn main() -> Result<()> {
|
|||||||
.read_to_string(&mut buf)
|
.read_to_string(&mut buf)
|
||||||
.context("reading MIR JSON from stdin")?;
|
.context("reading MIR JSON from stdin")?;
|
||||||
// Basic sanity check that it's JSON
|
// Basic sanity check that it's JSON
|
||||||
let _: serde_json::Value = serde_json::from_str(&buf)
|
let _: serde_json::Value =
|
||||||
.context("stdin does not contain valid JSON")?;
|
serde_json::from_str(&buf).context("stdin does not contain valid JSON")?;
|
||||||
let tmp = std::env::temp_dir().join("ny_llvmc_stdin.json");
|
let tmp = std::env::temp_dir().join("ny_llvmc_stdin.json");
|
||||||
let mut f = File::create(&tmp).context("create temp json file")?;
|
let mut f = File::create(&tmp).context("create temp json file")?;
|
||||||
f.write_all(buf.as_bytes()).context("write temp json")?;
|
f.write_all(buf.as_bytes()).context("write temp json")?;
|
||||||
@ -71,9 +103,27 @@ fn main() -> Result<()> {
|
|||||||
bail!("input JSON not found: {}", input_path.display());
|
bail!("input JSON not found: {}", input_path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
run_harness_in(&harness_path, &input_path, &args.out)
|
// Produce object first
|
||||||
.with_context(|| format!("failed to compile MIR JSON via harness: {}", input_path.display()))?;
|
let obj_path = if emit_exe {
|
||||||
println!("[ny-llvmc] object written: {}", args.out.display());
|
let mut p = args.out.clone();
|
||||||
|
p.set_extension("o");
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
args.out.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
run_harness_in(&harness_path, &input_path, &obj_path).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to compile MIR JSON via harness: {}",
|
||||||
|
input_path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if emit_exe {
|
||||||
|
link_executable(&obj_path, &args.out, args.nyrt.as_ref(), args.libs.as_deref())?;
|
||||||
|
println!("[ny-llvmc] executable written: {}", args.out.display());
|
||||||
|
} else {
|
||||||
|
println!("[ny-llvmc] object written: {}", obj_path.display());
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup temp file if used
|
// Cleanup temp file if used
|
||||||
if let Some(p) = temp_path {
|
if let Some(p) = temp_path {
|
||||||
@ -120,3 +170,39 @@ fn ensure_python() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn link_executable(obj: &Path, out_exe: &Path, nyrt_dir_opt: Option<&PathBuf>, extra_libs: Option<&str>) -> Result<()> {
|
||||||
|
// Resolve nyRT static lib
|
||||||
|
let nyrt_dir = if let Some(dir) = nyrt_dir_opt {
|
||||||
|
dir.clone()
|
||||||
|
} else {
|
||||||
|
// try target/release then crates/nyrt/target/release
|
||||||
|
let a = PathBuf::from("target/release");
|
||||||
|
let b = PathBuf::from("crates/nyrt/target/release");
|
||||||
|
if a.join("libnyrt.a").exists() { a } else { b }
|
||||||
|
};
|
||||||
|
let libnyrt = nyrt_dir.join("libnyrt.a");
|
||||||
|
if !libnyrt.exists() {
|
||||||
|
bail!("libnyrt.a not found in {} (use --nyrt to specify)", nyrt_dir.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose a C linker
|
||||||
|
let linker = ["cc", "clang", "gcc"].into_iter().find(|c| Command::new(c).arg("--version").output().map(|o| o.status.success()).unwrap_or(false)).unwrap_or("cc");
|
||||||
|
|
||||||
|
let mut cmd = Command::new(linker);
|
||||||
|
cmd.arg("-o").arg(out_exe);
|
||||||
|
cmd.arg(obj);
|
||||||
|
// Whole-archive libnyrt to ensure all objects are linked
|
||||||
|
cmd.arg("-Wl,--whole-archive").arg(&libnyrt).arg("-Wl,--no-whole-archive");
|
||||||
|
// Common libs on Linux
|
||||||
|
cmd.arg("-ldl").arg("-lpthread").arg("-lm");
|
||||||
|
if let Some(extras) = extra_libs {
|
||||||
|
for tok in extras.split_whitespace() {
|
||||||
|
cmd.arg(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let status = cmd.status().context("failed to invoke system linker")?;
|
||||||
|
if !status.success() {
|
||||||
|
bail!("linker exited with status: {:?}", status.code());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -677,7 +677,9 @@ pub extern "C" fn main() -> i32 {
|
|||||||
// Choose GC hooks based on env (default dev: Counting for observability unless explicitly off)
|
// Choose GC hooks based on env (default dev: Counting for observability unless explicitly off)
|
||||||
let mut rt_builder = nyash_rust::runtime::NyashRuntimeBuilder::new();
|
let mut rt_builder = nyash_rust::runtime::NyashRuntimeBuilder::new();
|
||||||
let gc_mode = nyash_rust::runtime::gc_mode::GcMode::from_env();
|
let gc_mode = nyash_rust::runtime::gc_mode::GcMode::from_env();
|
||||||
let controller = std::sync::Arc::new(nyash_rust::runtime::gc_controller::GcController::new(gc_mode));
|
let controller = std::sync::Arc::new(nyash_rust::runtime::gc_controller::GcController::new(
|
||||||
|
gc_mode,
|
||||||
|
));
|
||||||
rt_builder = rt_builder.with_gc_hooks(controller);
|
rt_builder = rt_builder.with_gc_hooks(controller);
|
||||||
let rt_hooks = rt_builder.build();
|
let rt_hooks = rt_builder.build();
|
||||||
nyash_rust::runtime::global_hooks::set_from_runtime(&rt_hooks);
|
nyash_rust::runtime::global_hooks::set_from_runtime(&rt_hooks);
|
||||||
@ -720,16 +722,23 @@ pub extern "C" fn main() -> i32 {
|
|||||||
let want_json = std::env::var("NYASH_GC_METRICS_JSON").ok().as_deref() == Some("1");
|
let want_json = std::env::var("NYASH_GC_METRICS_JSON").ok().as_deref() == Some("1");
|
||||||
let want_text = std::env::var("NYASH_GC_METRICS").ok().as_deref() == Some("1");
|
let want_text = std::env::var("NYASH_GC_METRICS").ok().as_deref() == Some("1");
|
||||||
if want_json || want_text {
|
if want_json || want_text {
|
||||||
let (sp, br, bw) = rt_hooks
|
let (sp, br, bw) = rt_hooks.gc.snapshot_counters().unwrap_or((0, 0, 0));
|
||||||
.gc
|
|
||||||
.snapshot_counters()
|
|
||||||
.unwrap_or((0, 0, 0));
|
|
||||||
let handles = nyash_rust::jit::rt::handles::len();
|
let handles = nyash_rust::jit::rt::handles::len();
|
||||||
let gc_mode_s = gc_mode.as_str();
|
let gc_mode_s = gc_mode.as_str();
|
||||||
// Include allocation totals if controller is used
|
// Include allocation totals if controller is used
|
||||||
let any_gc: &dyn std::any::Any = &*rt_hooks.gc;
|
let any_gc: &dyn std::any::Any = &*rt_hooks.gc;
|
||||||
let (alloc_count, alloc_bytes, trial_nodes, trial_edges, collect_total, collect_sp, collect_alloc, last_ms, last_reason) = if let Some(ctrl) = any_gc
|
let (
|
||||||
.downcast_ref::<nyash_rust::runtime::gc_controller::GcController>()
|
alloc_count,
|
||||||
|
alloc_bytes,
|
||||||
|
trial_nodes,
|
||||||
|
trial_edges,
|
||||||
|
collect_total,
|
||||||
|
collect_sp,
|
||||||
|
collect_alloc,
|
||||||
|
last_ms,
|
||||||
|
last_reason,
|
||||||
|
) = if let Some(ctrl) =
|
||||||
|
any_gc.downcast_ref::<nyash_rust::runtime::gc_controller::GcController>()
|
||||||
{
|
{
|
||||||
let (ac, ab) = ctrl.alloc_totals();
|
let (ac, ab) = ctrl.alloc_totals();
|
||||||
let (tn, te) = ctrl.trial_reachability_last();
|
let (tn, te) = ctrl.trial_reachability_last();
|
||||||
@ -741,9 +750,18 @@ pub extern "C" fn main() -> i32 {
|
|||||||
(0, 0, 0, 0, 0, 0, 0, 0, 0)
|
(0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
};
|
};
|
||||||
// Settings snapshot (env)
|
// Settings snapshot (env)
|
||||||
let sp_interval = std::env::var("NYASH_GC_COLLECT_SP").ok().and_then(|s| s.parse::<u64>().ok()).unwrap_or(0);
|
let sp_interval = std::env::var("NYASH_GC_COLLECT_SP")
|
||||||
let alloc_thresh = std::env::var("NYASH_GC_COLLECT_ALLOC").ok().and_then(|s| s.parse::<u64>().ok()).unwrap_or(0);
|
.ok()
|
||||||
let auto_sp = std::env::var("NYASH_LLVM_AUTO_SAFEPOINT").ok().map(|v| v == "1").unwrap_or(true);
|
.and_then(|s| s.parse::<u64>().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let alloc_thresh = std::env::var("NYASH_GC_COLLECT_ALLOC")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse::<u64>().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let auto_sp = std::env::var("NYASH_LLVM_AUTO_SAFEPOINT")
|
||||||
|
.ok()
|
||||||
|
.map(|v| v == "1")
|
||||||
|
.unwrap_or(true);
|
||||||
if want_json {
|
if want_json {
|
||||||
// Minimal JSON assembly to avoid extra deps in nyrt
|
// Minimal JSON assembly to avoid extra deps in nyrt
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@ -23,13 +23,11 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
|
|||||||
let nargs = argc.max(0) as usize;
|
let nargs = argc.max(0) as usize;
|
||||||
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
|
||||||
// Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf)
|
// Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf)
|
||||||
let mut encode_from_legacy_into = |dst: &mut Vec<u8>, arg_pos: usize| {
|
let mut encode_from_legacy_into =
|
||||||
nyrt_encode_from_legacy_at(dst, arg_pos)
|
|dst: &mut Vec<u8>, arg_pos: usize| nyrt_encode_from_legacy_at(dst, arg_pos);
|
||||||
};
|
|
||||||
// Encode argument value or fallback to legacy slot (avoid capturing &mut buf)
|
// Encode argument value or fallback to legacy slot (avoid capturing &mut buf)
|
||||||
let mut encode_arg_into = |dst: &mut Vec<u8>, val: i64, pos: usize| {
|
let mut encode_arg_into =
|
||||||
nyrt_encode_arg_or_legacy(dst, val, pos)
|
|dst: &mut Vec<u8>, val: i64, pos: usize| nyrt_encode_arg_or_legacy(dst, val, pos);
|
||||||
};
|
|
||||||
if nargs >= 1 {
|
if nargs >= 1 {
|
||||||
encode_arg_into(&mut buf, a1, 1);
|
encode_arg_into(&mut buf, a1, 1);
|
||||||
}
|
}
|
||||||
@ -203,9 +201,8 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let mut encode_arg = |val: i64, pos: usize| {
|
let mut encode_arg =
|
||||||
crate::encode::nyrt_encode_arg_or_legacy(&mut buf, val, pos)
|
|val: i64, pos: usize| crate::encode::nyrt_encode_arg_or_legacy(&mut buf, val, pos);
|
||||||
};
|
|
||||||
if nargs >= 1 {
|
if nargs >= 1 {
|
||||||
encode_arg(a1, 1);
|
encode_arg(a1, 1);
|
||||||
}
|
}
|
||||||
@ -218,7 +215,8 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Invoke via shared helper
|
// Invoke via shared helper
|
||||||
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = match invoke_core::plugin_invoke_call(
|
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) =
|
||||||
|
match invoke_core::plugin_invoke_call(
|
||||||
invoke.unwrap(),
|
invoke.unwrap(),
|
||||||
type_id as u32,
|
type_id as u32,
|
||||||
method_id as u32,
|
method_id as u32,
|
||||||
@ -425,10 +423,17 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
|
|||||||
&mut out_len,
|
&mut out_len,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if rc != 0 { return 0; }
|
if rc != 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let out_slice = &out[..out_len];
|
let out_slice = &out[..out_len];
|
||||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
|
if let Some((tag, sz, payload)) =
|
||||||
if let Some(v) = super::invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(out_slice)
|
||||||
|
{
|
||||||
|
if let Some(v) = super::invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap())
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -667,8 +672,12 @@ pub extern "C" fn nyash_plugin_invoke3_tagged_i64(
|
|||||||
if rc != 0 {
|
if rc != 0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
if let Some((tag, sz, payload)) =
|
||||||
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||||
|
{
|
||||||
|
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -759,8 +768,12 @@ pub extern "C" fn nyash_plugin_invoke_tagged_v_i64(
|
|||||||
if rc != 0 {
|
if rc != 0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if let Some((tag, sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
if let Some((tag, sz, payload)) =
|
||||||
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) { return v; }
|
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||||
|
{
|
||||||
|
if let Some(v) = invoke_core::decode_entry_to_i64(tag, sz, payload, invoke.unwrap()) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,7 @@ use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
|
|||||||
pub struct Receiver {
|
pub struct Receiver {
|
||||||
pub instance_id: u32,
|
pub instance_id: u32,
|
||||||
pub real_type_id: u32,
|
pub real_type_id: u32,
|
||||||
pub invoke:
|
pub invoke: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve receiver from a0: prefer handle registry; fallback to legacy VM args when allowed.
|
/// Resolve receiver from a0: prefer handle registry; fallback to legacy VM args when allowed.
|
||||||
@ -27,9 +26,7 @@ pub fn resolve_receiver_for_a0(a0: i64) -> Option<Receiver> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2) Legacy VM args (index by a0) unless handle-only is enforced
|
// 2) Legacy VM args (index by a0) unless handle-only is enforced
|
||||||
if a0 >= 0
|
if a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
|
||||||
&& std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1")
|
|
||||||
{
|
|
||||||
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
|
||||||
let idx = a0 as usize;
|
let idx = a0 as usize;
|
||||||
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||||
@ -104,7 +101,15 @@ pub fn decode_entry_to_i64(
|
|||||||
tag: u8,
|
tag: u8,
|
||||||
sz: usize,
|
sz: usize,
|
||||||
payload: &[u8],
|
payload: &[u8],
|
||||||
fallback_invoke: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
fallback_invoke: unsafe extern "C" fn(
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
*const u8,
|
||||||
|
usize,
|
||||||
|
*mut u8,
|
||||||
|
*mut usize,
|
||||||
|
) -> i32,
|
||||||
) -> Option<i64> {
|
) -> Option<i64> {
|
||||||
match tag {
|
match tag {
|
||||||
2 => nyash_rust::runtime::plugin_ffi_common::decode::i32(payload).map(|v| v as i64),
|
2 => nyash_rust::runtime::plugin_ffi_common::decode::i32(payload).map(|v| v as i64),
|
||||||
@ -154,8 +159,10 @@ pub fn decode_entry_to_i64(
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
1 => nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
1 => {
|
||||||
.map(|b| if b { 1 } else { 0 }),
|
nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
||||||
|
.map(|b| if b { 1 } else { 0 })
|
||||||
|
}
|
||||||
5 => {
|
5 => {
|
||||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") && sz == 8 {
|
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") && sz == 8 {
|
||||||
let mut b = [0u8; 8];
|
let mut b = [0u8; 8];
|
||||||
@ -192,8 +199,13 @@ pub fn decode_entry_to_f64(tag: u8, sz: usize, payload: &[u8]) -> Option<f64> {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
1 => nyash_rust::runtime::plugin_ffi_common::decode::bool(payload)
|
1 => nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).map(|b| {
|
||||||
.map(|b| if b { 1.0 } else { 0.0 }),
|
if b {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -158,7 +158,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn encoding_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn encoding_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"toUtf8Bytes" => M_TO_UTF8_BYTES,
|
"toUtf8Bytes" => M_TO_UTF8_BYTES,
|
||||||
@ -184,42 +186,91 @@ extern "C" fn encoding_invoke_id(
|
|||||||
unsafe {
|
unsafe {
|
||||||
match method_id {
|
match method_id {
|
||||||
M_BIRTH => {
|
M_BIRTH => {
|
||||||
if result_len.is_null() { return E_ARGS; }
|
if result_len.is_null() {
|
||||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
return E_ARGS;
|
||||||
|
}
|
||||||
|
if preflight(result, result_len, 4) {
|
||||||
|
return E_SHORT;
|
||||||
|
}
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
if let Ok(mut m) = INST.lock() { m.insert(id, EncInstance); } else { return E_PLUGIN; }
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.insert(id, EncInstance);
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
let b = id.to_le_bytes();
|
let b = id.to_le_bytes();
|
||||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||||
*result_len = 4; OK
|
*result_len = 4;
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
M_FINI => {
|
||||||
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.remove(&instance_id);
|
||||||
|
OK
|
||||||
|
} else {
|
||||||
|
E_PLUGIN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
|
||||||
M_TO_UTF8_BYTES => {
|
M_TO_UTF8_BYTES => {
|
||||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let s = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
write_tlv_bytes(s.as_bytes(), result, result_len)
|
write_tlv_bytes(s.as_bytes(), result, result_len)
|
||||||
}
|
}
|
||||||
M_FROM_UTF8_BYTES => {
|
M_FROM_UTF8_BYTES => {
|
||||||
let bytes = match read_arg_bytes(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let bytes = match read_arg_bytes(args, args_len, 0) {
|
||||||
match String::from_utf8(bytes) { Ok(s) => write_tlv_string(&s, result, result_len), Err(_) => write_tlv_string("", result, result_len) }
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
match String::from_utf8(bytes) {
|
||||||
|
Ok(s) => write_tlv_string(&s, result, result_len),
|
||||||
|
Err(_) => write_tlv_string("", result, result_len),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_BASE64_ENC => {
|
M_BASE64_ENC => {
|
||||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = base64::encode(b); return write_tlv_string(&s, result, result_len); }
|
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let s = base64::encode(b);
|
||||||
|
return write_tlv_string(&s, result, result_len);
|
||||||
|
}
|
||||||
|
let s = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
let enc = base64::encode(s.as_bytes());
|
let enc = base64::encode(s.as_bytes());
|
||||||
write_tlv_string(&enc, result, result_len)
|
write_tlv_string(&enc, result, result_len)
|
||||||
}
|
}
|
||||||
M_BASE64_DEC => {
|
M_BASE64_DEC => {
|
||||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let s = match read_arg_string(args, args_len, 0) {
|
||||||
match base64::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
match base64::decode(s.as_bytes()) {
|
||||||
|
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||||
|
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_HEX_ENC => {
|
M_HEX_ENC => {
|
||||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = hex::encode(b); return write_tlv_string(&s, result, result_len); }
|
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let s = hex::encode(b);
|
||||||
|
return write_tlv_string(&s, result, result_len);
|
||||||
|
}
|
||||||
|
let s = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
let enc = hex::encode(s.as_bytes());
|
let enc = hex::encode(s.as_bytes());
|
||||||
write_tlv_string(&enc, result, result_len)
|
write_tlv_string(&enc, result, result_len)
|
||||||
}
|
}
|
||||||
M_HEX_DEC => {
|
M_HEX_DEC => {
|
||||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
let s = match read_arg_string(args, args_len, 0) {
|
||||||
match hex::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
Some(v) => v,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
match hex::decode(s.as_bytes()) {
|
||||||
|
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||||
|
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => E_METHOD,
|
_ => E_METHOD,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -681,7 +681,11 @@ extern "C" fn filebox_invoke_id(
|
|||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
map.insert(
|
map.insert(
|
||||||
id,
|
id,
|
||||||
FileBoxInstance { file: None, path: String::new(), buffer: None },
|
FileBoxInstance {
|
||||||
|
file: None,
|
||||||
|
path: String::new(),
|
||||||
|
buffer: None,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return NYB_E_PLUGIN_ERROR;
|
return NYB_E_PLUGIN_ERROR;
|
||||||
@ -900,7 +904,11 @@ extern "C" fn filebox_invoke_id(
|
|||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
map.insert(
|
map.insert(
|
||||||
new_id,
|
new_id,
|
||||||
FileBoxInstance { file: None, path: String::new(), buffer: None },
|
FileBoxInstance {
|
||||||
|
file: None,
|
||||||
|
path: String::new(),
|
||||||
|
buffer: None,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Return Handle TLV (type_id from config resolves host-side; we encode (6,new_id) here if needed)
|
// Return Handle TLV (type_id from config resolves host-side; we encode (6,new_id) here if needed)
|
||||||
|
|||||||
@ -127,7 +127,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn mathbox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn mathbox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"sqrt" => M_SQRT,
|
"sqrt" => M_SQRT,
|
||||||
@ -140,7 +142,9 @@ extern "C" fn mathbox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
extern "C" fn timebox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn timebox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"now" => T_NOW,
|
"now" => T_NOW,
|
||||||
|
|||||||
19
plugins/nyash-net-plugin/src/ffi.rs
Normal file
19
plugins/nyash-net-plugin/src/ffi.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
// Safe wrapper: convert C string pointer to owned String.
|
||||||
|
// Safety details are contained within; caller gets a safe String.
|
||||||
|
pub fn cstr_to_string(ptr: *const c_char) -> String {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
unsafe { CStr::from_ptr(ptr) }
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export a safe view over a raw byte slice pointer.
|
||||||
|
// This function is unsafe since it trusts the pointer/length.
|
||||||
|
pub unsafe fn slice<'a>(p: *const u8, len: usize) -> &'a [u8] {
|
||||||
|
std::slice::from_raw_parts(p, len)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
78
plugins/nyash-net-plugin/src/state.rs
Normal file
78
plugins/nyash-net-plugin/src/state.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicU32, Ordering},
|
||||||
|
Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ClientState, RequestState, ResponseState, ServerState, SockClientState, SockConnState,
|
||||||
|
SockServerState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) static SERVER_INSTANCES: Lazy<Mutex<HashMap<u32, ServerState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
pub(crate) static SERVER_START_SEQ: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static ACTIVE_SERVER_ID: Lazy<Mutex<Option<u32>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
pub(crate) static LAST_ACCEPTED_REQ: Lazy<Mutex<Option<u32>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
pub(crate) static REQUESTS: Lazy<Mutex<HashMap<u32, RequestState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
pub(crate) static RESPONSES: Lazy<Mutex<HashMap<u32, ResponseState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
pub(crate) static CLIENTS: Lazy<Mutex<HashMap<u32, ClientState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
pub(crate) static SERVER_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static REQUEST_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static RESPONSE_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static CLIENT_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static SOCK_SERVER_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static SOCK_CONN_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
pub(crate) static SOCK_CLIENT_ID: AtomicU32 = AtomicU32::new(1);
|
||||||
|
|
||||||
|
pub(crate) static SOCK_SERVERS: Lazy<Mutex<HashMap<u32, SockServerState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
pub(crate) static SOCK_CONNS: Lazy<Mutex<HashMap<u32, SockConnState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
pub(crate) static SOCK_CLIENTS: Lazy<Mutex<HashMap<u32, SockClientState>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_server_id() -> u32 {
|
||||||
|
SERVER_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_server_start_seq() -> u32 {
|
||||||
|
SERVER_START_SEQ.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_request_id() -> u32 {
|
||||||
|
REQUEST_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_response_id() -> u32 {
|
||||||
|
RESPONSE_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_client_id() -> u32 {
|
||||||
|
CLIENT_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_sock_server_id() -> u32 {
|
||||||
|
SOCK_SERVER_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_sock_conn_id() -> u32 {
|
||||||
|
SOCK_CONN_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn next_sock_client_id() -> u32 {
|
||||||
|
SOCK_CLIENT_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
181
plugins/nyash-net-plugin/src/tlv.rs
Normal file
181
plugins/nyash-net-plugin/src/tlv.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
use super::{E_INV_ARGS, E_SHORT, OK};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn tlv_result_size(payloads: &[(u8, &[u8])]) -> usize {
|
||||||
|
4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ensure_result_capacity(res: *mut u8, res_len: *mut usize, need: usize) -> Result<(), i32> {
|
||||||
|
if res_len.is_null() {
|
||||||
|
return Err(E_INV_ARGS);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
if res.is_null() || *res_len < need {
|
||||||
|
*res_len = need;
|
||||||
|
return Err(E_SHORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn write_bytes_unchecked(bytes: &[u8], res: *mut u8, res_len: *mut usize) {
|
||||||
|
std::ptr::copy_nonoverlapping(bytes.as_ptr(), res, bytes.len());
|
||||||
|
*res_len = bytes.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_u32(v: u32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
let bytes = v.to_le_bytes();
|
||||||
|
if let Err(err) = ensure_result_capacity(res, res_len, bytes.len()) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
write_bytes_unchecked(&bytes, res, res_len);
|
||||||
|
}
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_tlv_result(payloads: &[(u8, &[u8])], res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
let need = tlv_result_size(payloads);
|
||||||
|
if let Err(err) = ensure_result_capacity(res, res_len, need) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
let mut buf = Vec::with_capacity(need);
|
||||||
|
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||||
|
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||||
|
for (tag, p) in payloads {
|
||||||
|
buf.push(*tag);
|
||||||
|
buf.push(0);
|
||||||
|
buf.extend_from_slice(&(p.len() as u16).to_le_bytes());
|
||||||
|
buf.extend_from_slice(p);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
write_bytes_unchecked(&buf, res, res_len);
|
||||||
|
}
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_tlv_void(res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
write_tlv_result(&[(9u8, &[])], res, res_len)
|
||||||
|
}
|
||||||
|
pub fn write_tlv_string(s: &str, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
write_tlv_result(&[(6u8, s.as_bytes())], res, res_len)
|
||||||
|
}
|
||||||
|
pub fn write_tlv_bytes(b: &[u8], res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
write_tlv_result(&[(7u8, b)], res, res_len)
|
||||||
|
}
|
||||||
|
pub fn write_tlv_i32(v: i32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
write_tlv_result(&[(2u8, &v.to_le_bytes())], res, res_len)
|
||||||
|
}
|
||||||
|
pub fn write_tlv_handle(t: u32, id: u32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||||
|
let mut payload = [0u8; 8];
|
||||||
|
payload[0..4].copy_from_slice(&t.to_le_bytes());
|
||||||
|
payload[4..8].copy_from_slice(&id.to_le_bytes());
|
||||||
|
write_tlv_result(&[(8u8, &payload)], res, res_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tlv_parse_header(data: &[u8]) -> Result<(u16, u16, usize), ()> {
|
||||||
|
if data.len() < 4 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let ver = u16::from_le_bytes([data[0], data[1]]);
|
||||||
|
let argc = u16::from_le_bytes([data[2], data[3]]);
|
||||||
|
if ver != 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok((ver, argc, 4))
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_string(data: &[u8]) -> Result<String, ()> {
|
||||||
|
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
if tag != 6 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(std::str::from_utf8(&data[p..p + size])
|
||||||
|
.map_err(|_| ())?
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_two_strings(data: &[u8]) -> Result<(String, String), ()> {
|
||||||
|
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 2 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let (tag1, size1, p1) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
if tag1 != 6 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let s1 = std::str::from_utf8(&data[p1..p1 + size1])
|
||||||
|
.map_err(|_| ())?
|
||||||
|
.to_string();
|
||||||
|
pos = p1 + size1;
|
||||||
|
let (tag2, size2, p2) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
if tag2 != 6 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let s2 = std::str::from_utf8(&data[p2..p2 + size2])
|
||||||
|
.map_err(|_| ())?
|
||||||
|
.to_string();
|
||||||
|
Ok((s1, s2))
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_bytes(data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||||
|
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
if tag != 6 && tag != 7 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(data[p..p + size].to_vec())
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_i32(data: &[u8]) -> Result<i32, ()> {
|
||||||
|
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
match (tag, size) {
|
||||||
|
(2, 4) => {
|
||||||
|
let mut b = [0u8; 4];
|
||||||
|
b.copy_from_slice(&data[p..p + 4]);
|
||||||
|
Ok(i32::from_le_bytes(b))
|
||||||
|
}
|
||||||
|
(5, 8) => {
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
b.copy_from_slice(&data[p..p + 8]);
|
||||||
|
Ok(i64::from_le_bytes(b) as i32)
|
||||||
|
}
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_handle(data: &[u8]) -> Result<(u32, u32), ()> {
|
||||||
|
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||||
|
if tag != 8 || size != 8 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let mut t = [0u8; 4];
|
||||||
|
let mut i = [0u8; 4];
|
||||||
|
t.copy_from_slice(&data[p..p + 4]);
|
||||||
|
i.copy_from_slice(&data[p + 4..p + 8]);
|
||||||
|
Ok((u32::from_le_bytes(t), u32::from_le_bytes(i)))
|
||||||
|
}
|
||||||
|
pub fn tlv_parse_entry_hdr(data: &[u8], pos: usize) -> Result<(u8, usize, usize), ()> {
|
||||||
|
if pos + 4 > data.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let tag = data[pos];
|
||||||
|
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||||
|
let p = pos + 4;
|
||||||
|
if p + size > data.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok((tag, size, p))
|
||||||
|
}
|
||||||
@ -135,7 +135,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn pycompiler_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn pycompiler_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"birth" => METHOD_BIRTH,
|
"birth" => METHOD_BIRTH,
|
||||||
@ -156,48 +158,77 @@ extern "C" fn pycompiler_invoke_id(
|
|||||||
match method_id {
|
match method_id {
|
||||||
METHOD_BIRTH => unsafe {
|
METHOD_BIRTH => unsafe {
|
||||||
let mut id_g = NEXT_ID.lock().unwrap();
|
let mut id_g = NEXT_ID.lock().unwrap();
|
||||||
let id = *id_g; *id_g += 1;
|
let id = *id_g;
|
||||||
if result_len.is_null() { return NYB_E_SHORT_BUFFER; }
|
*id_g += 1;
|
||||||
|
if result_len.is_null() {
|
||||||
|
return NYB_E_SHORT_BUFFER;
|
||||||
|
}
|
||||||
let need = 4usize;
|
let need = 4usize;
|
||||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
if *result_len < need {
|
||||||
|
*result_len = need;
|
||||||
|
return NYB_E_SHORT_BUFFER;
|
||||||
|
}
|
||||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||||
out[0..4].copy_from_slice(&(id as u32).to_le_bytes());
|
out[0..4].copy_from_slice(&(id as u32).to_le_bytes());
|
||||||
*result_len = need;
|
*result_len = need;
|
||||||
NYB_SUCCESS
|
NYB_SUCCESS
|
||||||
},
|
},
|
||||||
METHOD_COMPILE => unsafe {
|
METHOD_COMPILE => unsafe {
|
||||||
let ir = if args.is_null() || args_len < 8 { None } else {
|
let ir = if args.is_null() || args_len < 8 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
let buf = std::slice::from_raw_parts(args, args_len);
|
let buf = std::slice::from_raw_parts(args, args_len);
|
||||||
let tag = u16::from_le_bytes([buf[4], buf[5]]);
|
let tag = u16::from_le_bytes([buf[4], buf[5]]);
|
||||||
let len = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
let len = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
||||||
if tag == 6 && 8 + len <= buf.len() {
|
if tag == 6 && 8 + len <= buf.len() {
|
||||||
std::str::from_utf8(&buf[8..8+len]).ok().map(|s| s.to_string())
|
std::str::from_utf8(&buf[8..8 + len])
|
||||||
} else { None }
|
.ok()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let nyash_source = if let Some(s) = ir.or_else(|| std::env::var("NYASH_PY_IR").ok()) {
|
let nyash_source = if let Some(s) = ir.or_else(|| std::env::var("NYASH_PY_IR").ok()) {
|
||||||
match serde_json::from_str::<Json>(&s).ok() {
|
match serde_json::from_str::<Json>(&s).ok() {
|
||||||
Some(Json::Object(map)) => {
|
Some(Json::Object(map)) => {
|
||||||
if let Some(Json::String(src)) = map.get("nyash_source") { src.clone() }
|
if let Some(Json::String(src)) = map.get("nyash_source") {
|
||||||
else if let Some(module) = map.get("module") {
|
src.clone()
|
||||||
|
} else if let Some(module) = map.get("module") {
|
||||||
let mut ret_expr = "0".to_string();
|
let mut ret_expr = "0".to_string();
|
||||||
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array()) {
|
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array())
|
||||||
|
{
|
||||||
if let Some(fun0) = funcs.get(0) {
|
if let Some(fun0) = funcs.get(0) {
|
||||||
if let Some(retv) = fun0.get("return_value") {
|
if let Some(retv) = fun0.get("return_value") {
|
||||||
if retv.is_number() { ret_expr = retv.to_string(); }
|
if retv.is_number() {
|
||||||
else if let Some(s) = retv.as_str() { ret_expr = s.to_string(); }
|
ret_expr = retv.to_string();
|
||||||
|
} else if let Some(s) = retv.as_str() {
|
||||||
|
ret_expr = s.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format!("static box Generated {\n main() {\n return {}\n }\n}}", ret_expr)
|
}
|
||||||
} else { "static box Generated { main() { return 0 } }".to_string() }
|
format!(
|
||||||
|
"static box Generated {\n main() {\n return {}\n }\n}}",
|
||||||
|
ret_expr
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"static box Generated { main() { return 0 } }".to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => "static box Generated { main() { return 0 } }".to_string(),
|
_ => "static box Generated { main() { return 0 } }".to_string(),
|
||||||
}
|
}
|
||||||
} else { "static box Generated { main() { return 0 } }".to_string() };
|
} else {
|
||||||
|
"static box Generated { main() { return 0 } }".to_string()
|
||||||
|
};
|
||||||
let bytes = nyash_source.as_bytes();
|
let bytes = nyash_source.as_bytes();
|
||||||
if result_len.is_null() { return NYB_E_SHORT_BUFFER; }
|
if result_len.is_null() {
|
||||||
|
return NYB_E_SHORT_BUFFER;
|
||||||
|
}
|
||||||
let need = 4 + bytes.len();
|
let need = 4 + bytes.len();
|
||||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
if *result_len < need {
|
||||||
|
*result_len = need;
|
||||||
|
return NYB_E_SHORT_BUFFER;
|
||||||
|
}
|
||||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||||
out[0..2].copy_from_slice(&6u16.to_le_bytes());
|
out[0..2].copy_from_slice(&6u16.to_le_bytes());
|
||||||
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
|
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
|
||||||
|
|||||||
@ -122,7 +122,9 @@ const METHOD_FINI: u32 = u32::MAX;
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn pyparser_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn pyparser_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"birth" => METHOD_BIRTH,
|
"birth" => METHOD_BIRTH,
|
||||||
@ -143,7 +145,9 @@ extern "C" fn pyparser_invoke_id(
|
|||||||
match method_id {
|
match method_id {
|
||||||
METHOD_BIRTH => unsafe {
|
METHOD_BIRTH => unsafe {
|
||||||
let instance_id = 1u32; // simple singleton
|
let instance_id = 1u32; // simple singleton
|
||||||
if result_len.is_null() { return -1; }
|
if result_len.is_null() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if *result_len < 4 {
|
if *result_len < 4 {
|
||||||
*result_len = 4;
|
*result_len = 4;
|
||||||
return -1;
|
return -1;
|
||||||
@ -157,26 +161,35 @@ extern "C" fn pyparser_invoke_id(
|
|||||||
// Decode TLV string from args if present, else env fallback
|
// Decode TLV string from args if present, else env fallback
|
||||||
let code = unsafe {
|
let code = unsafe {
|
||||||
if args.is_null() || args_len < 4 {
|
if args.is_null() || args_len < 4 {
|
||||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
std::env::var("NYASH_PY_CODE")
|
||||||
|
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||||
} else {
|
} else {
|
||||||
let buf = std::slice::from_raw_parts(args, args_len);
|
let buf = std::slice::from_raw_parts(args, args_len);
|
||||||
if args_len >= 8 {
|
if args_len >= 8 {
|
||||||
let tag = u16::from_le_bytes([buf[0], buf[1]]);
|
let tag = u16::from_le_bytes([buf[0], buf[1]]);
|
||||||
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
|
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
|
||||||
if tag == 6 && 4 + len <= args_len {
|
if tag == 6 && 4 + len <= args_len {
|
||||||
match std::str::from_utf8(&buf[4..4 + len]) { Ok(s) => s.to_string(), Err(_) => std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string()) }
|
match std::str::from_utf8(&buf[4..4 + len]) {
|
||||||
} else {
|
Ok(s) => s.to_string(),
|
||||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
Err(_) => std::env::var("NYASH_PY_CODE")
|
||||||
|
.unwrap_or_else(|_| "def main():\n return 0".to_string()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
std::env::var("NYASH_PY_CODE")
|
||||||
|
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::env::var("NYASH_PY_CODE")
|
||||||
|
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let parse_result = Python::with_gil(|py| parse_python_code(py, &code));
|
let parse_result = Python::with_gil(|py| parse_python_code(py, &code));
|
||||||
match serde_json::to_string(&parse_result) {
|
match serde_json::to_string(&parse_result) {
|
||||||
Ok(json) => unsafe {
|
Ok(json) => unsafe {
|
||||||
if result_len.is_null() { return -1; }
|
if result_len.is_null() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
let bytes = json.as_bytes();
|
let bytes = json.as_bytes();
|
||||||
let need = 4 + bytes.len();
|
let need = 4 + bytes.len();
|
||||||
if *result_len < need {
|
if *result_len < need {
|
||||||
|
|||||||
284
plugins/nyash-python-plugin/src/ffi.rs
Normal file
284
plugins/nyash-python-plugin/src/ffi.rs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
#![allow(non_snake_case, non_camel_case_types, dead_code)]
|
||||||
|
use libloading::Library;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::os::raw::{c_char, c_int, c_long, c_void};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub type PyObject = c_void;
|
||||||
|
pub type PyGILState_STATE = c_int;
|
||||||
|
|
||||||
|
pub struct CPython {
|
||||||
|
pub(crate) _lib: Library,
|
||||||
|
pub(crate) Py_Initialize: unsafe extern "C" fn(),
|
||||||
|
pub(crate) Py_Finalize: unsafe extern "C" fn(),
|
||||||
|
pub(crate) Py_IsInitialized: unsafe extern "C" fn() -> c_int,
|
||||||
|
pub(crate) PyGILState_Ensure: unsafe extern "C" fn() -> PyGILState_STATE,
|
||||||
|
pub(crate) PyGILState_Release: unsafe extern "C" fn(PyGILState_STATE),
|
||||||
|
pub(crate) PyRun_StringFlags: unsafe extern "C" fn(
|
||||||
|
*const c_char,
|
||||||
|
c_int,
|
||||||
|
*mut PyObject,
|
||||||
|
*mut PyObject,
|
||||||
|
*mut c_void,
|
||||||
|
) -> *mut PyObject,
|
||||||
|
pub(crate) PyImport_AddModule: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||||
|
pub(crate) PyModule_GetDict: unsafe extern "C" fn(*mut PyObject) -> *mut PyObject,
|
||||||
|
pub(crate) PyImport_ImportModule: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||||
|
pub(crate) PyObject_Str: unsafe extern "C" fn(*mut PyObject) -> *mut PyObject,
|
||||||
|
pub(crate) PyUnicode_AsUTF8: unsafe extern "C" fn(*mut PyObject) -> *const c_char,
|
||||||
|
pub(crate) Py_DecRef: unsafe extern "C" fn(*mut PyObject),
|
||||||
|
pub(crate) Py_IncRef: unsafe extern "C" fn(*mut PyObject),
|
||||||
|
pub(crate) PyObject_GetAttrString:
|
||||||
|
unsafe extern "C" fn(*mut PyObject, *const c_char) -> *mut PyObject,
|
||||||
|
pub(crate) PyObject_CallObject:
|
||||||
|
unsafe extern "C" fn(*mut PyObject, *mut PyObject) -> *mut PyObject,
|
||||||
|
pub(crate) PyObject_Call:
|
||||||
|
unsafe extern "C" fn(*mut PyObject, *mut PyObject, *mut PyObject) -> *mut PyObject,
|
||||||
|
pub(crate) PyTuple_New: unsafe extern "C" fn(isize) -> *mut PyObject,
|
||||||
|
pub(crate) PyTuple_SetItem: unsafe extern "C" fn(*mut PyObject, isize, *mut PyObject) -> c_int,
|
||||||
|
pub(crate) PyLong_FromLongLong: unsafe extern "C" fn(i64) -> *mut PyObject,
|
||||||
|
pub(crate) PyUnicode_FromString: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||||
|
pub(crate) PyBool_FromLong: unsafe extern "C" fn(c_long: c_long) -> *mut PyObject,
|
||||||
|
pub(crate) PyFloat_FromDouble: unsafe extern "C" fn(f64) -> *mut PyObject,
|
||||||
|
pub(crate) PyFloat_AsDouble: unsafe extern "C" fn(*mut PyObject) -> f64,
|
||||||
|
pub(crate) PyLong_AsLongLong: unsafe extern "C" fn(*mut PyObject) -> i64,
|
||||||
|
pub(crate) PyBytes_FromStringAndSize:
|
||||||
|
unsafe extern "C" fn(*const c_char, isize) -> *mut PyObject,
|
||||||
|
pub(crate) PyBytes_AsStringAndSize:
|
||||||
|
unsafe extern "C" fn(*mut PyObject, *mut *mut c_char, *mut isize) -> c_int,
|
||||||
|
pub(crate) PyDict_New: unsafe extern "C" fn() -> *mut PyObject,
|
||||||
|
pub(crate) PyDict_SetItemString:
|
||||||
|
unsafe extern "C" fn(*mut PyObject, *const c_char, *mut PyObject) -> c_int,
|
||||||
|
pub(crate) PyErr_Occurred: unsafe extern "C" fn() -> *mut PyObject,
|
||||||
|
pub(crate) PyErr_Fetch:
|
||||||
|
unsafe extern "C" fn(*mut *mut PyObject, *mut *mut PyObject, *mut *mut PyObject),
|
||||||
|
pub(crate) PyErr_Clear: unsafe extern "C" fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static CPY: Lazy<Mutex<Option<CPython>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
|
pub fn try_load_cpython() -> Result<(), ()> {
|
||||||
|
let mut candidates: Vec<String> = vec![
|
||||||
|
// Linux
|
||||||
|
"libpython3.12.so".into(),
|
||||||
|
"libpython3.12.so.1.0".into(),
|
||||||
|
"libpython3.11.so".into(),
|
||||||
|
"libpython3.11.so.1.0".into(),
|
||||||
|
"libpython3.10.so".into(),
|
||||||
|
"libpython3.10.so.1.0".into(),
|
||||||
|
"libpython3.9.so".into(),
|
||||||
|
"libpython3.9.so.1.0".into(),
|
||||||
|
// macOS
|
||||||
|
"libpython3.12.dylib".into(),
|
||||||
|
"libpython3.11.dylib".into(),
|
||||||
|
"libpython3.10.dylib".into(),
|
||||||
|
"libpython3.9.dylib".into(),
|
||||||
|
];
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
let dlls = [
|
||||||
|
"python312.dll",
|
||||||
|
"python311.dll",
|
||||||
|
"python310.dll",
|
||||||
|
"python39.dll",
|
||||||
|
];
|
||||||
|
for d in dlls {
|
||||||
|
candidates.push(d.into());
|
||||||
|
}
|
||||||
|
if let Ok(pyhome) = std::env::var("PYTHONHOME") {
|
||||||
|
for d in [
|
||||||
|
"python312.dll",
|
||||||
|
"python311.dll",
|
||||||
|
"python310.dll",
|
||||||
|
"python39.dll",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
let p = std::path::Path::new(&pyhome).join(d);
|
||||||
|
if p.exists() {
|
||||||
|
candidates.push(p.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name in candidates.into_iter() {
|
||||||
|
if let Ok(lib) = unsafe { Library::new(&name) } {
|
||||||
|
unsafe {
|
||||||
|
let Py_Initialize = *lib
|
||||||
|
.get::<unsafe extern "C" fn()>(b"Py_Initialize\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let Py_Finalize = *lib
|
||||||
|
.get::<unsafe extern "C" fn()>(b"Py_Finalize\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let Py_IsInitialized = *lib
|
||||||
|
.get::<unsafe extern "C" fn() -> c_int>(b"Py_IsInitialized\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyGILState_Ensure = *lib
|
||||||
|
.get::<unsafe extern "C" fn() -> PyGILState_STATE>(b"PyGILState_Ensure\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyGILState_Release = *lib
|
||||||
|
.get::<unsafe extern "C" fn(PyGILState_STATE)>(b"PyGILState_Release\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyRun_StringFlags = *lib
|
||||||
|
.get::<unsafe extern "C" fn(
|
||||||
|
*const c_char,
|
||||||
|
c_int,
|
||||||
|
*mut PyObject,
|
||||||
|
*mut PyObject,
|
||||||
|
*mut c_void,
|
||||||
|
) -> *mut PyObject>(b"PyRun_StringFlags\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyImport_AddModule = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||||
|
b"PyImport_AddModule\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyModule_GetDict = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject) -> *mut PyObject>(
|
||||||
|
b"PyModule_GetDict\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyImport_ImportModule = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||||
|
b"PyImport_ImportModule\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyObject_Str = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject) -> *mut PyObject>(b"PyObject_Str\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyUnicode_AsUTF8 = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject) -> *const c_char>(
|
||||||
|
b"PyUnicode_AsUTF8\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let Py_DecRef = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject)>(b"Py_DecRef\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let Py_IncRef = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject)>(b"Py_IncRef\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyObject_GetAttrString = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject, *const c_char) -> *mut PyObject>(
|
||||||
|
b"PyObject_GetAttrString\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyObject_CallObject = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject, *mut PyObject) -> *mut PyObject>(
|
||||||
|
b"PyObject_CallObject\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyObject_Call = *lib
|
||||||
|
.get::<unsafe extern "C" fn(
|
||||||
|
*mut PyObject,
|
||||||
|
*mut PyObject,
|
||||||
|
*mut PyObject,
|
||||||
|
) -> *mut PyObject>(b"PyObject_Call\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyTuple_New = *lib
|
||||||
|
.get::<unsafe extern "C" fn(isize) -> *mut PyObject>(b"PyTuple_New\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyTuple_SetItem = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject, isize, *mut PyObject) -> c_int>(
|
||||||
|
b"PyTuple_SetItem\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyLong_FromLongLong = *lib
|
||||||
|
.get::<unsafe extern "C" fn(i64) -> *mut PyObject>(b"PyLong_FromLongLong\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyUnicode_FromString = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||||
|
b"PyUnicode_FromString\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyBool_FromLong = *lib
|
||||||
|
.get::<unsafe extern "C" fn(c_long: c_long) -> *mut PyObject>(
|
||||||
|
b"PyBool_FromLong\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyFloat_FromDouble = *lib
|
||||||
|
.get::<unsafe extern "C" fn(f64) -> *mut PyObject>(b"PyFloat_FromDouble\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyFloat_AsDouble = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject) -> f64>(b"PyFloat_AsDouble\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyLong_AsLongLong = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*mut PyObject) -> i64>(b"PyLong_AsLongLong\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyBytes_FromStringAndSize = *lib
|
||||||
|
.get::<unsafe extern "C" fn(*const c_char, isize) -> *mut PyObject>(
|
||||||
|
b"PyBytes_FromStringAndSize\0",
|
||||||
|
)
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyBytes_AsStringAndSize = *lib.get::<unsafe extern "C" fn(*mut PyObject, *mut *mut c_char, *mut isize) -> c_int>(b"PyBytes_AsStringAndSize\0").map_err(|_| ())?;
|
||||||
|
let PyDict_New = *lib
|
||||||
|
.get::<unsafe extern "C" fn() -> *mut PyObject>(b"PyDict_New\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyDict_SetItemString = *lib.get::<unsafe extern "C" fn(*mut PyObject, *const c_char, *mut PyObject) -> c_int>(b"PyDict_SetItemString\0").map_err(|_| ())?;
|
||||||
|
let PyErr_Occurred = *lib
|
||||||
|
.get::<unsafe extern "C" fn() -> *mut PyObject>(b"PyErr_Occurred\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyErr_Fetch =
|
||||||
|
*lib.get::<unsafe extern "C" fn(
|
||||||
|
*mut *mut PyObject,
|
||||||
|
*mut *mut PyObject,
|
||||||
|
*mut *mut PyObject,
|
||||||
|
)>(b"PyErr_Fetch\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
let PyErr_Clear = *lib
|
||||||
|
.get::<unsafe extern "C" fn()>(b"PyErr_Clear\0")
|
||||||
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
|
let cpy = CPython {
|
||||||
|
_lib: lib,
|
||||||
|
Py_Initialize,
|
||||||
|
Py_Finalize,
|
||||||
|
Py_IsInitialized,
|
||||||
|
PyGILState_Ensure,
|
||||||
|
PyGILState_Release,
|
||||||
|
PyRun_StringFlags,
|
||||||
|
PyImport_AddModule,
|
||||||
|
PyModule_GetDict,
|
||||||
|
PyImport_ImportModule,
|
||||||
|
PyObject_Str,
|
||||||
|
PyUnicode_AsUTF8,
|
||||||
|
Py_DecRef,
|
||||||
|
Py_IncRef,
|
||||||
|
PyObject_GetAttrString,
|
||||||
|
PyObject_CallObject,
|
||||||
|
PyObject_Call,
|
||||||
|
PyTuple_New,
|
||||||
|
PyTuple_SetItem,
|
||||||
|
PyLong_FromLongLong,
|
||||||
|
PyUnicode_FromString,
|
||||||
|
PyBool_FromLong,
|
||||||
|
PyFloat_FromDouble,
|
||||||
|
PyFloat_AsDouble,
|
||||||
|
PyLong_AsLongLong,
|
||||||
|
PyBytes_FromStringAndSize,
|
||||||
|
PyBytes_AsStringAndSize,
|
||||||
|
PyDict_New,
|
||||||
|
PyDict_SetItemString,
|
||||||
|
PyErr_Occurred,
|
||||||
|
PyErr_Fetch,
|
||||||
|
PyErr_Clear,
|
||||||
|
};
|
||||||
|
*CPY.lock().unwrap() = Some(cpy);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_cpython() -> Result<(), ()> {
|
||||||
|
if CPY.lock().unwrap().is_none() {
|
||||||
|
try_load_cpython()?;
|
||||||
|
unsafe {
|
||||||
|
if let Some(cpy) = &*CPY.lock().unwrap() {
|
||||||
|
if (cpy.Py_IsInitialized)() == 0 {
|
||||||
|
(cpy.Py_Initialize)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
19
plugins/nyash-python-plugin/src/gil.rs
Normal file
19
plugins/nyash-python-plugin/src/gil.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use crate::ffi::{CPython, PyGILState_STATE};
|
||||||
|
|
||||||
|
pub struct GILGuard<'a> {
|
||||||
|
cpy: &'a CPython,
|
||||||
|
state: PyGILState_STATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GILGuard<'a> {
|
||||||
|
pub fn acquire(cpy: &'a CPython) -> Self {
|
||||||
|
let state = unsafe { (cpy.PyGILState_Ensure)() };
|
||||||
|
GILGuard { cpy, state }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for GILGuard<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { (self.cpy.PyGILState_Release)(self.state) };
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
461
plugins/nyash-python-plugin/src/pytypes.rs
Normal file
461
plugins/nyash-python-plugin/src/pytypes.rs
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
use crate::ffi::{self, CPython, PyObject};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
pub enum DecodedValue {
|
||||||
|
Float(f64),
|
||||||
|
Int(i64),
|
||||||
|
Str(String),
|
||||||
|
Bytes(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PyOwned {
|
||||||
|
ptr: NonNull<PyObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct PyBorrowed<'a> {
|
||||||
|
ptr: NonNull<PyObject>,
|
||||||
|
_marker: PhantomData<&'a PyObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PyOwned {
|
||||||
|
pub unsafe fn from_new(ptr: *mut PyObject) -> Option<Self> {
|
||||||
|
NonNull::new(ptr).map(|ptr| PyOwned { ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_raw(ptr: *mut PyObject) -> Option<Self> {
|
||||||
|
NonNull::new(ptr).map(|ptr| PyOwned { ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn from_borrowed(cpy: &CPython, borrowed: PyBorrowed<'_>) -> Self {
|
||||||
|
(cpy.Py_IncRef)(borrowed.ptr.as_ptr());
|
||||||
|
PyOwned { ptr: borrowed.ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *mut PyObject {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn borrow(&self) -> PyBorrowed<'_> {
|
||||||
|
PyBorrowed {
|
||||||
|
ptr: self.ptr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn clone_ref(&self, cpy: &CPython) -> Self {
|
||||||
|
unsafe {
|
||||||
|
(cpy.Py_IncRef)(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
PyOwned { ptr: self.ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_raw(self) -> *mut PyObject {
|
||||||
|
let ptr = self.ptr.as_ptr();
|
||||||
|
std::mem::forget(self);
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PyOwned {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(guard) = ffi::CPY.lock() {
|
||||||
|
if let Some(cpy) = guard.as_ref() {
|
||||||
|
unsafe {
|
||||||
|
(cpy.Py_DecRef)(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for PyOwned {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let guard = ffi::CPY.lock().expect("CPython state poisoned");
|
||||||
|
let cpy = guard.as_ref().expect("CPython not initialized");
|
||||||
|
unsafe {
|
||||||
|
(cpy.Py_IncRef)(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
PyOwned { ptr: self.ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for PyOwned {}
|
||||||
|
unsafe impl Sync for PyOwned {}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl<'a> PyBorrowed<'a> {
|
||||||
|
pub unsafe fn new(ptr: *mut PyObject) -> Option<Self> {
|
||||||
|
NonNull::new(ptr).map(|ptr| PyBorrowed {
|
||||||
|
ptr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *mut PyObject {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Copy for PyBorrowed<'a> {}
|
||||||
|
|
||||||
|
impl<'a> Clone for PyBorrowed<'a> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn autodecode(cpy: &CPython, obj: *mut PyObject) -> Option<DecodedValue> {
|
||||||
|
unsafe {
|
||||||
|
let f = (cpy.PyFloat_AsDouble)(obj);
|
||||||
|
if (cpy.PyErr_Occurred)().is_null() {
|
||||||
|
return Some(DecodedValue::Float(f));
|
||||||
|
}
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
|
||||||
|
let i = (cpy.PyLong_AsLongLong)(obj);
|
||||||
|
if (cpy.PyErr_Occurred)().is_null() {
|
||||||
|
return Some(DecodedValue::Int(i));
|
||||||
|
}
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
|
||||||
|
let u = (cpy.PyUnicode_AsUTF8)(obj);
|
||||||
|
if (cpy.PyErr_Occurred)().is_null() && !u.is_null() {
|
||||||
|
let s = CStr::from_ptr(u).to_string_lossy().to_string();
|
||||||
|
return Some(DecodedValue::Str(s));
|
||||||
|
}
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
|
||||||
|
let mut ptr: *mut c_char = std::ptr::null_mut();
|
||||||
|
let mut sz: isize = 0;
|
||||||
|
if (cpy.PyBytes_AsStringAndSize)(obj, &mut ptr, &mut sz) == 0 {
|
||||||
|
let slice = std::slice::from_raw_parts(ptr as *const u8, sz as usize);
|
||||||
|
return Some(DecodedValue::Bytes(slice.to_vec()));
|
||||||
|
}
|
||||||
|
if !(cpy.PyErr_Occurred)().is_null() {
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cstring_from_str(s: &str) -> Result<CString, ()> {
|
||||||
|
CString::new(s).map_err(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn cstr_to_string(ptr: *const c_char) -> Option<String> {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(CStr::from_ptr(ptr).to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn incref(cpy: &CPython, obj: *mut PyObject) {
|
||||||
|
(cpy.Py_IncRef)(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn decref(cpy: &CPython, obj: *mut PyObject) {
|
||||||
|
(cpy.Py_DecRef)(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_py_error_string(cpy: &CPython) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
if (cpy.PyErr_Occurred)().is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut ptype: *mut PyObject = std::ptr::null_mut();
|
||||||
|
let mut pvalue: *mut PyObject = std::ptr::null_mut();
|
||||||
|
let mut ptrace: *mut PyObject = std::ptr::null_mut();
|
||||||
|
(cpy.PyErr_Fetch)(&mut ptype, &mut pvalue, &mut ptrace);
|
||||||
|
let s = if !pvalue.is_null() {
|
||||||
|
let sobj = (cpy.PyObject_Str)(pvalue);
|
||||||
|
if sobj.is_null() {
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
return Some("Python error".to_string());
|
||||||
|
}
|
||||||
|
let cstr = (cpy.PyUnicode_AsUTF8)(sobj);
|
||||||
|
let msg = if cstr.is_null() {
|
||||||
|
"Python error".to_string()
|
||||||
|
} else {
|
||||||
|
CStr::from_ptr(cstr).to_string_lossy().to_string()
|
||||||
|
};
|
||||||
|
(cpy.Py_DecRef)(sobj);
|
||||||
|
msg
|
||||||
|
} else {
|
||||||
|
"Python error".to_string()
|
||||||
|
};
|
||||||
|
(cpy.PyErr_Clear)();
|
||||||
|
Some(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count_tlv_args(args: *const u8, args_len: usize) -> usize {
|
||||||
|
if args.is_null() || args_len < 4 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||||
|
if buf.len() < 4 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u16::from_le_bytes([buf[2], buf[3]]) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tuple_from_tlv(
|
||||||
|
cpy: &CPython,
|
||||||
|
args: *const u8,
|
||||||
|
args_len: usize,
|
||||||
|
) -> Result<*mut PyObject, ()> {
|
||||||
|
let argc = count_tlv_args(args, args_len) as isize;
|
||||||
|
let tuple = unsafe { (cpy.PyTuple_New)(argc) };
|
||||||
|
if tuple.is_null() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if argc == 0 {
|
||||||
|
return Ok(tuple);
|
||||||
|
}
|
||||||
|
if !fill_tuple_from_tlv(cpy, tuple, args, args_len) {
|
||||||
|
unsafe {
|
||||||
|
(cpy.Py_DecRef)(tuple);
|
||||||
|
}
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(tuple)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kwargs_from_tlv(
|
||||||
|
cpy: &CPython,
|
||||||
|
args: *const u8,
|
||||||
|
args_len: usize,
|
||||||
|
) -> Result<*mut PyObject, ()> {
|
||||||
|
let dict = unsafe { (cpy.PyDict_New)() };
|
||||||
|
if dict.is_null() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if !fill_kwargs_from_tlv(cpy, dict, args, args_len) {
|
||||||
|
unsafe {
|
||||||
|
(cpy.Py_DecRef)(dict);
|
||||||
|
}
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(dict)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_tuple_from_tlv(
|
||||||
|
cpy: &CPython,
|
||||||
|
tuple: *mut PyObject,
|
||||||
|
args: *const u8,
|
||||||
|
args_len: usize,
|
||||||
|
) -> bool {
|
||||||
|
if args.is_null() || args_len < 4 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||||
|
let mut off = 4usize;
|
||||||
|
let mut idx: isize = 0;
|
||||||
|
while off + 4 <= buf.len() {
|
||||||
|
let tag = buf[off];
|
||||||
|
let _rsv = buf[off + 1];
|
||||||
|
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||||
|
if off + 4 + size > buf.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let payload = &buf[off + 4..off + 4 + size];
|
||||||
|
let mut obj: *mut PyObject = std::ptr::null_mut();
|
||||||
|
unsafe {
|
||||||
|
let mut owned_transfer: Option<PyOwned> = None;
|
||||||
|
match tag {
|
||||||
|
1 => {
|
||||||
|
let v = if size >= 1 && payload[0] != 0 { 1 } else { 0 };
|
||||||
|
obj = (cpy.PyBool_FromLong)(v);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if size != 4 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 4];
|
||||||
|
b.copy_from_slice(payload);
|
||||||
|
obj = (cpy.PyLong_FromLongLong)(i32::from_le_bytes(b) as i64);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
if size != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
b.copy_from_slice(payload);
|
||||||
|
obj = (cpy.PyLong_FromLongLong)(i64::from_le_bytes(b));
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
if size != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
b.copy_from_slice(payload);
|
||||||
|
obj = (cpy.PyFloat_FromDouble)(f64::from_le_bytes(b));
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let c = match CString::new(payload) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
obj = (cpy.PyUnicode_FromString)(c.as_ptr());
|
||||||
|
}
|
||||||
|
7 => {
|
||||||
|
let ptr = if size > 0 {
|
||||||
|
payload.as_ptr() as *const c_char
|
||||||
|
} else {
|
||||||
|
std::ptr::null()
|
||||||
|
};
|
||||||
|
obj = (cpy.PyBytes_FromStringAndSize)(ptr, size as isize);
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
if size != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut i = [0u8; 4];
|
||||||
|
i.copy_from_slice(&payload[4..8]);
|
||||||
|
let inst_id = u32::from_le_bytes(i);
|
||||||
|
let Some(handle) = super::PY_HANDLES.lock().unwrap().get(&inst_id).cloned()
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
owned_transfer = Some(handle);
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
let raw = if let Some(handle) = owned_transfer.take() {
|
||||||
|
handle.into_raw()
|
||||||
|
} else {
|
||||||
|
obj
|
||||||
|
};
|
||||||
|
if (cpy.PyTuple_SetItem)(tuple, idx, raw) != 0 {
|
||||||
|
if let Some(rewrap) = PyOwned::from_raw(raw) {
|
||||||
|
drop(rewrap);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
off += 4 + size;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_kwargs_from_tlv(
|
||||||
|
cpy: &CPython,
|
||||||
|
dict: *mut PyObject,
|
||||||
|
args: *const u8,
|
||||||
|
args_len: usize,
|
||||||
|
) -> bool {
|
||||||
|
if args.is_null() || args_len < 4 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||||
|
let mut off = 4usize;
|
||||||
|
while off + 4 <= buf.len() {
|
||||||
|
// key (string)
|
||||||
|
if buf[off] != 6 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let key_size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||||
|
if off + 4 + key_size > buf.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let key_slice = &buf[off + 4..off + 4 + key_size];
|
||||||
|
let key_c = match CString::new(key_slice) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
off += 4 + key_size;
|
||||||
|
if off + 4 > buf.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let tag_v = buf[off];
|
||||||
|
let _rsv = buf[off + 1];
|
||||||
|
let size_v = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||||
|
if off + 4 + size_v > buf.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let val_payload = &buf[off + 4..off + 4 + size_v];
|
||||||
|
let obj: *mut PyObject;
|
||||||
|
unsafe {
|
||||||
|
match tag_v {
|
||||||
|
1 => {
|
||||||
|
let v = if size_v >= 1 && val_payload[0] != 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
obj = (cpy.PyBool_FromLong)(v);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if size_v != 4 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 4];
|
||||||
|
b.copy_from_slice(val_payload);
|
||||||
|
obj = (cpy.PyLong_FromLongLong)(i32::from_le_bytes(b) as i64);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
if size_v != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
b.copy_from_slice(val_payload);
|
||||||
|
obj = (cpy.PyLong_FromLongLong)(i64::from_le_bytes(b));
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
if size_v != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut b = [0u8; 8];
|
||||||
|
b.copy_from_slice(val_payload);
|
||||||
|
obj = (cpy.PyFloat_FromDouble)(f64::from_le_bytes(b));
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let c = match CString::new(val_payload) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
obj = (cpy.PyUnicode_FromString)(c.as_ptr());
|
||||||
|
}
|
||||||
|
7 => {
|
||||||
|
let ptr = if size_v > 0 {
|
||||||
|
val_payload.as_ptr() as *const c_char
|
||||||
|
} else {
|
||||||
|
std::ptr::null()
|
||||||
|
};
|
||||||
|
obj = (cpy.PyBytes_FromStringAndSize)(ptr, size_v as isize);
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
if size_v != 8 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut i = [0u8; 4];
|
||||||
|
i.copy_from_slice(&val_payload[4..8]);
|
||||||
|
let inst_id = u32::from_le_bytes(i);
|
||||||
|
let Some(handle) = super::PY_HANDLES.lock().unwrap().get(&inst_id).cloned()
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
obj = handle.into_raw();
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
let rc = (cpy.PyDict_SetItemString)(dict, key_c.as_ptr(), obj);
|
||||||
|
(cpy.Py_DecRef)(obj);
|
||||||
|
if rc != 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off += 4 + size_v;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
@ -220,7 +220,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn regex_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn regex_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"compile" => M_COMPILE,
|
"compile" => M_COMPILE,
|
||||||
@ -246,66 +248,148 @@ extern "C" fn regex_invoke_id(
|
|||||||
match method_id {
|
match method_id {
|
||||||
M_BIRTH => {
|
M_BIRTH => {
|
||||||
// mirror v1: birth may take optional pattern
|
// mirror v1: birth may take optional pattern
|
||||||
if result_len.is_null() { return E_ARGS; }
|
if result_len.is_null() {
|
||||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
return E_ARGS;
|
||||||
|
}
|
||||||
|
if preflight(result, result_len, 4) {
|
||||||
|
return E_SHORT;
|
||||||
|
}
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
let inst = if let Some(pat) = read_arg_string(args, args_len, 0) {
|
let inst = if let Some(pat) = read_arg_string(args, args_len, 0) {
|
||||||
match Regex::new(&pat) { Ok(re) => RegexInstance { re: Some(re) }, Err(_) => RegexInstance { re: None } }
|
match Regex::new(&pat) {
|
||||||
} else { RegexInstance { re: None } };
|
Ok(re) => RegexInstance { re: Some(re) },
|
||||||
if let Ok(mut m) = INST.lock() { m.insert(id, inst); } else { return E_PLUGIN; }
|
Err(_) => RegexInstance { re: None },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RegexInstance { re: None }
|
||||||
|
};
|
||||||
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.insert(id, inst);
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
let b = id.to_le_bytes();
|
let b = id.to_le_bytes();
|
||||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||||
*result_len = 4; OK
|
*result_len = 4;
|
||||||
|
OK
|
||||||
}
|
}
|
||||||
M_FINI => {
|
M_FINI => {
|
||||||
if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN }
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.remove(&instance_id);
|
||||||
|
OK
|
||||||
|
} else {
|
||||||
|
E_PLUGIN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_COMPILE => {
|
M_COMPILE => {
|
||||||
let pat = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let pat = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
if let Ok(mut m) = INST.lock() {
|
if let Ok(mut m) = INST.lock() {
|
||||||
if let Some(inst) = m.get_mut(&instance_id) { inst.re = Regex::new(&pat).ok(); OK } else { E_HANDLE }
|
if let Some(inst) = m.get_mut(&instance_id) {
|
||||||
} else { E_PLUGIN }
|
inst.re = Regex::new(&pat).ok();
|
||||||
|
OK
|
||||||
|
} else {
|
||||||
|
E_HANDLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
E_PLUGIN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_IS_MATCH => {
|
M_IS_MATCH => {
|
||||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let text = match read_arg_string(args, args_len, 0) {
|
||||||
if let Ok(m) = INST.lock() {
|
Some(s) => s,
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
None => return E_ARGS,
|
||||||
if let Some(re) = &inst.re { return write_tlv_bool(re.is_match(&text), result, result_len); } else { return write_tlv_bool(false, result, result_len); }
|
};
|
||||||
} else { return E_HANDLE; }
|
|
||||||
} else { return E_PLUGIN; }
|
|
||||||
}
|
|
||||||
M_FIND => {
|
|
||||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
if let Some(re) = &inst.re {
|
if let Some(re) = &inst.re {
|
||||||
let s = re.find(&text).map(|m| m.as_str().to_string()).unwrap_or_else(|| "".to_string());
|
return write_tlv_bool(re.is_match(&text), result, result_len);
|
||||||
return write_tlv_string(&s, result, result_len);
|
} else {
|
||||||
} else { return write_tlv_string("", result, result_len); }
|
return write_tlv_bool(false, result, result_len);
|
||||||
} else { return E_HANDLE; }
|
|
||||||
} else { return E_PLUGIN; }
|
|
||||||
}
|
}
|
||||||
M_REPLACE_ALL => {
|
} else {
|
||||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
return E_HANDLE;
|
||||||
let repl = match read_arg_string(args, args_len, 1) { Some(s) => s, None => return E_ARGS };
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
M_FIND => {
|
||||||
|
let text = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
if let Some(re) = &inst.re { let out = re.replace_all(&text, repl.as_str()).to_string(); return write_tlv_string(&out, result, result_len); } else { return write_tlv_string(&text, result, result_len); }
|
if let Some(re) = &inst.re {
|
||||||
} else { return E_HANDLE; }
|
let s = re
|
||||||
} else { return E_PLUGIN; }
|
.find(&text)
|
||||||
|
.map(|m| m.as_str().to_string())
|
||||||
|
.unwrap_or_else(|| "".to_string());
|
||||||
|
return write_tlv_string(&s, result, result_len);
|
||||||
|
} else {
|
||||||
|
return write_tlv_string("", result, result_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
M_REPLACE_ALL => {
|
||||||
|
let text = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
let repl = match read_arg_string(args, args_len, 1) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
|
if let Ok(m) = INST.lock() {
|
||||||
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
|
if let Some(re) = &inst.re {
|
||||||
|
let out = re.replace_all(&text, repl.as_str()).to_string();
|
||||||
|
return write_tlv_string(&out, result, result_len);
|
||||||
|
} else {
|
||||||
|
return write_tlv_string(&text, result, result_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_SPLIT => {
|
M_SPLIT => {
|
||||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let text = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
let limit = read_arg_i64(args, args_len, 1).unwrap_or(0);
|
let limit = read_arg_i64(args, args_len, 1).unwrap_or(0);
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
if let Some(re) = &inst.re {
|
if let Some(re) = &inst.re {
|
||||||
let parts: Vec<String> = if limit > 0 { re.splitn(&text, limit as usize).map(|s| s.to_string()).collect() } else { re.split(&text).map(|s| s.to_string()).collect() };
|
let parts: Vec<String> = if limit > 0 {
|
||||||
|
re.splitn(&text, limit as usize)
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
re.split(&text).map(|s| s.to_string()).collect()
|
||||||
|
};
|
||||||
let out = parts.join("\n");
|
let out = parts.join("\n");
|
||||||
return write_tlv_string(&out, result, result_len);
|
return write_tlv_string(&out, result, result_len);
|
||||||
} else { return write_tlv_string(&text, result, result_len); }
|
} else {
|
||||||
} else { return E_HANDLE; }
|
return write_tlv_string(&text, result, result_len);
|
||||||
} else { return E_PLUGIN; }
|
}
|
||||||
|
} else {
|
||||||
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => E_METHOD,
|
_ => E_METHOD,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,7 +163,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
extern "C" fn toml_resolve(name: *const std::os::raw::c_char) -> u32 {
|
extern "C" fn toml_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||||
if name.is_null() { return 0; }
|
if name.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||||
match s.as_ref() {
|
match s.as_ref() {
|
||||||
"parse" => M_PARSE,
|
"parse" => M_PARSE,
|
||||||
@ -186,47 +188,93 @@ extern "C" fn toml_invoke_id(
|
|||||||
unsafe {
|
unsafe {
|
||||||
match method_id {
|
match method_id {
|
||||||
M_BIRTH => {
|
M_BIRTH => {
|
||||||
if result_len.is_null() { return E_ARGS; }
|
if result_len.is_null() {
|
||||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
return E_ARGS;
|
||||||
|
}
|
||||||
|
if preflight(result, result_len, 4) {
|
||||||
|
return E_SHORT;
|
||||||
|
}
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
if let Ok(mut m) = INST.lock() { m.insert(id, TomlInstance { value: None }); } else { return E_PLUGIN; }
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.insert(id, TomlInstance { value: None });
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
let b = id.to_le_bytes();
|
let b = id.to_le_bytes();
|
||||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||||
*result_len = 4; OK
|
*result_len = 4;
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
M_FINI => {
|
||||||
|
if let Ok(mut m) = INST.lock() {
|
||||||
|
m.remove(&instance_id);
|
||||||
|
OK
|
||||||
|
} else {
|
||||||
|
E_PLUGIN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
|
||||||
M_PARSE => {
|
M_PARSE => {
|
||||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let text = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
if let Ok(mut m) = INST.lock() {
|
if let Ok(mut m) = INST.lock() {
|
||||||
if let Some(inst) = m.get_mut(&instance_id) {
|
if let Some(inst) = m.get_mut(&instance_id) {
|
||||||
inst.value = toml::from_str::<toml::Value>(&text).ok();
|
inst.value = toml::from_str::<toml::Value>(&text).ok();
|
||||||
return write_tlv_bool(inst.value.is_some(), result, result_len);
|
return write_tlv_bool(inst.value.is_some(), result, result_len);
|
||||||
} else { return E_HANDLE; }
|
} else {
|
||||||
} else { return E_PLUGIN; }
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_GET => {
|
M_GET => {
|
||||||
let path = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
let path = match read_arg_string(args, args_len, 0) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return E_ARGS,
|
||||||
|
};
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
let mut cur = match &inst.value { Some(v) => v, None => { return write_tlv_string("", result, result_len); } };
|
let mut cur = match &inst.value {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
return write_tlv_string("", result, result_len);
|
||||||
|
}
|
||||||
|
};
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
for seg in path.split('.') {
|
for seg in path.split('.') {
|
||||||
match cur.get(seg) { Some(v) => cur = v, None => { return write_tlv_string("", result, result_len); } }
|
match cur.get(seg) {
|
||||||
|
Some(v) => cur = v,
|
||||||
|
None => {
|
||||||
|
return write_tlv_string("", result, result_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return write_tlv_string(&cur.to_string(), result, result_len);
|
return write_tlv_string(&cur.to_string(), result, result_len);
|
||||||
} else { return E_HANDLE; }
|
} else {
|
||||||
} else { return E_PLUGIN; }
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
M_TO_JSON => {
|
M_TO_JSON => {
|
||||||
if let Ok(m) = INST.lock() {
|
if let Ok(m) = INST.lock() {
|
||||||
if let Some(inst) = m.get(&instance_id) {
|
if let Some(inst) = m.get(&instance_id) {
|
||||||
if let Some(v) = &inst.value {
|
if let Some(v) = &inst.value {
|
||||||
if let Ok(s) = serde_json::to_string(v) { return write_tlv_string(&s, result, result_len); }
|
if let Ok(s) = serde_json::to_string(v) {
|
||||||
|
return write_tlv_string(&s, result, result_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return write_tlv_string("{}", result, result_len);
|
return write_tlv_string("{}", result, result_len);
|
||||||
} else { return E_HANDLE; }
|
} else {
|
||||||
} else { return E_PLUGIN; }
|
return E_HANDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return E_PLUGIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => E_METHOD,
|
_ => E_METHOD,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,47 +16,57 @@ use crate::operator_traits::{
|
|||||||
OperatorError,
|
OperatorError,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== IntegerBox Operator Implementations =====
|
// Small helpers to reduce duplication in dynamic operators
|
||||||
|
#[inline]
|
||||||
|
fn concat_result(left: &dyn NyashBox, right: &dyn NyashBox) -> Box<dyn NyashBox> {
|
||||||
|
let l = left.to_string_box();
|
||||||
|
let r = right.to_string_box();
|
||||||
|
Box::new(StringBox::new(format!("{}{}", l.value, r.value)))
|
||||||
|
}
|
||||||
|
|
||||||
/// IntegerBox + IntegerBox -> IntegerBox
|
#[inline]
|
||||||
impl NyashAdd<IntegerBox> for IntegerBox {
|
fn can_repeat(times: i64) -> bool {
|
||||||
type Output = IntegerBox;
|
(0..=10_000).contains(×)
|
||||||
|
}
|
||||||
|
|
||||||
fn add(self, rhs: IntegerBox) -> Self::Output {
|
// ===== Static numeric operators (macro-generated) =====
|
||||||
IntegerBox::new(self.value + rhs.value)
|
macro_rules! impl_static_numeric_ops {
|
||||||
|
($ty:ty, $zero:expr) => {
|
||||||
|
impl NyashAdd<$ty> for $ty {
|
||||||
|
type Output = $ty;
|
||||||
|
fn add(self, rhs: $ty) -> Self::Output {
|
||||||
|
< $ty >::new(self.value + rhs.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IntegerBox - IntegerBox -> IntegerBox
|
impl NyashSub<$ty> for $ty {
|
||||||
impl NyashSub<IntegerBox> for IntegerBox {
|
type Output = $ty;
|
||||||
type Output = IntegerBox;
|
fn sub(self, rhs: $ty) -> Self::Output {
|
||||||
|
< $ty >::new(self.value - rhs.value)
|
||||||
fn sub(self, rhs: IntegerBox) -> Self::Output {
|
|
||||||
IntegerBox::new(self.value - rhs.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IntegerBox * IntegerBox -> IntegerBox
|
impl NyashMul<$ty> for $ty {
|
||||||
impl NyashMul<IntegerBox> for IntegerBox {
|
type Output = $ty;
|
||||||
type Output = IntegerBox;
|
fn mul(self, rhs: $ty) -> Self::Output {
|
||||||
|
< $ty >::new(self.value * rhs.value)
|
||||||
fn mul(self, rhs: IntegerBox) -> Self::Output {
|
|
||||||
IntegerBox::new(self.value * rhs.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IntegerBox / IntegerBox -> IntegerBox (with zero check)
|
impl NyashDiv<$ty> for $ty {
|
||||||
impl NyashDiv<IntegerBox> for IntegerBox {
|
type Output = Result<$ty, OperatorError>;
|
||||||
type Output = Result<IntegerBox, OperatorError>;
|
fn div(self, rhs: $ty) -> Self::Output {
|
||||||
|
if rhs.value == $zero {
|
||||||
fn div(self, rhs: IntegerBox) -> Self::Output {
|
|
||||||
if rhs.value == 0 {
|
|
||||||
Err(OperatorError::DivisionByZero)
|
Err(OperatorError::DivisionByZero)
|
||||||
} else {
|
} else {
|
||||||
Ok(IntegerBox::new(self.value / rhs.value))
|
Ok(< $ty >::new(self.value / rhs.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_static_numeric_ops!(IntegerBox, 0);
|
||||||
|
|
||||||
/// Dynamic dispatch implementation for IntegerBox
|
/// Dynamic dispatch implementation for IntegerBox
|
||||||
impl DynamicAdd for IntegerBox {
|
impl DynamicAdd for IntegerBox {
|
||||||
@ -73,14 +83,8 @@ impl DynamicAdd for IntegerBox {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Convert both to strings and concatenate
|
// Fallback: Convert both to strings and concatenate (existing AddBox behavior)
|
||||||
// This preserves the existing AddBox behavior
|
Some(concat_result(self, other))
|
||||||
let left_str = self.to_string_box();
|
|
||||||
let right_str = other.to_string_box();
|
|
||||||
Some(Box::new(StringBox::new(format!(
|
|
||||||
"{}{}",
|
|
||||||
left_str.value, right_str.value
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_add_with(&self, other_type: &str) -> bool {
|
fn can_add_with(&self, other_type: &str) -> bool {
|
||||||
@ -126,8 +130,7 @@ impl DynamicMul for IntegerBox {
|
|||||||
|
|
||||||
// IntegerBox * StringBox -> Repeated string
|
// IntegerBox * StringBox -> Repeated string
|
||||||
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
|
if let Some(other_str) = other.as_any().downcast_ref::<StringBox>() {
|
||||||
if self.value >= 0 && self.value <= 10000 {
|
if can_repeat(self.value) {
|
||||||
// Safety limit
|
|
||||||
let repeated = other_str.value.repeat(self.value as usize);
|
let repeated = other_str.value.repeat(self.value as usize);
|
||||||
return Some(Box::new(StringBox::new(repeated)));
|
return Some(Box::new(StringBox::new(repeated)));
|
||||||
}
|
}
|
||||||
@ -171,47 +174,7 @@ impl DynamicDiv for IntegerBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== FloatBox Operator Implementations =====
|
impl_static_numeric_ops!(FloatBox, 0.0);
|
||||||
|
|
||||||
/// FloatBox + FloatBox -> FloatBox
|
|
||||||
impl NyashAdd<FloatBox> for FloatBox {
|
|
||||||
type Output = FloatBox;
|
|
||||||
|
|
||||||
fn add(self, rhs: FloatBox) -> Self::Output {
|
|
||||||
FloatBox::new(self.value + rhs.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FloatBox - FloatBox -> FloatBox
|
|
||||||
impl NyashSub<FloatBox> for FloatBox {
|
|
||||||
type Output = FloatBox;
|
|
||||||
|
|
||||||
fn sub(self, rhs: FloatBox) -> Self::Output {
|
|
||||||
FloatBox::new(self.value - rhs.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FloatBox * FloatBox -> FloatBox
|
|
||||||
impl NyashMul<FloatBox> for FloatBox {
|
|
||||||
type Output = FloatBox;
|
|
||||||
|
|
||||||
fn mul(self, rhs: FloatBox) -> Self::Output {
|
|
||||||
FloatBox::new(self.value * rhs.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FloatBox / FloatBox -> FloatBox (with zero check)
|
|
||||||
impl NyashDiv<FloatBox> for FloatBox {
|
|
||||||
type Output = Result<FloatBox, OperatorError>;
|
|
||||||
|
|
||||||
fn div(self, rhs: FloatBox) -> Self::Output {
|
|
||||||
if rhs.value == 0.0 {
|
|
||||||
Err(OperatorError::DivisionByZero)
|
|
||||||
} else {
|
|
||||||
Ok(FloatBox::new(self.value / rhs.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== FloatBox Dynamic Operator Implementations =====
|
// ===== FloatBox Dynamic Operator Implementations =====
|
||||||
|
|
||||||
@ -228,12 +191,7 @@ impl DynamicAdd for FloatBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Convert both to strings and concatenate
|
// Fallback: Convert both to strings and concatenate
|
||||||
let left_str = self.to_string_box();
|
Some(concat_result(self, other))
|
||||||
let right_str = other.to_string_box();
|
|
||||||
Some(Box::new(StringBox::new(format!(
|
|
||||||
"{}{}",
|
|
||||||
left_str.value, right_str.value
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_add_with(&self, other_type: &str) -> bool {
|
fn can_add_with(&self, other_type: &str) -> bool {
|
||||||
@ -344,11 +302,7 @@ impl DynamicAdd for StringBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StringBox + any other type -> Convert to string and concatenate
|
// StringBox + any other type -> Convert to string and concatenate
|
||||||
let other_str = other.to_string_box();
|
Some(concat_result(self, other))
|
||||||
Some(Box::new(StringBox::new(format!(
|
|
||||||
"{}{}",
|
|
||||||
self.value, other_str.value
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_add_with(&self, _other_type: &str) -> bool {
|
fn can_add_with(&self, _other_type: &str) -> bool {
|
||||||
@ -370,8 +324,7 @@ impl DynamicMul for StringBox {
|
|||||||
fn try_mul(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
fn try_mul(&self, other: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
||||||
// StringBox * IntegerBox -> Repeated string
|
// StringBox * IntegerBox -> Repeated string
|
||||||
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
|
if let Some(other_int) = other.as_any().downcast_ref::<IntegerBox>() {
|
||||||
if other_int.value >= 0 && other_int.value <= 10000 {
|
if can_repeat(other_int.value) {
|
||||||
// Safety limit
|
|
||||||
let repeated = self.value.repeat(other_int.value as usize);
|
let repeated = self.value.repeat(other_int.value as usize);
|
||||||
return Some(Box::new(StringBox::new(repeated)));
|
return Some(Box::new(StringBox::new(repeated)));
|
||||||
}
|
}
|
||||||
@ -505,6 +458,67 @@ impl DynamicDiv for BoolBox {
|
|||||||
pub struct OperatorResolver;
|
pub struct OperatorResolver;
|
||||||
|
|
||||||
impl OperatorResolver {
|
impl OperatorResolver {
|
||||||
|
#[inline]
|
||||||
|
fn try_dyn_left_add(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
||||||
|
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||||
|
if let Some(result) = int_box.try_add(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(str_box) = left.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
|
if let Some(result) = str_box.try_add(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||||
|
if let Some(result) = float_box.try_add(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||||
|
if let Some(result) = bool_box.try_add(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_dyn_left_sub(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
||||||
|
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||||
|
if let Some(result) = int_box.try_sub(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||||
|
if let Some(result) = float_box.try_sub(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||||
|
if let Some(result) = bool_box.try_sub(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_dyn_left_mul(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
||||||
|
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||||
|
if let Some(result) = int_box.try_mul(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(str_box) = left.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
|
if let Some(result) = str_box.try_mul(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||||
|
if let Some(result) = float_box.try_mul(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||||
|
if let Some(result) = bool_box.try_mul(right) { return Some(result); }
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_dyn_left_div(left: &dyn NyashBox, right: &dyn NyashBox) -> Option<Box<dyn NyashBox>> {
|
||||||
|
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||||
|
return int_box.try_div(right);
|
||||||
|
}
|
||||||
|
if let Some(float_box) = left.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||||
|
return float_box.try_div(right);
|
||||||
|
}
|
||||||
|
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||||
|
return bool_box.try_div(right);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
/// Resolve addition operation with hybrid dispatch
|
/// Resolve addition operation with hybrid dispatch
|
||||||
pub fn resolve_add(
|
pub fn resolve_add(
|
||||||
left: &dyn NyashBox,
|
left: &dyn NyashBox,
|
||||||
@ -513,33 +527,7 @@ impl OperatorResolver {
|
|||||||
// Try to cast to concrete types first and use their DynamicAdd implementation
|
// Try to cast to concrete types first and use their DynamicAdd implementation
|
||||||
// This approach uses the concrete types rather than trait objects
|
// This approach uses the concrete types rather than trait objects
|
||||||
|
|
||||||
// Check if left implements DynamicAdd by trying common types
|
if let Some(result) = Self::try_dyn_left_add(left, right) { return Ok(result); }
|
||||||
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
|
||||||
if let Some(result) = int_box.try_add(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(str_box) = left.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
|
||||||
if let Some(result) = str_box.try_add(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(float_box) = right
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
|
||||||
{
|
|
||||||
if let Some(result) = float_box.try_add(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
|
||||||
if let Some(result) = bool_box.try_add(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(OperatorError::UnsupportedOperation {
|
Err(OperatorError::UnsupportedOperation {
|
||||||
operator: "+".to_string(),
|
operator: "+".to_string(),
|
||||||
@ -553,21 +541,7 @@ impl OperatorResolver {
|
|||||||
left: &dyn NyashBox,
|
left: &dyn NyashBox,
|
||||||
right: &dyn NyashBox,
|
right: &dyn NyashBox,
|
||||||
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
||||||
// Try concrete types for DynamicSub
|
if let Some(result) = Self::try_dyn_left_sub(left, right) { return Ok(result); }
|
||||||
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
|
||||||
if let Some(result) = int_box.try_sub(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(float_box) = left
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
|
||||||
{
|
|
||||||
if let Some(result) = float_box.try_sub(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(OperatorError::UnsupportedOperation {
|
Err(OperatorError::UnsupportedOperation {
|
||||||
operator: "-".to_string(),
|
operator: "-".to_string(),
|
||||||
@ -581,33 +555,7 @@ impl OperatorResolver {
|
|||||||
left: &dyn NyashBox,
|
left: &dyn NyashBox,
|
||||||
right: &dyn NyashBox,
|
right: &dyn NyashBox,
|
||||||
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
||||||
// Try concrete types for DynamicMul
|
if let Some(result) = Self::try_dyn_left_mul(left, right) { return Ok(result); }
|
||||||
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
|
||||||
if let Some(result) = int_box.try_mul(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(str_box) = left.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
|
||||||
if let Some(result) = str_box.try_mul(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(float_box) = left
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
|
||||||
{
|
|
||||||
if let Some(result) = float_box.try_mul(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
|
||||||
if let Some(result) = bool_box.try_mul(right) {
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(OperatorError::UnsupportedOperation {
|
Err(OperatorError::UnsupportedOperation {
|
||||||
operator: "*".to_string(),
|
operator: "*".to_string(),
|
||||||
@ -621,35 +569,7 @@ impl OperatorResolver {
|
|||||||
left: &dyn NyashBox,
|
left: &dyn NyashBox,
|
||||||
right: &dyn NyashBox,
|
right: &dyn NyashBox,
|
||||||
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
) -> Result<Box<dyn NyashBox>, OperatorError> {
|
||||||
// Try concrete types for DynamicDiv
|
if let Some(result) = Self::try_dyn_left_div(left, right) { return Ok(result); }
|
||||||
if let Some(int_box) = left.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
|
||||||
if let Some(result) = int_box.try_div(right) {
|
|
||||||
return Ok(result);
|
|
||||||
} else {
|
|
||||||
// If try_div returns None, it might be division by zero
|
|
||||||
return Err(OperatorError::DivisionByZero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(float_box) = left
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
|
||||||
{
|
|
||||||
if let Some(result) = float_box.try_div(right) {
|
|
||||||
return Ok(result);
|
|
||||||
} else {
|
|
||||||
// If try_div returns None, it might be division by zero
|
|
||||||
return Err(OperatorError::DivisionByZero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(bool_box) = left.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
|
||||||
if let Some(result) = bool_box.try_div(right) {
|
|
||||||
return Ok(result);
|
|
||||||
} else {
|
|
||||||
return Err(OperatorError::DivisionByZero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(OperatorError::UnsupportedOperation {
|
Err(OperatorError::UnsupportedOperation {
|
||||||
operator: "/".to_string(),
|
operator: "/".to_string(),
|
||||||
|
|||||||
@ -11,6 +11,37 @@ use crate::mir::instruction::{
|
|||||||
TypeOpKind as MirTypeOpKind, WeakRefOp as MirWeakRefOp,
|
TypeOpKind as MirTypeOpKind, WeakRefOp as MirWeakRefOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Local macro utilities for generating InstructionMeta boilerplate.
|
||||||
|
// This macro is intentionally scoped to this module to avoid polluting the crate namespace.
|
||||||
|
macro_rules! inst_meta {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
pub struct $name:ident { $($field:ident : $fty:ty),* $(,)? }
|
||||||
|
=> {
|
||||||
|
from_mir = |$i:ident| $from_expr:expr;
|
||||||
|
effects = $effects:expr;
|
||||||
|
dst = $dst:expr;
|
||||||
|
used = $used:expr;
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct $name { $(pub $field: $fty),* }
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
pub fn from_mir($i: &MirInstruction) -> Option<Self> { $from_expr }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstructionMeta for $name {
|
||||||
|
fn effects(&self) -> EffectMask { ($effects)(self) }
|
||||||
|
fn dst(&self) -> Option<ValueId> { ($dst)(self) }
|
||||||
|
fn used(&self) -> Vec<ValueId> { ($used)(self) }
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub trait InstructionMeta {
|
pub trait InstructionMeta {
|
||||||
fn effects(&self) -> EffectMask;
|
fn effects(&self) -> EffectMask;
|
||||||
fn dst(&self) -> Option<ValueId>;
|
fn dst(&self) -> Option<ValueId>;
|
||||||
@ -130,499 +161,289 @@ pub fn used_via_meta(i: &MirInstruction) -> Option<Vec<ValueId>> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- BarrierRead ----
|
// ---- BarrierRead ---- (macro-generated)
|
||||||
#[derive(Debug, Clone, Copy)]
|
inst_meta! {
|
||||||
pub struct BarrierReadInst { pub ptr: ValueId }
|
pub struct BarrierReadInst { ptr: ValueId }
|
||||||
|
=> {
|
||||||
impl BarrierReadInst {
|
from_mir = |i| match i { MirInstruction::BarrierRead { ptr } => Some(BarrierReadInst { ptr: *ptr }), _ => None };
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
effects = |_: &Self| EffectMask::READ.add(Effect::Barrier);
|
||||||
match i { MirInstruction::BarrierRead { ptr } => Some(BarrierReadInst { ptr: *ptr }), _ => None }
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.ptr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for BarrierReadInst {
|
// ---- BarrierWrite ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ.add(Effect::Barrier) }
|
inst_meta! {
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
pub struct BarrierWriteInst { ptr: ValueId }
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.ptr] }
|
=> {
|
||||||
}
|
from_mir = |i| match i { MirInstruction::BarrierWrite { ptr } => Some(BarrierWriteInst { ptr: *ptr }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::WRITE.add(Effect::Barrier);
|
||||||
// ---- BarrierWrite ----
|
dst = |_: &Self| None;
|
||||||
#[derive(Debug, Clone, Copy)]
|
used = |s: &Self| vec![s.ptr];
|
||||||
pub struct BarrierWriteInst { pub ptr: ValueId }
|
|
||||||
|
|
||||||
impl BarrierWriteInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::BarrierWrite { ptr } => Some(BarrierWriteInst { ptr: *ptr }), _ => None }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for BarrierWriteInst {
|
// ---- Barrier (unified) ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask { EffectMask::WRITE.add(Effect::Barrier) }
|
inst_meta! {
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
pub struct BarrierInst { op: MirBarrierOp, ptr: ValueId }
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.ptr] }
|
=> {
|
||||||
}
|
from_mir = |i| match i { MirInstruction::Barrier { op, ptr } => Some(BarrierInst { op: *op, ptr: *ptr }), _ => None };
|
||||||
|
effects = |s: &Self| match s.op { MirBarrierOp::Read => EffectMask::READ.add(Effect::Barrier), MirBarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier) };
|
||||||
// ---- Barrier (unified) ----
|
dst = |_: &Self| None;
|
||||||
#[derive(Debug, Clone, Copy)]
|
used = |s: &Self| vec![s.ptr];
|
||||||
pub struct BarrierInst { pub op: MirBarrierOp, pub ptr: ValueId }
|
|
||||||
|
|
||||||
impl BarrierInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::Barrier { op, ptr } => Some(BarrierInst { op: *op, ptr: *ptr }), _ => None }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for BarrierInst {
|
// ---- Ref ops ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask {
|
inst_meta! {
|
||||||
match self.op {
|
pub struct RefNewInst { dst: ValueId, box_val: ValueId }
|
||||||
MirBarrierOp::Read => EffectMask::READ.add(Effect::Barrier),
|
=> {
|
||||||
MirBarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier),
|
from_mir = |i| match i { MirInstruction::RefNew { dst, box_val } => Some(RefNewInst { dst: *dst, box_val: *box_val }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.box_val];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
inst_meta! {
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.ptr] }
|
pub struct RefGetInst { dst: ValueId, reference: ValueId }
|
||||||
}
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::RefGet { dst, reference, .. } => Some(RefGetInst { dst: *dst, reference: *reference }), _ => None };
|
||||||
// ---- Ref ops ----
|
effects = |_: &Self| EffectMask::READ;
|
||||||
#[derive(Debug, Clone, Copy)]
|
dst = |s: &Self| Some(s.dst);
|
||||||
pub struct RefNewInst { pub dst: ValueId, pub box_val: ValueId }
|
used = |s: &Self| vec![s.reference];
|
||||||
impl RefNewInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::RefNew { dst, box_val } => Some(RefNewInst { dst: *dst, box_val: *box_val }), _ => None }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InstructionMeta for RefNewInst {
|
inst_meta! {
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
pub struct RefSetInst { reference: ValueId, value: ValueId }
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
=> {
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.box_val] }
|
from_mir = |i| match i { MirInstruction::RefSet { reference, value, .. } => Some(RefSetInst { reference: *reference, value: *value }), _ => None };
|
||||||
}
|
effects = |_: &Self| EffectMask::WRITE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
#[derive(Debug, Clone, Copy)]
|
used = |s: &Self| vec![s.reference, s.value];
|
||||||
pub struct RefGetInst { pub dst: ValueId, pub reference: ValueId }
|
|
||||||
impl RefGetInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::RefGet { dst, reference, .. } => Some(RefGetInst { dst: *dst, reference: *reference }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for RefGetInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.reference] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct RefSetInst { pub reference: ValueId, pub value: ValueId }
|
|
||||||
impl RefSetInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::RefSet { reference, value, .. } => Some(RefSetInst { reference: *reference, value: *value }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for RefSetInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::WRITE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.reference, self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Weak ops ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct WeakNewInst { pub dst: ValueId, pub box_val: ValueId }
|
|
||||||
impl WeakNewInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::WeakNew { dst, box_val } => Some(WeakNewInst { dst: *dst, box_val: *box_val }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for WeakNewInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.box_val] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct WeakLoadInst { pub dst: ValueId, pub weak_ref: ValueId }
|
|
||||||
impl WeakLoadInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::WeakLoad { dst, weak_ref } => Some(WeakLoadInst { dst: *dst, weak_ref: *weak_ref }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for WeakLoadInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.weak_ref] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct WeakRefInst { pub dst: ValueId, pub op: MirWeakRefOp, pub value: ValueId }
|
|
||||||
impl WeakRefInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::WeakRef { dst, op, value } => Some(WeakRefInst { dst: *dst, op: *op, value: *value }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for WeakRefInst {
|
|
||||||
fn effects(&self) -> EffectMask {
|
|
||||||
match self.op {
|
|
||||||
MirWeakRefOp::New => EffectMask::PURE,
|
|
||||||
MirWeakRefOp::Load => EffectMask::READ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Future ops ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FutureNewInst { pub dst: ValueId, pub value: ValueId }
|
|
||||||
impl FutureNewInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::FutureNew { dst, value } => Some(FutureNewInst { dst: *dst, value: *value }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for FutureNewInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Alloc) }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FutureSetInst { pub future: ValueId, pub value: ValueId }
|
|
||||||
impl FutureSetInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::FutureSet { future, value } => Some(FutureSetInst { future: *future, value: *value }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for FutureSetInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::WRITE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.future, self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct AwaitInst { pub dst: ValueId, pub future: ValueId }
|
|
||||||
impl AwaitInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::Await { dst, future } => Some(AwaitInst { dst: *dst, future: *future }), _ => None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstructionMeta for AwaitInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ.add(Effect::Async) }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.future] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- UnaryOp ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct UnaryOpInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub operand: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnaryOpInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::UnaryOp { dst, operand, .. } => Some(UnaryOpInst { dst: *dst, operand: *operand }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for UnaryOpInst {
|
// ---- Weak ops ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
inst_meta! {
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
pub struct WeakNewInst { dst: ValueId, box_val: ValueId }
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.operand] }
|
=> {
|
||||||
}
|
from_mir = |i| match i { MirInstruction::WeakNew { dst, box_val } => Some(WeakNewInst { dst: *dst, box_val: *box_val }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
// ---- Compare ----
|
dst = |s: &Self| Some(s.dst);
|
||||||
#[derive(Debug, Clone, Copy)]
|
used = |s: &Self| vec![s.box_val];
|
||||||
pub struct CompareInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub lhs: ValueId,
|
|
||||||
pub rhs: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompareInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Compare { dst, lhs, rhs, .. } => Some(CompareInst { dst: *dst, lhs: *lhs, rhs: *rhs }),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
inst_meta! {
|
||||||
|
pub struct WeakLoadInst { dst: ValueId, weak_ref: ValueId }
|
||||||
impl InstructionMeta for CompareInst {
|
=> {
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
from_mir = |i| match i { MirInstruction::WeakLoad { dst, weak_ref } => Some(WeakLoadInst { dst: *dst, weak_ref: *weak_ref }), _ => None };
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
effects = |_: &Self| EffectMask::READ;
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.lhs, self.rhs] }
|
dst = |s: &Self| Some(s.dst);
|
||||||
}
|
used = |s: &Self| vec![s.weak_ref];
|
||||||
|
|
||||||
// ---- Load ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct LoadInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub ptr: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoadInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Load { dst, ptr } => Some(LoadInst { dst: *dst, ptr: *ptr }),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
inst_meta! {
|
||||||
|
pub struct WeakRefInst { dst: ValueId, op: MirWeakRefOp, value: ValueId }
|
||||||
impl InstructionMeta for LoadInst {
|
=> {
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ }
|
from_mir = |i| match i { MirInstruction::WeakRef { dst, op, value } => Some(WeakRefInst { dst: *dst, op: *op, value: *value }), _ => None };
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
effects = |s: &Self| match s.op { MirWeakRefOp::New => EffectMask::PURE, MirWeakRefOp::Load => EffectMask::READ };
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.ptr] }
|
dst = |s: &Self| Some(s.dst);
|
||||||
}
|
used = |s: &Self| vec![s.value];
|
||||||
|
|
||||||
// ---- Cast ----
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CastInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub value: ValueId,
|
|
||||||
pub target_type: MirType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CastInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Cast { dst, value, target_type } =>
|
|
||||||
Some(CastInst { dst: *dst, value: *value, target_type: target_type.clone() }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for CastInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- TypeOp ----
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct TypeOpInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub op: MirTypeOpKind,
|
|
||||||
pub value: ValueId,
|
|
||||||
pub ty: MirType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeOpInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::TypeOp { dst, op, value, ty } =>
|
|
||||||
Some(TypeOpInst { dst: *dst, op: *op, value: *value, ty: ty.clone() }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for TypeOpInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- ArrayGet ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ArrayGetInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub array: ValueId,
|
|
||||||
pub index: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrayGetInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::ArrayGet { dst, array, index } =>
|
|
||||||
Some(ArrayGetInst { dst: *dst, array: *array, index: *index }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for ArrayGetInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::READ }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.array, self.index] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Phi ----
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PhiInst { pub dst: ValueId, pub inputs: Vec<(BasicBlockId, ValueId)> }
|
|
||||||
|
|
||||||
impl PhiInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Phi { dst, inputs } => Some(PhiInst { dst: *dst, inputs: inputs.clone() }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for PhiInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { self.inputs.iter().map(|(_, v)| *v).collect() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- NewBox ----
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct NewBoxInst {
|
|
||||||
pub dst: ValueId,
|
|
||||||
pub args: Vec<ValueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NewBoxInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::NewBox { dst, args, .. } =>
|
|
||||||
Some(NewBoxInst { dst: *dst, args: args.clone() }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for NewBoxInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Alloc) }
|
|
||||||
fn dst(&self) -> Option<ValueId> { Some(self.dst) }
|
|
||||||
fn used(&self) -> Vec<ValueId> { self.args.clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Store ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct StoreInst {
|
|
||||||
pub value: ValueId,
|
|
||||||
pub ptr: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StoreInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Store { value, ptr } => Some(StoreInst { value: *value, ptr: *ptr }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for StoreInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::WRITE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value, self.ptr] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- ArraySet ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ArraySetInst {
|
|
||||||
pub array: ValueId,
|
|
||||||
pub index: ValueId,
|
|
||||||
pub value: ValueId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArraySetInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::ArraySet { array, index, value } =>
|
|
||||||
Some(ArraySetInst { array: *array, index: *index, value: *value }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for ArraySetInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::WRITE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.array, self.index, self.value] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Return ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct ReturnInst { pub value: Option<ValueId> }
|
|
||||||
|
|
||||||
impl ReturnInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Return { value } => Some(ReturnInst { value: *value }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for ReturnInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { self.value.map(|v| vec![v]).unwrap_or_default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Branch ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct BranchInst { pub condition: ValueId }
|
|
||||||
|
|
||||||
impl BranchInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Branch { condition, .. } => Some(BranchInst { condition: *condition }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InstructionMeta for BranchInst {
|
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.condition] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Jump ----
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct JumpInst;
|
|
||||||
|
|
||||||
impl JumpInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i { MirInstruction::Jump { .. } => Some(JumpInst), _ => None }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for JumpInst {
|
// ---- Future ops ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE }
|
inst_meta! {
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
pub struct FutureNewInst { dst: ValueId, value: ValueId }
|
||||||
fn used(&self) -> Vec<ValueId> { Vec::new() }
|
=> {
|
||||||
}
|
from_mir = |i| match i { MirInstruction::FutureNew { dst, value } => Some(FutureNewInst { dst: *dst, value: *value }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE.add(Effect::Alloc);
|
||||||
// ---- Print ----
|
dst = |s: &Self| Some(s.dst);
|
||||||
#[derive(Debug, Clone, Copy)]
|
used = |s: &Self| vec![s.value];
|
||||||
pub struct PrintInst { pub value: ValueId, pub effects_mask: EffectMask }
|
|
||||||
|
|
||||||
impl PrintInst {
|
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
|
||||||
match i {
|
|
||||||
MirInstruction::Print { value, effects } => Some(PrintInst { value: *value, effects_mask: *effects }),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inst_meta! {
|
||||||
|
pub struct FutureSetInst { future: ValueId, value: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::FutureSet { future, value } => Some(FutureSetInst { future: *future, value: *value }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::WRITE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.future, s.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for PrintInst {
|
|
||||||
fn effects(&self) -> EffectMask { self.effects_mask }
|
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
|
||||||
}
|
}
|
||||||
|
inst_meta! {
|
||||||
// ---- Debug ----
|
pub struct AwaitInst { dst: ValueId, future: ValueId }
|
||||||
#[derive(Debug, Clone, Copy)]
|
=> {
|
||||||
pub struct DebugInst { pub value: ValueId }
|
from_mir = |i| match i { MirInstruction::Await { dst, future } => Some(AwaitInst { dst: *dst, future: *future }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::READ.add(Effect::Async);
|
||||||
impl DebugInst {
|
dst = |s: &Self| Some(s.dst);
|
||||||
pub fn from_mir(i: &MirInstruction) -> Option<Self> {
|
used = |s: &Self| vec![s.future];
|
||||||
match i { MirInstruction::Debug { value, .. } => Some(DebugInst { value: *value }), _ => None }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionMeta for DebugInst {
|
// ---- UnaryOp ---- (macro-generated)
|
||||||
fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Debug) }
|
inst_meta! {
|
||||||
fn dst(&self) -> Option<ValueId> { None }
|
pub struct UnaryOpInst { dst: ValueId, operand: ValueId }
|
||||||
fn used(&self) -> Vec<ValueId> { vec![self.value] }
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::UnaryOp { dst, operand, .. } => Some(UnaryOpInst { dst: *dst, operand: *operand }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.operand];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Compare ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct CompareInst { dst: ValueId, lhs: ValueId, rhs: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Compare { dst, lhs, rhs, .. } => Some(CompareInst { dst: *dst, lhs: *lhs, rhs: *rhs }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.lhs, s.rhs];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Load ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct LoadInst { dst: ValueId, ptr: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Load { dst, ptr } => Some(LoadInst { dst: *dst, ptr: *ptr }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::READ;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.ptr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Cast ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct CastInst { dst: ValueId, value: ValueId, target_type: MirType }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Cast { dst, value, target_type } => Some(CastInst { dst: *dst, value: *value, target_type: target_type.clone() }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- TypeOp ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct TypeOpInst { dst: ValueId, op: MirTypeOpKind, value: ValueId, ty: MirType }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::TypeOp { dst, op, value, ty } => Some(TypeOpInst { dst: *dst, op: *op, value: *value, ty: ty.clone() }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ArrayGet ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct ArrayGetInst { dst: ValueId, array: ValueId, index: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::ArrayGet { dst, array, index } => Some(ArrayGetInst { dst: *dst, array: *array, index: *index }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::READ;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| vec![s.array, s.index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Phi ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct PhiInst { dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)> }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Phi { dst, inputs } => Some(PhiInst { dst: *dst, inputs: inputs.clone() }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| s.inputs.iter().map(|(_, v)| *v).collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- NewBox ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct NewBoxInst { dst: ValueId, args: Vec<ValueId> }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::NewBox { dst, args, .. } => Some(NewBoxInst { dst: *dst, args: args.clone() }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE.add(Effect::Alloc);
|
||||||
|
dst = |s: &Self| Some(s.dst);
|
||||||
|
used = |s: &Self| s.args.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Store ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct StoreInst { value: ValueId, ptr: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Store { value, ptr } => Some(StoreInst { value: *value, ptr: *ptr }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::WRITE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.value, s.ptr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ArraySet ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct ArraySetInst { array: ValueId, index: ValueId, value: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::ArraySet { array, index, value } => Some(ArraySetInst { array: *array, index: *index, value: *value }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::WRITE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.array, s.index, s.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Return ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct ReturnInst { value: Option<ValueId> }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Return { value } => Some(ReturnInst { value: *value }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| s.value.map(|v| vec![v]).unwrap_or_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Branch ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct BranchInst { condition: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Branch { condition, .. } => Some(BranchInst { condition: *condition }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.condition];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Jump ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct JumpInst { }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Jump { .. } => Some(JumpInst {}), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |_: &Self| Vec::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Print ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct PrintInst { value: ValueId, effects_mask: EffectMask }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Print { value, effects } => Some(PrintInst { value: *value, effects_mask: *effects }), _ => None };
|
||||||
|
effects = |s: &Self| s.effects_mask;
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Debug ---- (macro-generated)
|
||||||
|
inst_meta! {
|
||||||
|
pub struct DebugInst { value: ValueId }
|
||||||
|
=> {
|
||||||
|
from_mir = |i| match i { MirInstruction::Debug { value, .. } => Some(DebugInst { value: *value }), _ => None };
|
||||||
|
effects = |_: &Self| EffectMask::PURE.add(Effect::Debug);
|
||||||
|
dst = |_: &Self| None;
|
||||||
|
used = |s: &Self| vec![s.value];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Call-like (dst/used only; effects fallback in MirInstruction) ----
|
// ---- Call-like (dst/used only; effects fallback in MirInstruction) ----
|
||||||
|
|||||||
@ -3,18 +3,88 @@ mod tests {
|
|||||||
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
|
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
|
||||||
use crate::boxes::array::ArrayBox;
|
use crate::boxes::array::ArrayBox;
|
||||||
use crate::boxes::math_box::FloatBox;
|
use crate::boxes::math_box::FloatBox;
|
||||||
|
use crate::runtime::plugin_loader_unified::PluginHost;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
// RAII: environment variable guard (restores on drop)
|
||||||
|
struct EnvGuard {
|
||||||
|
key: &'static str,
|
||||||
|
prev: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnvGuard {
|
||||||
|
fn set(key: &'static str, val: &str) -> Self {
|
||||||
|
let prev = env::var(key).ok();
|
||||||
|
env::set_var(key, val);
|
||||||
|
EnvGuard { key, prev }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(key: &'static str) -> Self {
|
||||||
|
let prev = env::var(key).ok();
|
||||||
|
env::remove_var(key);
|
||||||
|
EnvGuard { key, prev }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EnvGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match &self.prev {
|
||||||
|
Some(v) => env::set_var(self.key, v),
|
||||||
|
None => env::remove_var(self.key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: read-lock the global plugin host and pass immutable ref to closure
|
||||||
|
fn with_host<R>(f: impl FnOnce(&PluginHost) -> R) -> R {
|
||||||
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
let guard = host.read().expect("plugin host RwLock poisoned");
|
||||||
|
f(&*guard)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Test helpers (invoke wrappers) ----
|
||||||
|
fn inv_ok(
|
||||||
|
h: &PluginHost,
|
||||||
|
box_ty: &str,
|
||||||
|
method: &str,
|
||||||
|
id: u32,
|
||||||
|
args: &[Box<dyn NyashBox>],
|
||||||
|
) -> Option<Box<dyn NyashBox>> {
|
||||||
|
h.invoke_instance_method(box_ty, method, id, args)
|
||||||
|
.expect(&format!("invoke {}::{}", box_ty, method))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inv_some(
|
||||||
|
h: &PluginHost,
|
||||||
|
box_ty: &str,
|
||||||
|
method: &str,
|
||||||
|
id: u32,
|
||||||
|
args: &[Box<dyn NyashBox>],
|
||||||
|
) -> Box<dyn NyashBox> {
|
||||||
|
inv_ok(h, box_ty, method, id, args)
|
||||||
|
.unwrap_or_else(|| panic!("{}::{} returned None", box_ty, method))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inv_void(
|
||||||
|
h: &PluginHost,
|
||||||
|
box_ty: &str,
|
||||||
|
method: &str,
|
||||||
|
id: u32,
|
||||||
|
args: &[Box<dyn NyashBox>],
|
||||||
|
) {
|
||||||
|
let _ = h
|
||||||
|
.invoke_instance_method(box_ty, method, id, args)
|
||||||
|
.expect(&format!("invoke {}::{}", box_ty, method));
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_host() {
|
fn ensure_host() {
|
||||||
let _ = crate::runtime::init_global_plugin_host("nyash.toml");
|
let _ = crate::runtime::init_global_plugin_host("nyash.toml");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_plugin_instance(box_type: &str) -> (String, u32, Box<dyn NyashBox>) {
|
fn create_plugin_instance(box_type: &str) -> (String, u32, Box<dyn NyashBox>) {
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
let bx = with_host(|h| h.create_box(box_type, &[]).expect("create_box"));
|
||||||
let host = host.read().unwrap();
|
|
||||||
let bx = host.create_box(box_type, &[]).expect("create_box");
|
|
||||||
// Downcast to PluginBoxV2 to get instance_id
|
// Downcast to PluginBoxV2 to get instance_id
|
||||||
if let Some(p) = bx
|
if let Some(p) = bx
|
||||||
.as_any()
|
.as_any()
|
||||||
@ -30,58 +100,25 @@ mod tests {
|
|||||||
#[ignore = "MIR13 parity: MapBox TLV vs TypeBox under unified BoxCall/TypeOp pending"]
|
#[ignore = "MIR13 parity: MapBox TLV vs TypeBox under unified BoxCall/TypeOp pending"]
|
||||||
fn mapbox_get_set_size_tlv_vs_typebox() {
|
fn mapbox_get_set_size_tlv_vs_typebox() {
|
||||||
ensure_host();
|
ensure_host();
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
// TLV path: disable typebox (restored automatically)
|
||||||
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
// TLV path: disable typebox
|
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("MapBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("MapBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt1, "set", id1, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]);
|
||||||
// set("k", 42)
|
let sz = inv_some(h, &bt1, "size", id1, &[]);
|
||||||
let _ = h
|
let gv = inv_some(h, &bt1, "get", id1, &[Box::new(StringBox::new("k"))]);
|
||||||
.invoke_instance_method(
|
|
||||||
&bt1,
|
|
||||||
"set",
|
|
||||||
id1,
|
|
||||||
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
|
|
||||||
)
|
|
||||||
.expect("set tlv");
|
|
||||||
// size()
|
|
||||||
let sz = h
|
|
||||||
.invoke_instance_method(&bt1, "size", id1, &[])
|
|
||||||
.expect("size tlv")
|
|
||||||
.unwrap();
|
|
||||||
// get("k")
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt1, "get", id1, &[Box::new(StringBox::new("k"))])
|
|
||||||
.expect("get tlv")
|
|
||||||
.unwrap();
|
|
||||||
(sz.to_string_box().value, gv.to_string_box().value)
|
(sz.to_string_box().value, gv.to_string_box().value)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path: enable typebox
|
// TypeBox path: enable typebox
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("MapBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("MapBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt2, "set", id2, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]);
|
||||||
let _ = h
|
let sz = inv_some(h, &bt2, "size", id2, &[]);
|
||||||
.invoke_instance_method(
|
let gv = inv_some(h, &bt2, "get", id2, &[Box::new(StringBox::new("k"))]);
|
||||||
&bt2,
|
|
||||||
"set",
|
|
||||||
id2,
|
|
||||||
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
|
|
||||||
)
|
|
||||||
.expect("set tb");
|
|
||||||
let sz = h
|
|
||||||
.invoke_instance_method(&bt2, "size", id2, &[])
|
|
||||||
.expect("size tb")
|
|
||||||
.unwrap();
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt2, "get", id2, &[Box::new(StringBox::new("k"))])
|
|
||||||
.expect("get tb")
|
|
||||||
.unwrap();
|
|
||||||
(sz.to_string_box().value, gv.to_string_box().value)
|
(sz.to_string_box().value, gv.to_string_box().value)
|
||||||
};
|
});
|
||||||
|
|
||||||
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match");
|
assert_eq!(out_tlv, out_tb, "TLV vs TypeBox results should match");
|
||||||
}
|
}
|
||||||
@ -90,53 +127,24 @@ mod tests {
|
|||||||
#[ignore = "MIR13 parity: ArrayBox len/get under unified ops pending"]
|
#[ignore = "MIR13 parity: ArrayBox len/get under unified ops pending"]
|
||||||
fn arraybox_set_get_len_tlv_vs_typebox() {
|
fn arraybox_set_get_len_tlv_vs_typebox() {
|
||||||
ensure_host();
|
ensure_host();
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
// TLV path (guarded)
|
||||||
// TLV path
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt1, "set", id1, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]);
|
||||||
let _ = h
|
let ln = inv_some(h, &bt1, "len", id1, &[]);
|
||||||
.invoke_instance_method(
|
let gv = inv_some(h, &bt1, "get", id1, &[Box::new(IntegerBox::new(0))]);
|
||||||
&bt1,
|
|
||||||
"set",
|
|
||||||
id1,
|
|
||||||
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
|
|
||||||
)
|
|
||||||
.expect("set tlv");
|
|
||||||
let ln = h
|
|
||||||
.invoke_instance_method(&bt1, "len", id1, &[])
|
|
||||||
.expect("len tlv")
|
|
||||||
.unwrap();
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt1, "get", id1, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("get tlv")
|
|
||||||
.unwrap();
|
|
||||||
(ln.to_string_box().value, gv.to_string_box().value)
|
(ln.to_string_box().value, gv.to_string_box().value)
|
||||||
};
|
});
|
||||||
// TypeBox path
|
// TypeBox path (guarded)
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt2, "set", id2, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]);
|
||||||
let _ = h
|
let ln = inv_some(h, &bt2, "length", id2, &[]);
|
||||||
.invoke_instance_method(
|
let gv = inv_some(h, &bt2, "get", id2, &[Box::new(IntegerBox::new(0))]);
|
||||||
&bt2,
|
|
||||||
"set",
|
|
||||||
id2,
|
|
||||||
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
|
|
||||||
)
|
|
||||||
.expect("set tb");
|
|
||||||
let ln = h
|
|
||||||
.invoke_instance_method(&bt2, "length", id2, &[])
|
|
||||||
.expect("len tb")
|
|
||||||
.unwrap();
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt2, "get", id2, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("get tb")
|
|
||||||
.unwrap();
|
|
||||||
(ln.to_string_box().value, gv.to_string_box().value)
|
(ln.to_string_box().value, gv.to_string_box().value)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (ArrayBox)"
|
"TLV vs TypeBox results should match (ArrayBox)"
|
||||||
@ -149,36 +157,22 @@ mod tests {
|
|||||||
ensure_host();
|
ensure_host();
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("StringBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("StringBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
|
||||||
// birth with init string: use fromUtf8 via set of arg in create? Current loader birth() no-arg, so concat
|
// birth with init string: use fromUtf8 via set of arg in create? Current loader birth() no-arg, so concat
|
||||||
let _ = h
|
inv_void(h, &bt1, "concat", id1, &[Box::new(StringBox::new("ab"))]);
|
||||||
.invoke_instance_method(&bt1, "concat", id1, &[Box::new(StringBox::new("ab"))])
|
let ln = inv_some(h, &bt1, "length", id1, &[]);
|
||||||
.expect("concat tlv")
|
|
||||||
.unwrap();
|
|
||||||
let ln = h
|
|
||||||
.invoke_instance_method(&bt1, "length", id1, &[])
|
|
||||||
.expect("len tlv")
|
|
||||||
.unwrap();
|
|
||||||
(ln.to_string_box().value)
|
(ln.to_string_box().value)
|
||||||
};
|
});
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("StringBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("StringBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt2, "concat", id2, &[Box::new(StringBox::new("ab"))]);
|
||||||
let _ = h
|
let ln = inv_some(h, &bt2, "length", id2, &[]);
|
||||||
.invoke_instance_method(&bt2, "concat", id2, &[Box::new(StringBox::new("ab"))])
|
|
||||||
.expect("concat tb")
|
|
||||||
.unwrap();
|
|
||||||
let ln = h
|
|
||||||
.invoke_instance_method(&bt2, "length", id2, &[])
|
|
||||||
.expect("len tb")
|
|
||||||
.unwrap();
|
|
||||||
(ln.to_string_box().value)
|
(ln.to_string_box().value)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (StringBox)"
|
"TLV vs TypeBox results should match (StringBox)"
|
||||||
@ -191,35 +185,21 @@ mod tests {
|
|||||||
ensure_host();
|
ensure_host();
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("IntegerBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("IntegerBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt1, "set", id1, &[Box::new(IntegerBox::new(123))]);
|
||||||
let _ = h
|
let gv = inv_some(h, &bt1, "get", id1, &[]);
|
||||||
.invoke_instance_method(&bt1, "set", id1, &[Box::new(IntegerBox::new(123))])
|
|
||||||
.expect("set tlv")
|
|
||||||
.unwrap();
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt1, "get", id1, &[])
|
|
||||||
.expect("get tlv")
|
|
||||||
.unwrap();
|
|
||||||
gv.to_string_box().value
|
gv.to_string_box().value
|
||||||
};
|
});
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("IntegerBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("IntegerBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt2, "set", id2, &[Box::new(IntegerBox::new(123))]);
|
||||||
let _ = h
|
let gv = inv_some(h, &bt2, "get", id2, &[]);
|
||||||
.invoke_instance_method(&bt2, "set", id2, &[Box::new(IntegerBox::new(123))])
|
|
||||||
.expect("set tb")
|
|
||||||
.unwrap();
|
|
||||||
let gv = h
|
|
||||||
.invoke_instance_method(&bt2, "get", id2, &[])
|
|
||||||
.expect("get tb")
|
|
||||||
.unwrap();
|
|
||||||
gv.to_string_box().value
|
gv.to_string_box().value
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (IntegerBox)"
|
"TLV vs TypeBox results should match (IntegerBox)"
|
||||||
@ -232,25 +212,23 @@ mod tests {
|
|||||||
ensure_host();
|
ensure_host();
|
||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("ConsoleBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("ConsoleBox");
|
||||||
let out_tlv_is_none = {
|
let out_tlv_is_none = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
|
||||||
let rv = h
|
let rv = h
|
||||||
.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))])
|
.invoke_instance_method(&bt1, "println", id1, &[Box::new(StringBox::new("hello"))])
|
||||||
.expect("println tlv");
|
.expect("println tlv");
|
||||||
rv.is_none()
|
rv.is_none()
|
||||||
};
|
});
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("ConsoleBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("ConsoleBox");
|
||||||
let out_tb_is_none = {
|
let out_tb_is_none = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
|
||||||
let rv = h
|
let rv = h
|
||||||
.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))])
|
.invoke_instance_method(&bt2, "println", id2, &[Box::new(StringBox::new("hello"))])
|
||||||
.expect("println tb");
|
.expect("println tb");
|
||||||
rv.is_none()
|
rv.is_none()
|
||||||
};
|
});
|
||||||
assert!(
|
assert!(
|
||||||
out_tlv_is_none && out_tb_is_none,
|
out_tlv_is_none && out_tb_is_none,
|
||||||
"println should return void/None in both modes"
|
"println should return void/None in both modes"
|
||||||
@ -264,62 +242,36 @@ mod tests {
|
|||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("MathBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("MathBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let s1 = inv_some(h, &bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))]);
|
||||||
let s1 = h
|
let s2 = inv_some(h, &bt1, "sin", id1, &[Box::new(IntegerBox::new(0))]);
|
||||||
.invoke_instance_method(&bt1, "sqrt", id1, &[Box::new(IntegerBox::new(9))])
|
let s3 = inv_some(h, &bt1, "cos", id1, &[Box::new(IntegerBox::new(0))]);
|
||||||
.expect("sqrt tlv")
|
let s4 = inv_some(h, &bt1, "round", id1, &[Box::new(IntegerBox::new(26))]);
|
||||||
.unwrap();
|
|
||||||
let s2 = h
|
|
||||||
.invoke_instance_method(&bt1, "sin", id1, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("sin tlv")
|
|
||||||
.unwrap();
|
|
||||||
let s3 = h
|
|
||||||
.invoke_instance_method(&bt1, "cos", id1, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("cos tlv")
|
|
||||||
.unwrap();
|
|
||||||
let s4 = h
|
|
||||||
.invoke_instance_method(&bt1, "round", id1, &[Box::new(IntegerBox::new(26))])
|
|
||||||
.expect("round tlv")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
s1.to_string_box().value,
|
s1.to_string_box().value,
|
||||||
s2.to_string_box().value,
|
s2.to_string_box().value,
|
||||||
s3.to_string_box().value,
|
s3.to_string_box().value,
|
||||||
s4.to_string_box().value,
|
s4.to_string_box().value,
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("MathBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("MathBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let s1 = inv_some(h, &bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))]);
|
||||||
let s1 = h
|
let s2 = inv_some(h, &bt2, "sin", id2, &[Box::new(IntegerBox::new(0))]);
|
||||||
.invoke_instance_method(&bt2, "sqrt", id2, &[Box::new(IntegerBox::new(9))])
|
let s3 = inv_some(h, &bt2, "cos", id2, &[Box::new(IntegerBox::new(0))]);
|
||||||
.expect("sqrt tb")
|
let s4 = inv_some(h, &bt2, "round", id2, &[Box::new(IntegerBox::new(26))]);
|
||||||
.unwrap();
|
|
||||||
let s2 = h
|
|
||||||
.invoke_instance_method(&bt2, "sin", id2, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("sin tb")
|
|
||||||
.unwrap();
|
|
||||||
let s3 = h
|
|
||||||
.invoke_instance_method(&bt2, "cos", id2, &[Box::new(IntegerBox::new(0))])
|
|
||||||
.expect("cos tb")
|
|
||||||
.unwrap();
|
|
||||||
let s4 = h
|
|
||||||
.invoke_instance_method(&bt2, "round", id2, &[Box::new(IntegerBox::new(26))])
|
|
||||||
.expect("round tb")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
s1.to_string_box().value,
|
s1.to_string_box().value,
|
||||||
s2.to_string_box().value,
|
s2.to_string_box().value,
|
||||||
s3.to_string_box().value,
|
s3.to_string_box().value,
|
||||||
s4.to_string_box().value,
|
s4.to_string_box().value,
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (MathBox)"
|
"TLV vs TypeBox results should match (MathBox)"
|
||||||
@ -341,46 +293,34 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("EncodingBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("EncodingBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let b64 = inv_some(
|
||||||
let b64 = h
|
h,
|
||||||
.invoke_instance_method(
|
|
||||||
&bt1,
|
&bt1,
|
||||||
"base64Encode",
|
"base64Encode",
|
||||||
id1,
|
id1,
|
||||||
&[Box::new(StringBox::new("hi"))],
|
&[Box::new(StringBox::new("hi"))],
|
||||||
)
|
);
|
||||||
.expect("b64 tlv")
|
let hex = inv_some(h, &bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))]);
|
||||||
.unwrap();
|
|
||||||
let hex = h
|
|
||||||
.invoke_instance_method(&bt1, "hexEncode", id1, &[Box::new(StringBox::new("hi"))])
|
|
||||||
.expect("hex tlv")
|
|
||||||
.unwrap();
|
|
||||||
(b64.to_string_box().value, hex.to_string_box().value)
|
(b64.to_string_box().value, hex.to_string_box().value)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("EncodingBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("EncodingBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let b64 = inv_some(
|
||||||
let b64 = h
|
h,
|
||||||
.invoke_instance_method(
|
|
||||||
&bt2,
|
&bt2,
|
||||||
"base64Encode",
|
"base64Encode",
|
||||||
id2,
|
id2,
|
||||||
&[Box::new(StringBox::new("hi"))],
|
&[Box::new(StringBox::new("hi"))],
|
||||||
)
|
);
|
||||||
.expect("b64 tb")
|
let hex = inv_some(h, &bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))]);
|
||||||
.unwrap();
|
|
||||||
let hex = h
|
|
||||||
.invoke_instance_method(&bt2, "hexEncode", id2, &[Box::new(StringBox::new("hi"))])
|
|
||||||
.expect("hex tb")
|
|
||||||
.unwrap();
|
|
||||||
(b64.to_string_box().value, hex.to_string_box().value)
|
(b64.to_string_box().value, hex.to_string_box().value)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (EncodingBox)"
|
"TLV vs TypeBox results should match (EncodingBox)"
|
||||||
@ -394,42 +334,24 @@ mod tests {
|
|||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("RegexBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("RegexBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]);
|
||||||
let _ = h
|
let m = inv_some(h, &bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]);
|
||||||
.invoke_instance_method(&bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))])
|
let f = inv_some(h, &bt1, "find", id1, &[Box::new(StringBox::new("hello"))]);
|
||||||
.expect("compile tlv");
|
|
||||||
let m = h
|
|
||||||
.invoke_instance_method(&bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))])
|
|
||||||
.expect("isMatch tlv")
|
|
||||||
.unwrap();
|
|
||||||
let f = h
|
|
||||||
.invoke_instance_method(&bt1, "find", id1, &[Box::new(StringBox::new("hello"))])
|
|
||||||
.expect("find tlv")
|
|
||||||
.unwrap();
|
|
||||||
(m.to_string_box().value, f.to_string_box().value)
|
(m.to_string_box().value, f.to_string_box().value)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("RegexBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("RegexBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(h, &bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]);
|
||||||
let _ = h
|
let m = inv_some(h, &bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]);
|
||||||
.invoke_instance_method(&bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))])
|
let f = inv_some(h, &bt2, "find", id2, &[Box::new(StringBox::new("hello"))]);
|
||||||
.expect("compile tb");
|
|
||||||
let m = h
|
|
||||||
.invoke_instance_method(&bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))])
|
|
||||||
.expect("isMatch tb")
|
|
||||||
.unwrap();
|
|
||||||
let f = h
|
|
||||||
.invoke_instance_method(&bt2, "find", id2, &[Box::new(StringBox::new("hello"))])
|
|
||||||
.expect("find tb")
|
|
||||||
.unwrap();
|
|
||||||
(m.to_string_box().value, f.to_string_box().value)
|
(m.to_string_box().value, f.to_string_box().value)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (RegexBox)"
|
"TLV vs TypeBox results should match (RegexBox)"
|
||||||
@ -443,12 +365,11 @@ mod tests {
|
|||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("PathBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("PathBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let j = inv_some(
|
||||||
let j = h
|
h,
|
||||||
.invoke_instance_method(
|
|
||||||
&bt1,
|
&bt1,
|
||||||
"join",
|
"join",
|
||||||
id1,
|
id1,
|
||||||
@ -456,51 +377,30 @@ mod tests {
|
|||||||
Box::new(StringBox::new("/a/b")),
|
Box::new(StringBox::new("/a/b")),
|
||||||
Box::new(StringBox::new("c.txt")),
|
Box::new(StringBox::new("c.txt")),
|
||||||
],
|
],
|
||||||
)
|
);
|
||||||
.expect("join tlv")
|
let d = inv_some(h, &bt1, "dirname", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||||
.unwrap();
|
let b = inv_some(h, &bt1, "basename", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||||
let d = h
|
let n = inv_some(
|
||||||
.invoke_instance_method(
|
h,
|
||||||
&bt1,
|
|
||||||
"dirname",
|
|
||||||
id1,
|
|
||||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
|
||||||
)
|
|
||||||
.expect("dirname tlv")
|
|
||||||
.unwrap();
|
|
||||||
let b = h
|
|
||||||
.invoke_instance_method(
|
|
||||||
&bt1,
|
|
||||||
"basename",
|
|
||||||
id1,
|
|
||||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
|
||||||
)
|
|
||||||
.expect("basename tlv")
|
|
||||||
.unwrap();
|
|
||||||
let n = h
|
|
||||||
.invoke_instance_method(
|
|
||||||
&bt1,
|
&bt1,
|
||||||
"normalize",
|
"normalize",
|
||||||
id1,
|
id1,
|
||||||
&[Box::new(StringBox::new("/a/./b/../b/c"))],
|
&[Box::new(StringBox::new("/a/./b/../b/c"))],
|
||||||
)
|
);
|
||||||
.expect("normalize tlv")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
j.to_string_box().value,
|
j.to_string_box().value,
|
||||||
d.to_string_box().value,
|
d.to_string_box().value,
|
||||||
b.to_string_box().value,
|
b.to_string_box().value,
|
||||||
n.to_string_box().value,
|
n.to_string_box().value,
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("PathBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("PathBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let j = inv_some(
|
||||||
let j = h
|
h,
|
||||||
.invoke_instance_method(
|
|
||||||
&bt2,
|
&bt2,
|
||||||
"join",
|
"join",
|
||||||
id2,
|
id2,
|
||||||
@ -508,43 +408,23 @@ mod tests {
|
|||||||
Box::new(StringBox::new("/a/b")),
|
Box::new(StringBox::new("/a/b")),
|
||||||
Box::new(StringBox::new("c.txt")),
|
Box::new(StringBox::new("c.txt")),
|
||||||
],
|
],
|
||||||
)
|
);
|
||||||
.expect("join tb")
|
let d = inv_some(h, &bt2, "dirname", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||||
.unwrap();
|
let b = inv_some(h, &bt2, "basename", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||||
let d = h
|
let n = inv_some(
|
||||||
.invoke_instance_method(
|
h,
|
||||||
&bt2,
|
|
||||||
"dirname",
|
|
||||||
id2,
|
|
||||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
|
||||||
)
|
|
||||||
.expect("dirname tb")
|
|
||||||
.unwrap();
|
|
||||||
let b = h
|
|
||||||
.invoke_instance_method(
|
|
||||||
&bt2,
|
|
||||||
"basename",
|
|
||||||
id2,
|
|
||||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
|
||||||
)
|
|
||||||
.expect("basename tb")
|
|
||||||
.unwrap();
|
|
||||||
let n = h
|
|
||||||
.invoke_instance_method(
|
|
||||||
&bt2,
|
&bt2,
|
||||||
"normalize",
|
"normalize",
|
||||||
id2,
|
id2,
|
||||||
&[Box::new(StringBox::new("/a/./b/../b/c"))],
|
&[Box::new(StringBox::new("/a/./b/../b/c"))],
|
||||||
)
|
);
|
||||||
.expect("normalize tb")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
j.to_string_box().value,
|
j.to_string_box().value,
|
||||||
d.to_string_box().value,
|
d.to_string_box().value,
|
||||||
b.to_string_box().value,
|
b.to_string_box().value,
|
||||||
n.to_string_box().value,
|
n.to_string_box().value,
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (PathBox)"
|
"TLV vs TypeBox results should match (PathBox)"
|
||||||
@ -559,54 +439,48 @@ mod tests {
|
|||||||
let toml_text = "[package]\nname=\"nyash\"\n[deps]\nregex=\"1\"\n";
|
let toml_text = "[package]\nname=\"nyash\"\n[deps]\nregex=\"1\"\n";
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("TOMLBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("TOMLBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(
|
||||||
let _ = h
|
h,
|
||||||
.invoke_instance_method(&bt1, "parse", id1, &[Box::new(StringBox::new(toml_text))])
|
&bt1,
|
||||||
.expect("parse tlv")
|
"parse",
|
||||||
.unwrap();
|
id1,
|
||||||
let name = h
|
&[Box::new(StringBox::new(toml_text))],
|
||||||
.invoke_instance_method(
|
);
|
||||||
|
let name = inv_some(
|
||||||
|
h,
|
||||||
&bt1,
|
&bt1,
|
||||||
"get",
|
"get",
|
||||||
id1,
|
id1,
|
||||||
&[Box::new(StringBox::new("package.name"))],
|
&[Box::new(StringBox::new("package.name"))],
|
||||||
)
|
);
|
||||||
.expect("get tlv")
|
let json = inv_some(h, &bt1, "toJson", id1, &[]);
|
||||||
.unwrap();
|
|
||||||
let json = h
|
|
||||||
.invoke_instance_method(&bt1, "toJson", id1, &[])
|
|
||||||
.expect("toJson tlv")
|
|
||||||
.unwrap();
|
|
||||||
(name.to_string_box().value, json.to_string_box().value)
|
(name.to_string_box().value, json.to_string_box().value)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("TOMLBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("TOMLBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
inv_void(
|
||||||
let _ = h
|
h,
|
||||||
.invoke_instance_method(&bt2, "parse", id2, &[Box::new(StringBox::new(toml_text))])
|
&bt2,
|
||||||
.expect("parse tb")
|
"parse",
|
||||||
.unwrap();
|
id2,
|
||||||
let name = h
|
&[Box::new(StringBox::new(toml_text))],
|
||||||
.invoke_instance_method(
|
);
|
||||||
|
let name = inv_some(
|
||||||
|
h,
|
||||||
&bt2,
|
&bt2,
|
||||||
"get",
|
"get",
|
||||||
id2,
|
id2,
|
||||||
&[Box::new(StringBox::new("package.name"))],
|
&[Box::new(StringBox::new("package.name"))],
|
||||||
)
|
);
|
||||||
.expect("get tb")
|
let json = inv_some(h, &bt2, "toJson", id2, &[]);
|
||||||
.unwrap();
|
|
||||||
let json = h
|
|
||||||
.invoke_instance_method(&bt2, "toJson", id2, &[])
|
|
||||||
.expect("toJson tb")
|
|
||||||
.unwrap();
|
|
||||||
(name.to_string_box().value, json.to_string_box().value)
|
(name.to_string_box().value, json.to_string_box().value)
|
||||||
};
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_tlv, out_tb,
|
out_tlv, out_tb,
|
||||||
"TLV vs TypeBox results should match (TOMLBox)"
|
"TLV vs TypeBox results should match (TOMLBox)"
|
||||||
@ -620,28 +494,20 @@ mod tests {
|
|||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
|
||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("TimeBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("TimeBox");
|
||||||
let t_tlv = {
|
let t_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let v = inv_some(h, &bt1, "now", id1, &[]);
|
||||||
let v = h
|
|
||||||
.invoke_instance_method(&bt1, "now", id1, &[])
|
|
||||||
.expect("now tlv")
|
|
||||||
.unwrap();
|
|
||||||
v.to_string_box().value.parse::<i64>().unwrap_or(0)
|
v.to_string_box().value.parse::<i64>().unwrap_or(0)
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("TimeBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("TimeBox");
|
||||||
let t_tb = {
|
let t_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let v = inv_some(h, &bt2, "now", id2, &[]);
|
||||||
let v = h
|
|
||||||
.invoke_instance_method(&bt2, "now", id2, &[])
|
|
||||||
.expect("now tb")
|
|
||||||
.unwrap();
|
|
||||||
v.to_string_box().value.parse::<i64>().unwrap_or(0)
|
v.to_string_box().value.parse::<i64>().unwrap_or(0)
|
||||||
};
|
});
|
||||||
|
|
||||||
let diff = (t_tb - t_tlv).abs();
|
let diff = (t_tb - t_tlv).abs();
|
||||||
assert!(diff < 5_000, "TimeBox.now difference too large: {}ms", diff);
|
assert!(diff < 5_000, "TimeBox.now difference too large: {}ms", diff);
|
||||||
@ -654,49 +520,31 @@ mod tests {
|
|||||||
let host = crate::runtime::get_global_plugin_host();
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
|
||||||
// TLV path: verify get->inc->get increases by 1
|
// TLV path: verify get->inc->get increases by 1
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("CounterBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("CounterBox");
|
||||||
let (a1, b1) = {
|
let (a1, b1) = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let a = inv_some(h, &bt1, "get", id1, &[]);
|
||||||
let a = h
|
inv_void(h, &bt1, "inc", id1, &[]);
|
||||||
.invoke_instance_method(&bt1, "get", id1, &[])
|
let b = inv_some(h, &bt1, "get", id1, &[]);
|
||||||
.expect("get tlv")
|
|
||||||
.unwrap();
|
|
||||||
let _ = h
|
|
||||||
.invoke_instance_method(&bt1, "inc", id1, &[])
|
|
||||||
.expect("inc tlv");
|
|
||||||
let b = h
|
|
||||||
.invoke_instance_method(&bt1, "get", id1, &[])
|
|
||||||
.expect("get2 tlv")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
a.to_string_box().value.parse::<i64>().unwrap_or(0),
|
a.to_string_box().value.parse::<i64>().unwrap_or(0),
|
||||||
b.to_string_box().value.parse::<i64>().unwrap_or(0),
|
b.to_string_box().value.parse::<i64>().unwrap_or(0),
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
assert_eq!(b1 - a1, 1, "CounterBox TLV should increment by 1");
|
assert_eq!(b1 - a1, 1, "CounterBox TLV should increment by 1");
|
||||||
|
|
||||||
// TypeBox path: verify same delta behavior (not comparing absolute values due to singleton)
|
// TypeBox path: verify same delta behavior (not comparing absolute values due to singleton)
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("CounterBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("CounterBox");
|
||||||
let (a2, b2) = {
|
let (a2, b2) = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
let a = inv_some(h, &bt2, "get", id2, &[]);
|
||||||
let a = h
|
inv_void(h, &bt2, "inc", id2, &[]);
|
||||||
.invoke_instance_method(&bt2, "get", id2, &[])
|
let b = inv_some(h, &bt2, "get", id2, &[]);
|
||||||
.expect("get tb")
|
|
||||||
.unwrap();
|
|
||||||
let _ = h
|
|
||||||
.invoke_instance_method(&bt2, "inc", id2, &[])
|
|
||||||
.expect("inc tb");
|
|
||||||
let b = h
|
|
||||||
.invoke_instance_method(&bt2, "get", id2, &[])
|
|
||||||
.expect("get2 tb")
|
|
||||||
.unwrap();
|
|
||||||
(
|
(
|
||||||
a.to_string_box().value.parse::<i64>().unwrap_or(0),
|
a.to_string_box().value.parse::<i64>().unwrap_or(0),
|
||||||
b.to_string_box().value.parse::<i64>().unwrap_or(0),
|
b.to_string_box().value.parse::<i64>().unwrap_or(0),
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
assert_eq!(b2 - a2, 1, "CounterBox TypeBox should increment by 1");
|
assert_eq!(b2 - a2, 1, "CounterBox TypeBox should increment by 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,8 +566,7 @@ mod tests {
|
|||||||
// TLV path
|
// TLV path
|
||||||
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
env::set_var("NYASH_DISABLE_TYPEBOX", "1");
|
||||||
let (bt1, id1, _hold1) = create_plugin_instance("FileBox");
|
let (bt1, id1, _hold1) = create_plugin_instance("FileBox");
|
||||||
let out_tlv = {
|
let out_tlv = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
|
||||||
let _ = h
|
let _ = h
|
||||||
.invoke_instance_method(
|
.invoke_instance_method(
|
||||||
&bt1,
|
&bt1,
|
||||||
@ -749,21 +596,17 @@ mod tests {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
.expect("open2 tlv");
|
.expect("open2 tlv");
|
||||||
let rd = h
|
let rd = inv_some(h, &bt1, "read", id1, &[]);
|
||||||
.invoke_instance_method(&bt1, "read", id1, &[])
|
|
||||||
.expect("read tlv")
|
|
||||||
.unwrap();
|
|
||||||
let _ = h
|
let _ = h
|
||||||
.invoke_instance_method(&bt1, "close", id1, &[])
|
.invoke_instance_method(&bt1, "close", id1, &[])
|
||||||
.expect("close2 tlv");
|
.expect("close2 tlv");
|
||||||
rd.to_string_box().value
|
rd.to_string_box().value
|
||||||
};
|
});
|
||||||
|
|
||||||
// TypeBox path
|
// TypeBox path
|
||||||
env::remove_var("NYASH_DISABLE_TYPEBOX");
|
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||||
let (bt2, id2, _hold2) = create_plugin_instance("FileBox");
|
let (bt2, id2, _hold2) = create_plugin_instance("FileBox");
|
||||||
let out_tb = {
|
let out_tb = with_host(|h| {
|
||||||
let h = host.read().unwrap();
|
|
||||||
let _ = h
|
let _ = h
|
||||||
.invoke_instance_method(
|
.invoke_instance_method(
|
||||||
&bt2,
|
&bt2,
|
||||||
@ -792,15 +635,12 @@ mod tests {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
.expect("open2 tb");
|
.expect("open2 tb");
|
||||||
let rd = h
|
let rd = inv_some(h, &bt2, "read", id2, &[]);
|
||||||
.invoke_instance_method(&bt2, "read", id2, &[])
|
|
||||||
.expect("read tb")
|
|
||||||
.unwrap();
|
|
||||||
let _ = h
|
let _ = h
|
||||||
.invoke_instance_method(&bt2, "close", id2, &[])
|
.invoke_instance_method(&bt2, "close", id2, &[])
|
||||||
.expect("close2 tb");
|
.expect("close2 tb");
|
||||||
rd.to_string_box().value
|
rd.to_string_box().value
|
||||||
};
|
});
|
||||||
|
|
||||||
// Cleanup best-effort
|
// Cleanup best-effort
|
||||||
let _ = fs::remove_file(&path_str);
|
let _ = fs::remove_file(&path_str);
|
||||||
@ -813,7 +653,9 @@ mod tests {
|
|||||||
|
|
||||||
fn rand_id() -> u64 {
|
fn rand_id() -> u64 {
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
let now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("SystemTime before UNIX_EPOCH");
|
||||||
now.as_micros() as u64
|
now.as_micros() as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
src/tokenizer.rs
111
src/tokenizer.rs
@ -359,86 +359,11 @@ impl NyashTokenizer {
|
|||||||
self.advance();
|
self.advance();
|
||||||
return Ok(Token::new(TokenType::PipeForward, start_line, start_column));
|
return Ok(Token::new(TokenType::PipeForward, start_line, start_column));
|
||||||
}
|
}
|
||||||
Some('<') => {
|
// 単文字トークンはテーブル駆動で処理
|
||||||
|
Some(c) if self.single_char_token(c).is_some() => {
|
||||||
|
let tt = self.single_char_token(c).unwrap();
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::LESS, start_line, start_column))
|
Ok(Token::new(tt, start_line, start_column))
|
||||||
}
|
|
||||||
Some('>') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::GREATER, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('&') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::BitAnd, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('|') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::BitOr, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('^') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::BitXor, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('=') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::ASSIGN, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('+') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::PLUS, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('-') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::MINUS, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('*') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::MULTIPLY, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('/') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::DIVIDE, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('%') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::MODULO, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('.') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::DOT, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('(') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::LPAREN, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some(')') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::RPAREN, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('[') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::LBRACK, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some(']') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::RBRACK, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('{') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::LBRACE, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some('}') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::RBRACE, start_line, start_column))
|
|
||||||
}
|
|
||||||
Some(',') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::COMMA, start_line, start_column))
|
|
||||||
}
|
|
||||||
// '?' and ':' are handled earlier (including variants); avoid duplicate arms
|
|
||||||
Some('\n') => {
|
|
||||||
self.advance();
|
|
||||||
Ok(Token::new(TokenType::NEWLINE, start_line, start_column))
|
|
||||||
}
|
}
|
||||||
Some(c) => Err(TokenizeError::UnexpectedCharacter {
|
Some(c) => Err(TokenizeError::UnexpectedCharacter {
|
||||||
char: c,
|
char: c,
|
||||||
@ -449,6 +374,34 @@ impl NyashTokenizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 単文字トークンのマップ(最長一致系は呼び出し元で処理済み)
|
||||||
|
fn single_char_token(&self, c: char) -> Option<TokenType> {
|
||||||
|
// '?' は上位で分岐済み、':' も同様。ここでは純粋な1文字を扱う。
|
||||||
|
match c {
|
||||||
|
'<' => Some(TokenType::LESS),
|
||||||
|
'>' => Some(TokenType::GREATER),
|
||||||
|
'&' => Some(TokenType::BitAnd),
|
||||||
|
'|' => Some(TokenType::BitOr),
|
||||||
|
'^' => Some(TokenType::BitXor),
|
||||||
|
'=' => Some(TokenType::ASSIGN),
|
||||||
|
'+' => Some(TokenType::PLUS),
|
||||||
|
'-' => Some(TokenType::MINUS),
|
||||||
|
'*' => Some(TokenType::MULTIPLY),
|
||||||
|
'/' => Some(TokenType::DIVIDE),
|
||||||
|
'%' => Some(TokenType::MODULO),
|
||||||
|
'.' => Some(TokenType::DOT),
|
||||||
|
'(' => Some(TokenType::LPAREN),
|
||||||
|
')' => Some(TokenType::RPAREN),
|
||||||
|
'[' => Some(TokenType::LBRACK),
|
||||||
|
']' => Some(TokenType::RBRACK),
|
||||||
|
'{' => Some(TokenType::LBRACE),
|
||||||
|
'}' => Some(TokenType::RBRACE),
|
||||||
|
',' => Some(TokenType::COMMA),
|
||||||
|
'\n' => Some(TokenType::NEWLINE),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 文字列リテラルを読み取り
|
/// 文字列リテラルを読み取り
|
||||||
fn read_string(&mut self) -> Result<String, TokenizeError> {
|
fn read_string(&mut self) -> Result<String, TokenizeError> {
|
||||||
let start_line = self.line;
|
let start_line = self.line;
|
||||||
|
|||||||
@ -84,7 +84,18 @@ if [[ "${NYASH_LLVM_SKIP_EMIT:-0}" != "1" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [[ "${NYASH_LLVM_EMIT:-obj}" == "exe" ]]; then
|
||||||
|
echo " emitting EXE via ny-llvmc (crate) ..." >&2
|
||||||
|
# Ensure NyRT is built (for libnyrt.a)
|
||||||
|
if [[ ! -f crates/nyrt/target/release/libnyrt.a && "${NYASH_LLVM_SKIP_NYRT_BUILD:-0}" != "1" ]]; then
|
||||||
|
( cd crates/nyrt && cargo build --release -j 24 >/dev/null )
|
||||||
|
fi
|
||||||
|
NYRT_DIR_HINT="${NYASH_LLVM_NYRT:-crates/nyrt/target/release}"
|
||||||
|
./target/release/ny-llvmc --in "$NYASH_LLVM_MIR_JSON" --out "$OUT" --emit exe --nyrt "$NYRT_DIR_HINT" ${NYASH_LLVM_LIBS:+--libs "$NYASH_LLVM_LIBS"}
|
||||||
|
echo "✅ Done: $OUT"; echo " (runtime may require nyash.toml and plugins depending on app)"; exit 0
|
||||||
|
else
|
||||||
./target/release/ny-llvmc --in "$NYASH_LLVM_MIR_JSON" --out "$OBJ"
|
./target/release/ny-llvmc --in "$NYASH_LLVM_MIR_JSON" --out "$OBJ"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
if [[ "$COMPILER_MODE" == "harness" ]]; then
|
if [[ "$COMPILER_MODE" == "harness" ]]; then
|
||||||
|
|||||||
47
tools/crate_exe_smoke.sh
Normal file
47
tools/crate_exe_smoke.sh
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")"/.. && pwd)
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
BIN=./target/release/nyash
|
||||||
|
NYLL=./target/release/ny-llvmc
|
||||||
|
|
||||||
|
if [[ ! -x "$BIN" ]]; then
|
||||||
|
echo "[build] nyash ..." >&2
|
||||||
|
cargo build --release >/dev/null
|
||||||
|
fi
|
||||||
|
if [[ ! -x "$NYLL" ]]; then
|
||||||
|
echo "[build] ny-llvmc ..." >&2
|
||||||
|
cargo build --release -p nyash-llvm-compiler >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP="${1:-apps/tests/ternary_basic.nyash}"
|
||||||
|
OUT="${2:-tmp/crate_exe_smoke}"
|
||||||
|
|
||||||
|
mkdir -p tmp
|
||||||
|
JSON=tmp/crate_exe_smoke.json
|
||||||
|
|
||||||
|
echo "[1/3] Emitting MIR JSON via CLI ..." >&2
|
||||||
|
"$BIN" --emit-mir-json "$JSON" --backend mir "$APP" >/dev/null
|
||||||
|
|
||||||
|
echo "[2/3] Building EXE via ny-llvmc (crate) ..." >&2
|
||||||
|
NYASH_LLVM_NYRT_DIR=${NYASH_LLVM_NYRT:-target/release}
|
||||||
|
if [[ ! -f "$NYASH_LLVM_NYRT_DIR/libnyrt.a" ]]; then
|
||||||
|
( cd crates/nyrt && cargo build --release >/dev/null )
|
||||||
|
fi
|
||||||
|
"$NYLL" --in "$JSON" --out "$OUT" --emit exe --nyrt "$NYASH_LLVM_NYRT_DIR" ${NYASH_LLVM_LIBS:+--libs "$NYASH_LLVM_LIBS"}
|
||||||
|
|
||||||
|
echo "[3/3] Running EXE ..." >&2
|
||||||
|
set +e
|
||||||
|
"$OUT" >/dev/null 2>&1
|
||||||
|
CODE=$?
|
||||||
|
set -e
|
||||||
|
echo "exit=$CODE"
|
||||||
|
|
||||||
|
echo "✅ crate_exe_smoke OK: $OUT (exit=$CODE)"
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user