selfhost: add ternary parser and plugin prefix guard
This commit is contained in:
@ -31,10 +31,14 @@ Quick Next (today)
|
||||
1) ParserBox 拡張(Stage‑2 の堅牢化・回帰修正)✅ Done 2025‑09‑16
|
||||
- bool/null リテラルと空 RHS(代入/return/local)を Int(0) フォールバックで正規化。
|
||||
- simple assignment → Local 正常化を `==` 判定と共に調整。
|
||||
- 三項演算子 `cond ? a : b` を `Ternary` ノードに正規化し、自走スモーク追加。
|
||||
2) EmitterBox 拡張(JSON v0 の安定化)✅ Done 2025‑09‑16
|
||||
- `meta.usings` を常時出力(空は `[]`)。
|
||||
3) 自己ホスト経路で Ny 実装切替のゲート準備(現状は Python MVP 優先を維持)。
|
||||
4) テスト:
|
||||
3) Resolver/BoxIndex の prefix メタ反映 ✅ Done 2025‑09‑16
|
||||
- `plugin_meta_by_box` を構築し、`require_prefix` / `expose_short_names` を `resolve_using_target` へ適用。
|
||||
- `NYASH_PLUGIN_REQUIRE_PREFIX` が無効でも per-plugin meta で短名禁止を検知。
|
||||
4) 自己ホスト経路で Ny 実装切替のゲート準備(現状は Python MVP 優先を維持)。
|
||||
5) テスト:
|
||||
- `source tools/dev_env.sh pyvm`
|
||||
- `NYASH_VM_USE_PY=1 ./tools/selfhost_stage2_smoke.sh`
|
||||
- `NYASH_VM_USE_PY=1 ./tools/selfhost_stage2_bridge_smoke.sh`
|
||||
|
||||
@ -402,7 +402,37 @@ box ParserBox {
|
||||
parse_term2(src, i) { local lhs = me.parse_unary2(src, i) local j = me.gpos_get() local cont = 1 loop(cont == 1) { j = me.skip_ws(src, j) if j >= src.length() { cont = 0 } else { local op = src.substring(j, j+1) if op != "*" && op != "/" { cont = 0 } else { local rhs = me.parse_unary2(src, j+1) j = me.gpos_get() lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } } } me.gpos_set(j) return lhs }
|
||||
parse_sum2(src, i) { local lhs = me.parse_term2(src, i) local j = me.gpos_get() local cont = 1 loop(cont == 1) { j = me.skip_ws(src, j) if j >= src.length() { cont = 0 } else { local op = src.substring(j, j+1) if op != "+" && op != "-" { cont = 0 } else { local rhs = me.parse_term2(src, j+1) j = me.gpos_get() lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } } } me.gpos_set(j) return lhs }
|
||||
parse_compare2(src, i) { local lhs = me.parse_sum2(src, i) local j = me.gpos_get() j = me.skip_ws(src, j) local two = src.substring(j, j+2) local one = src.substring(j, j+1) local op = "" if two == "==" || two == "!=" || two == "<=" || two == ">=" { op = two j = j + 2 } else { if one == "<" || one == ">" { op = one j = j + 1 } } if op == "" { me.gpos_set(j) return lhs } local rhs = me.parse_sum2(src, j) j = me.gpos_get() me.gpos_set(j) return "{\"type\":\"Compare\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" }
|
||||
parse_expr2(src, i) { local lhs = me.parse_compare2(src, i) local j = me.gpos_get() local cont = 1 loop(cont == 1) { j = me.skip_ws(src, j) local two = src.substring(j, j+2) if two != "&&" && two != "||" { cont = 0 } else { local rhs = me.parse_compare2(src, j+2) j = me.gpos_get() lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } } me.gpos_set(j) return lhs }
|
||||
parse_expr2(src, i) {
|
||||
local lhs = me.parse_compare2(src, i)
|
||||
local j = me.gpos_get()
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
j = me.skip_ws(src, j)
|
||||
local two = src.substring(j, j+2)
|
||||
if two != "&&" && two != "||" { cont = 0 } else {
|
||||
local rhs = me.parse_compare2(src, j+2)
|
||||
j = me.gpos_get()
|
||||
lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
|
||||
}
|
||||
}
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "?" {
|
||||
j = j + 1
|
||||
j = me.skip_ws(src, j)
|
||||
local then_expr = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == ":" { j = j + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
local else_expr = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if else_expr.length() == 0 { else_expr = "{\"type\":\"Int\",\"value\":0}" }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Ternary\",\"cond\":" + lhs + ",\"then\":" + then_expr + ",\"else\":" + else_expr + "}"
|
||||
}
|
||||
me.gpos_set(j)
|
||||
return lhs
|
||||
}
|
||||
parse_args2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
local n = src.length()
|
||||
|
||||
@ -15,6 +15,7 @@ pub struct BoxIndex {
|
||||
pub aliases: HashMap<String, String>,
|
||||
pub plugin_boxes: HashSet<String>,
|
||||
pub plugin_meta: HashMap<String, PluginMeta>,
|
||||
pub plugin_meta_by_box: HashMap<String, PluginMeta>,
|
||||
pub plugins_require_prefix_global: bool,
|
||||
}
|
||||
|
||||
@ -43,6 +44,7 @@ impl BoxIndex {
|
||||
// plugin box types (best-effort; may be empty if host not initialized yet)
|
||||
let mut plugin_boxes: HashSet<String> = HashSet::new();
|
||||
let mut plugin_meta: HashMap<String, PluginMeta> = HashMap::new();
|
||||
let mut plugin_meta_by_box: HashMap<String, PluginMeta> = HashMap::new();
|
||||
let mut plugins_require_prefix_global = false;
|
||||
|
||||
// Read per-plugin meta and global flags from nyash.toml when available
|
||||
@ -59,7 +61,13 @@ impl BoxIndex {
|
||||
let prefix = t.get("prefix").and_then(|x| x.as_str()).map(|s| s.to_string());
|
||||
let require_prefix = t.get("require_prefix").and_then(|x| x.as_bool()).unwrap_or(false);
|
||||
let expose_short_names = t.get("expose_short_names").and_then(|x| x.as_bool()).unwrap_or(true);
|
||||
plugin_meta.insert(k.clone(), PluginMeta { prefix, require_prefix, expose_short_names });
|
||||
let meta = PluginMeta { prefix, require_prefix, expose_short_names };
|
||||
plugin_meta.insert(k.clone(), meta.clone());
|
||||
if let Some(arr) = t.get("boxes").and_then(|x| x.as_array()) {
|
||||
for b in arr {
|
||||
if let Some(name) = b.as_str() { plugin_meta_by_box.insert(name.to_string(), meta.clone()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,13 +76,18 @@ impl BoxIndex {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
if let Ok(h) = host.read() {
|
||||
if let Some(cfg) = h.config_ref() {
|
||||
for (_lib, def) in &cfg.libraries {
|
||||
for bt in &def.boxes { plugin_boxes.insert(bt.clone()); }
|
||||
for (lib, def) in &cfg.libraries {
|
||||
for bt in &def.boxes {
|
||||
plugin_boxes.insert(bt.clone());
|
||||
if let Some(meta) = plugin_meta.get(lib) {
|
||||
plugin_meta_by_box.insert(bt.clone(), meta.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self { aliases, plugin_boxes, plugin_meta, plugins_require_prefix_global }
|
||||
Self { aliases, plugin_boxes, plugin_meta, plugin_meta_by_box, plugins_require_prefix_global }
|
||||
}
|
||||
|
||||
pub fn is_known_plugin_short(name: &str) -> bool {
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use super::box_index::BoxIndex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Using/module resolution context accumulated from config/env/nyash.toml
|
||||
@ -135,39 +134,24 @@ pub(super) fn resolve_using_target(
|
||||
) -> Result<String, String> {
|
||||
if is_path { return Ok(tgt.to_string()); }
|
||||
let trace = verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
// Strict plugin prefix: if enabled and target matches a known plugin box type
|
||||
// and is not qualified (contains '.'), require a qualified/prefixed name.
|
||||
// Strict mode: env or nyash.toml [plugins] require_prefix=true
|
||||
let mut strict_effective = strict;
|
||||
if !strict_effective {
|
||||
if let Ok(text) = std::fs::read_to_string("nyash.toml") {
|
||||
if let Ok(doc) = toml::from_str::<toml::Value>(&text) {
|
||||
if let Some(tbl) = doc.get("plugins").and_then(|v| v.as_table()) {
|
||||
if let Some(v) = tbl.get("require_prefix").and_then(|v| v.as_bool()) { if v { strict_effective = true; } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let idx = super::box_index::get_box_index();
|
||||
let mut strict_effective = strict || idx.plugins_require_prefix_global;
|
||||
if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") { strict_effective = true; }
|
||||
|
||||
if strict_effective {
|
||||
let mut is_plugin_short = super::box_index::BoxIndex::is_known_plugin_short(tgt);
|
||||
if !is_plugin_short {
|
||||
// Fallback: heuristic list or env override
|
||||
if let Ok(raw) = std::env::var("NYASH_KNOWN_PLUGIN_SHORTNAMES") {
|
||||
let set: std::collections::HashSet<String> = raw.split(',').map(|s| s.trim().to_string()).collect();
|
||||
is_plugin_short = set.contains(tgt);
|
||||
} else {
|
||||
// Minimal builtins set
|
||||
const KNOWN: &[&str] = &[
|
||||
"ArrayBox","MapBox","StringBox","ConsoleBox","FileBox","PathBox","MathBox","IntegerBox","TOMLBox"
|
||||
];
|
||||
is_plugin_short = KNOWN.iter().any(|k| *k == tgt);
|
||||
let meta_for_target = idx.plugin_meta_by_box.get(tgt).cloned();
|
||||
let mut require_prefix_target = meta_for_target.as_ref().map(|m| m.require_prefix).unwrap_or(false);
|
||||
if let Some(m) = &meta_for_target { if !m.expose_short_names { require_prefix_target = true; } }
|
||||
let mut is_plugin_short = meta_for_target.is_some();
|
||||
if !is_plugin_short {
|
||||
is_plugin_short = idx.plugin_boxes.contains(tgt) || super::box_index::BoxIndex::is_known_plugin_short(tgt);
|
||||
}
|
||||
if (strict_effective || require_prefix_target) && is_plugin_short && !tgt.contains('.') {
|
||||
let mut msg = format!("plugin short name '{}' requires prefix", tgt);
|
||||
if let Some(meta) = &meta_for_target {
|
||||
if let Some(pref) = &meta.prefix {
|
||||
msg.push_str(&format!(" (use '{}.{}')", pref, tgt));
|
||||
}
|
||||
}
|
||||
if is_plugin_short && !tgt.contains('.') {
|
||||
return Err(format!("plugin short name '{}' requires prefix (strict)", tgt));
|
||||
}
|
||||
return Err(msg);
|
||||
}
|
||||
let key = {
|
||||
let base = context_dir.and_then(|p| p.to_str()).unwrap_or("");
|
||||
@ -332,3 +316,48 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn plugin_meta_requires_prefix_even_when_relaxed() {
|
||||
let dir = tempdir().expect("tempdir");
|
||||
let old = std::env::current_dir().expect("cwd");
|
||||
std::env::set_current_dir(dir.path()).expect("chdir");
|
||||
let toml = r#"
|
||||
[plugins]
|
||||
require_prefix = false
|
||||
|
||||
[plugins."test-plugin"]
|
||||
prefix = "test"
|
||||
require_prefix = true
|
||||
expose_short_names = false
|
||||
boxes = ["ArrayBox"]
|
||||
"#;
|
||||
std::fs::write("nyash.toml", toml).expect("write nyash.toml");
|
||||
crate::runner::box_index::refresh_box_index();
|
||||
crate::runner::box_index::cache_clear();
|
||||
|
||||
let res = resolve_using_target(
|
||||
"ArrayBox",
|
||||
false,
|
||||
&[],
|
||||
&[],
|
||||
&HashMap::new(),
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
assert!(res.is_err(), "expected prefix enforcement");
|
||||
let err = res.err().unwrap();
|
||||
assert!(err.contains("requires prefix"));
|
||||
assert!(err.contains("test."));
|
||||
|
||||
std::env::set_current_dir(old).expect("restore cwd");
|
||||
crate::runner::box_index::refresh_box_index();
|
||||
crate::runner::box_index::cache_clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::types::{PluginBoxV2, PluginHandleInner, LoadedPluginV2};
|
||||
use super::types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, LoadedPluginV2};
|
||||
use crate::bid::{BidResult, BidError};
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::config::nyash_toml_v2::{NyashConfigV2, LibraryDefinition};
|
||||
@ -146,6 +146,40 @@ impl PluginLoaderV2 {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn metadata_for_type_id(&self, type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
let config = self.config.as_ref()?;
|
||||
let cfg_path = self.config_path.as_ref()?;
|
||||
let toml_str = std::fs::read_to_string(cfg_path).ok()?;
|
||||
let toml_value: toml::Value = toml::from_str(&toml_str).ok()?;
|
||||
let (lib_name, box_type) = self.find_box_by_type_id(config, &toml_value, type_id)?;
|
||||
let plugin = {
|
||||
let plugins = self.plugins.read().ok()?;
|
||||
plugins.get(lib_name)?.clone()
|
||||
};
|
||||
let spec_key = (lib_name.to_string(), box_type.to_string());
|
||||
let mut resolved_type = type_id;
|
||||
let mut fini_method = None;
|
||||
if let Some(spec) = self.box_specs.read().ok()?.get(&spec_key).cloned() {
|
||||
if let Some(tid) = spec.type_id { resolved_type = tid; }
|
||||
if let Some(fini) = spec.fini_method_id { fini_method = Some(fini); }
|
||||
}
|
||||
if resolved_type == type_id || fini_method.is_none() {
|
||||
if let Some(cfg) = config.get_box_config(lib_name, box_type, &toml_value) {
|
||||
if resolved_type == type_id { resolved_type = cfg.type_id; }
|
||||
if fini_method.is_none() {
|
||||
fini_method = cfg.methods.get("fini").map(|m| m.method_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(PluginBoxMetadata {
|
||||
lib_name: lib_name.to_string(),
|
||||
box_type: box_type.to_string(),
|
||||
type_id: resolved_type,
|
||||
invoke_fn: plugin.invoke_fn,
|
||||
fini_method_id: fini_method,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn construct_existing_instance(&self, type_id: u32, instance_id: u32) -> Option<Box<dyn NyashBox>> {
|
||||
let config = self.config.as_ref()?;
|
||||
let cfg_path = self.config_path.as_ref()?;
|
||||
|
||||
@ -4,8 +4,14 @@ mod globals;
|
||||
mod errors;
|
||||
mod host_bridge;
|
||||
|
||||
pub use types::{PluginBoxV2, PluginHandleInner, NyashTypeBoxFfi, make_plugin_box_v2, construct_plugin_box};
|
||||
pub use types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, NyashTypeBoxFfi, make_plugin_box_v2, construct_plugin_box};
|
||||
pub use loader::PluginLoaderV2;
|
||||
pub use globals::{get_global_loader_v2, init_global_loader_v2, shutdown_plugins_v2};
|
||||
|
||||
pub fn metadata_for_type_id(type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
let loader = get_global_loader_v2();
|
||||
let guard = loader.read().ok()?;
|
||||
guard.metadata_for_type_id(type_id)
|
||||
}
|
||||
|
||||
pub fn backend_kind() -> &'static str { "enabled" }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::box_trait::{NyashBox, BoxCore, StringBox};
|
||||
use crate::box_trait::{BoxCore, NyashBox, StringBox};
|
||||
use super::host_bridge::InvokeFn;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -10,14 +11,23 @@ pub struct LoadedPluginV2 {
|
||||
pub(super) box_types: Vec<String>,
|
||||
pub(super) typeboxes: std::collections::HashMap<String, usize>,
|
||||
pub(super) init_fn: Option<unsafe extern "C" fn() -> i32>,
|
||||
pub(super) invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
pub(super) invoke_fn: InvokeFn,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginBoxMetadata {
|
||||
pub lib_name: String,
|
||||
pub box_type: String,
|
||||
pub type_id: u32,
|
||||
pub invoke_fn: InvokeFn,
|
||||
pub fini_method_id: Option<u32>,
|
||||
}
|
||||
|
||||
/// v2 Plugin Box handle core
|
||||
#[derive(Debug)]
|
||||
pub struct PluginHandleInner {
|
||||
pub type_id: u32,
|
||||
pub invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
pub invoke_fn: InvokeFn,
|
||||
pub instance_id: u32,
|
||||
pub fini_method_id: Option<u32>,
|
||||
pub(super) finalized: std::sync::atomic::AtomicBool,
|
||||
@ -104,7 +114,7 @@ impl PluginBoxV2 {
|
||||
}
|
||||
|
||||
/// Helper to construct a PluginBoxV2 from raw ids and invoke pointer safely
|
||||
pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize) -> i32) -> PluginBoxV2 {
|
||||
pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 {
|
||||
PluginBoxV2 { box_type, inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None, finalized: std::sync::atomic::AtomicBool::new(false) }) }
|
||||
}
|
||||
|
||||
@ -112,7 +122,7 @@ pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invo
|
||||
pub fn construct_plugin_box(
|
||||
box_type: String,
|
||||
type_id: u32,
|
||||
invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
invoke_fn: InvokeFn,
|
||||
instance_id: u32,
|
||||
fini_method_id: Option<u32>,
|
||||
) -> PluginBoxV2 {
|
||||
|
||||
@ -3,15 +3,27 @@ use crate::box_trait::NyashBox;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub type InvokeFn = unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginBoxV2 {
|
||||
pub box_type: String,
|
||||
pub inner: std::sync::Arc<PluginHandleInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginBoxMetadata {
|
||||
pub lib_name: String,
|
||||
pub box_type: String,
|
||||
pub type_id: u32,
|
||||
pub invoke_fn: InvokeFn,
|
||||
pub fini_method_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginHandleInner {
|
||||
pub type_id: u32,
|
||||
pub invoke_fn: InvokeFn,
|
||||
pub instance_id: u32,
|
||||
pub fini_method_id: Option<u32>,
|
||||
}
|
||||
@ -25,6 +37,7 @@ impl PluginLoaderV2 {
|
||||
pub fn create_box(&self, _t: &str, _a: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> { Err(BidError::PluginError) }
|
||||
pub fn extern_call(&self, _iface_name: &str, _method_name: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> { Err(BidError::PluginError) }
|
||||
pub fn invoke_instance_method(&self, _box_type: &str, _method_name: &str, _instance_id: u32, _args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> { Err(BidError::PluginError) }
|
||||
pub fn metadata_for_type_id(&self, _type_id: u32) -> Option<PluginBoxMetadata> { None }
|
||||
pub fn shutdown_singletons(&self) {}
|
||||
}
|
||||
|
||||
@ -34,3 +47,25 @@ pub fn init_global_loader_v2(_config_path: &str) -> BidResult<()> { Ok(()) }
|
||||
pub fn shutdown_plugins_v2() -> BidResult<()> { Ok(()) }
|
||||
|
||||
pub fn backend_kind() -> &'static str { "stub" }
|
||||
|
||||
pub fn metadata_for_type_id(_type_id: u32) -> Option<PluginBoxMetadata> { None }
|
||||
|
||||
pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 {
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_plugin_box(
|
||||
box_type: String,
|
||||
type_id: u32,
|
||||
invoke_fn: InvokeFn,
|
||||
instance_id: u32,
|
||||
fini_method_id: Option<u32>,
|
||||
) -> PluginBoxV2 {
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id }),
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,5 +122,20 @@ OUT=$(NYASH_USE_NY_COMPILER=1 NYASH_NY_COMPILER_EMIT_ONLY=0 NYASH_VM_USE_PY=${NY
|
||||
set -e
|
||||
echo "$OUT" | rg -q '^Result:\s*3\b' && pass "String.length()" || fail "String.length()" "$OUT"
|
||||
|
||||
# J) ternary expression → 10
|
||||
cat > "$TMP/selfhost_ternary_basic.nyash" <<'NY'
|
||||
return (1 < 2) ? 10 : 20
|
||||
NY
|
||||
set +e
|
||||
NYASH_USE_NY_COMPILER=1 NYASH_NY_COMPILER_EMIT_ONLY=0 NYASH_VM_USE_PY=${NYASH_VM_USE_PY:-1} \
|
||||
"$BIN" --backend vm "$TMP/selfhost_ternary_basic.nyash" >/dev/null 2>&1
|
||||
CODE=$?
|
||||
set -e
|
||||
if [[ "$CODE" -eq 10 ]]; then
|
||||
pass "Ternary basic"
|
||||
else
|
||||
fail "Ternary basic" "exit=$CODE"
|
||||
fi
|
||||
|
||||
echo "All selfhost Stage-2 smokes PASS" >&2
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user