refactor: unify string helpers and pattern2 derived slot

This commit is contained in:
2025-12-28 13:22:02 +09:00
parent 84e1cd7c7b
commit 10e6a15552
41 changed files with 2044 additions and 585 deletions

View File

@ -169,6 +169,7 @@ pub enum CoreMethodId {
StringConcat,
StringSubstring,
StringIndexOf,
StringIndexOfFrom,
StringReplace,
StringTrim,
StringSplit,
@ -210,156 +211,446 @@ pub enum CoreMethodId {
ResultGetValue,
}
/// SSOT for CoreMethodId metadata (name/arity/return types and policy flags).
#[derive(Debug, Clone, Copy)]
struct CoreMethodSpec {
id: CoreMethodId,
box_id: CoreBoxId,
name: &'static str,
arity: usize,
return_type_name: &'static str,
is_pure: bool,
allowed_in_condition: bool,
allowed_in_init: bool,
vtable_slot: Option<u16>,
}
const CORE_METHOD_SPECS: &[CoreMethodSpec] = &[
// StringBox methods
CoreMethodSpec {
id: CoreMethodId::StringLength,
box_id: CoreBoxId::String,
name: "length",
arity: 0,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: true,
allowed_in_init: true,
vtable_slot: Some(300),
},
CoreMethodSpec {
id: CoreMethodId::StringUpper,
box_id: CoreBoxId::String,
name: "toUpper",
arity: 0,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::StringLower,
box_id: CoreBoxId::String,
name: "toLower",
arity: 0,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::StringConcat,
box_id: CoreBoxId::String,
name: "concat",
arity: 1,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(302),
},
CoreMethodSpec {
id: CoreMethodId::StringSubstring,
box_id: CoreBoxId::String,
name: "substring",
arity: 2,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(301),
},
CoreMethodSpec {
id: CoreMethodId::StringIndexOf,
box_id: CoreBoxId::String,
name: "indexOf",
arity: 1,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(303),
},
CoreMethodSpec {
id: CoreMethodId::StringIndexOfFrom,
box_id: CoreBoxId::String,
name: "indexOf",
arity: 2,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(303),
},
CoreMethodSpec {
id: CoreMethodId::StringReplace,
box_id: CoreBoxId::String,
name: "replace",
arity: 2,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(304),
},
CoreMethodSpec {
id: CoreMethodId::StringTrim,
box_id: CoreBoxId::String,
name: "trim",
arity: 0,
return_type_name: "StringBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(305),
},
CoreMethodSpec {
id: CoreMethodId::StringSplit,
box_id: CoreBoxId::String,
name: "split",
arity: 1,
return_type_name: "Unknown",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
// IntegerBox methods
CoreMethodSpec {
id: CoreMethodId::IntegerAbs,
box_id: CoreBoxId::Integer,
name: "abs",
arity: 0,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::IntegerMin,
box_id: CoreBoxId::Integer,
name: "min",
arity: 1,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::IntegerMax,
box_id: CoreBoxId::Integer,
name: "max",
arity: 1,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
// BoolBox methods
CoreMethodSpec {
id: CoreMethodId::BoolNot,
box_id: CoreBoxId::Bool,
name: "not",
arity: 0,
return_type_name: "BoolBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::BoolAnd,
box_id: CoreBoxId::Bool,
name: "and",
arity: 1,
return_type_name: "BoolBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::BoolOr,
box_id: CoreBoxId::Bool,
name: "or",
arity: 1,
return_type_name: "BoolBox",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
// ArrayBox methods
CoreMethodSpec {
id: CoreMethodId::ArrayLength,
box_id: CoreBoxId::Array,
name: "length",
arity: 0,
return_type_name: "IntegerBox",
is_pure: true,
allowed_in_condition: true,
allowed_in_init: true,
vtable_slot: Some(102),
},
CoreMethodSpec {
id: CoreMethodId::ArrayPush,
box_id: CoreBoxId::Array,
name: "push",
arity: 1,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(103),
},
CoreMethodSpec {
id: CoreMethodId::ArrayPop,
box_id: CoreBoxId::Array,
name: "pop",
arity: 0,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(104),
},
CoreMethodSpec {
id: CoreMethodId::ArrayGet,
box_id: CoreBoxId::Array,
name: "get",
arity: 1,
return_type_name: "Unknown",
is_pure: true,
allowed_in_condition: true,
allowed_in_init: true,
vtable_slot: Some(100),
},
// MapBox methods
CoreMethodSpec {
id: CoreMethodId::MapGet,
box_id: CoreBoxId::Map,
name: "get",
arity: 1,
return_type_name: "Unknown",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(203),
},
CoreMethodSpec {
id: CoreMethodId::MapSet,
box_id: CoreBoxId::Map,
name: "set",
arity: 2,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(204),
},
CoreMethodSpec {
id: CoreMethodId::MapHas,
box_id: CoreBoxId::Map,
name: "has",
arity: 1,
return_type_name: "BoolBox",
is_pure: true,
allowed_in_condition: true,
allowed_in_init: true,
vtable_slot: Some(202),
},
CoreMethodSpec {
id: CoreMethodId::MapKeys,
box_id: CoreBoxId::Map,
name: "keys",
arity: 0,
return_type_name: "Unknown",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: Some(206),
},
// ConsoleBox methods
CoreMethodSpec {
id: CoreMethodId::ConsolePrintln,
box_id: CoreBoxId::Console,
name: "println",
arity: 1,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(400),
},
CoreMethodSpec {
id: CoreMethodId::ConsoleLog,
box_id: CoreBoxId::Console,
name: "log",
arity: 1,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(400),
},
CoreMethodSpec {
id: CoreMethodId::ConsoleError,
box_id: CoreBoxId::Console,
name: "error",
arity: 1,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: Some(402),
},
// FileBox methods
CoreMethodSpec {
id: CoreMethodId::FileRead,
box_id: CoreBoxId::File,
name: "read",
arity: 1,
return_type_name: "StringBox",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::FileWrite,
box_id: CoreBoxId::File,
name: "write",
arity: 1,
return_type_name: "Void",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::FileOpen,
box_id: CoreBoxId::File,
name: "open",
arity: 1,
return_type_name: "FileBox",
is_pure: false,
allowed_in_condition: false,
allowed_in_init: false,
vtable_slot: None,
},
// ResultBox methods
CoreMethodSpec {
id: CoreMethodId::ResultIsOk,
box_id: CoreBoxId::Result,
name: "isOk",
arity: 0,
return_type_name: "BoolBox",
is_pure: true,
allowed_in_condition: true,
allowed_in_init: true,
vtable_slot: None,
},
CoreMethodSpec {
id: CoreMethodId::ResultGetValue,
box_id: CoreBoxId::Result,
name: "getValue",
arity: 0,
return_type_name: "Unknown",
is_pure: true,
allowed_in_condition: false,
allowed_in_init: true,
vtable_slot: None,
},
];
impl CoreMethodId {
fn spec(&self) -> &'static CoreMethodSpec {
CORE_METHOD_SPECS
.iter()
.find(|spec| spec.id == *self)
.expect("CoreMethodSpec missing for CoreMethodId")
}
/// メソッドが属する Box ID
pub fn box_id(&self) -> CoreBoxId {
use CoreMethodId::*;
match self {
StringLength | StringUpper | StringLower | StringConcat | StringSubstring
| StringIndexOf | StringReplace | StringTrim | StringSplit => CoreBoxId::String,
IntegerAbs | IntegerMin | IntegerMax => CoreBoxId::Integer,
BoolNot | BoolAnd | BoolOr => CoreBoxId::Bool,
ArrayLength | ArrayPush | ArrayPop | ArrayGet => CoreBoxId::Array,
MapGet | MapSet | MapHas | MapKeys => CoreBoxId::Map,
ConsolePrintln | ConsoleLog | ConsoleError => CoreBoxId::Console,
FileRead | FileWrite | FileOpen => CoreBoxId::File,
ResultIsOk | ResultGetValue => CoreBoxId::Result,
}
self.spec().box_id
}
/// メソッド名(例: "length"
pub fn name(&self) -> &'static str {
use CoreMethodId::*;
match self {
StringLength => "length",
StringUpper => "upper",
StringLower => "lower",
StringConcat => "concat",
StringSubstring => "substring",
StringIndexOf => "indexOf",
StringReplace => "replace",
StringTrim => "trim",
StringSplit => "split",
IntegerAbs => "abs",
IntegerMin => "min",
IntegerMax => "max",
BoolNot => "not",
BoolAnd => "and",
BoolOr => "or",
ArrayLength => "length",
ArrayPush => "push",
ArrayPop => "pop",
ArrayGet => "get",
MapGet => "get",
MapSet => "set",
MapHas => "has",
MapKeys => "keys",
ConsolePrintln => "println",
ConsoleLog => "log",
ConsoleError => "error",
FileRead => "read",
FileWrite => "write",
FileOpen => "open",
ResultIsOk => "isOk",
ResultGetValue => "getValue",
}
self.spec().name
}
/// 引数の数
pub fn arity(&self) -> usize {
use CoreMethodId::*;
match self {
StringLength | StringUpper | StringLower | StringTrim | IntegerAbs | BoolNot
| ArrayLength | ArrayPop | MapKeys | ResultIsOk | ResultGetValue => 0,
StringConcat | StringIndexOf | StringReplace | StringSplit | IntegerMin
| IntegerMax | BoolAnd | BoolOr | ArrayGet | ArrayPush | MapGet | MapHas
| ConsolePrintln | ConsoleLog | ConsoleError | FileRead | FileWrite | FileOpen => 1,
StringSubstring | MapSet => 2,
}
self.spec().arity
}
/// Phase 84-4-B: 戻り値型(型推論用)
pub fn return_type_name(&self) -> &'static str {
use CoreMethodId::*;
match self {
StringLength | StringIndexOf | ArrayLength => "IntegerBox",
self.spec().return_type_name
}
StringUpper | StringLower | StringConcat | StringSubstring | StringReplace
| StringTrim => "StringBox",
IntegerAbs | IntegerMin | IntegerMax => "IntegerBox",
BoolNot | BoolAnd | BoolOr | MapHas | ResultIsOk => "BoolBox",
ArrayPush | ArrayPop | MapSet | ConsolePrintln | ConsoleLog | ConsoleError
| FileWrite => "Void",
ArrayGet | MapGet | MapKeys | StringSplit => "Unknown",
FileRead => "StringBox",
FileOpen => "FileBox",
ResultGetValue => "Unknown",
}
/// VTable slot for TypeRegistry (None when not exposed via vtable).
pub fn vtable_slot(&self) -> Option<u16> {
self.spec().vtable_slot
}
/// 全CoreMethodIdを反復
pub fn iter() -> impl Iterator<Item = CoreMethodId> {
use CoreMethodId::*;
[
StringLength,
StringUpper,
StringLower,
StringConcat,
StringSubstring,
StringIndexOf,
StringReplace,
StringTrim,
StringSplit,
IntegerAbs,
IntegerMin,
IntegerMax,
BoolNot,
BoolAnd,
BoolOr,
ArrayLength,
ArrayPush,
ArrayPop,
ArrayGet,
MapGet,
MapSet,
MapHas,
MapKeys,
ConsolePrintln,
ConsoleLog,
ConsoleError,
FileRead,
FileWrite,
FileOpen,
ResultIsOk,
ResultGetValue,
]
.into_iter()
CORE_METHOD_SPECS.iter().map(|spec| spec.id)
}
/// Box名とメソッド名から CoreMethodId を取得
pub fn from_box_and_method(box_id: CoreBoxId, method: &str) -> Option<CoreMethodId> {
Self::iter().find(|m| m.box_id() == box_id && m.name() == method)
let canonical = crate::runtime::core_method_aliases::canonical_method_name(method);
CORE_METHOD_SPECS
.iter()
.find(|spec| spec.box_id == box_id && spec.name == canonical)
.map(|spec| spec.id)
}
/// メソッド名とアリティから CoreMethodId を解決
pub fn resolve_by_name_and_arity(
method_name: &str,
arg_len: usize,
) -> Result<CoreMethodId, Vec<usize>> {
let canonical = crate::runtime::core_method_aliases::canonical_method_name(method_name);
let mut expected = Vec::new();
for spec in CORE_METHOD_SPECS.iter().filter(|spec| spec.name == canonical) {
expected.push(spec.arity);
if spec.arity == arg_len {
return Ok(spec.id);
}
}
expected.sort_unstable();
expected.dedup();
Err(expected)
}
/// Phase 224-B: Pure function (no side effects, deterministic)
@ -373,28 +664,7 @@ impl CoreMethodId {
/// - `StringLength`: Pure - always returns same length for same string
/// - `ArrayPush`: Not pure - mutates the array (side effect)
pub fn is_pure(&self) -> bool {
use CoreMethodId::*;
match self {
// String methods (pure - return new values, don't mutate)
StringLength | StringUpper | StringLower | StringConcat | StringSubstring
| StringIndexOf | StringReplace | StringTrim | StringSplit => true,
// Integer/Bool methods (pure - mathematical operations)
IntegerAbs | IntegerMin | IntegerMax | BoolNot | BoolAnd | BoolOr => true,
// Array/Map read operations (pure - don't mutate)
ArrayLength | ArrayGet => true,
MapGet | MapHas => true,
MapKeys => true,
// ResultBox read operations (pure)
ResultIsOk | ResultGetValue => true,
// Impure - mutate state or have side effects
ArrayPush | ArrayPop | MapSet => false,
ConsolePrintln | ConsoleLog | ConsoleError => false,
FileRead | FileWrite | FileOpen => false,
}
self.spec().is_pure
}
/// Phase 224-B: Allowed in loop condition expressions
@ -406,36 +676,7 @@ impl CoreMethodId {
///
/// This is a whitelist approach - default to false for safety.
pub fn allowed_in_condition(&self) -> bool {
use CoreMethodId::*;
match self {
// String read operations - allowed
StringLength => true,
// Array read operations - allowed
ArrayLength | ArrayGet => true,
// Map read operations - allowed
MapHas => true,
// ResultBox operations - allowed
ResultIsOk => true,
// Not yet whitelisted - be conservative
StringUpper | StringLower | StringConcat | StringSubstring | StringIndexOf
| StringReplace | StringTrim | StringSplit => false,
IntegerAbs | IntegerMin | IntegerMax => false,
BoolNot | BoolAnd | BoolOr => false,
MapGet => false,
MapKeys => false,
ResultGetValue => false,
// Obviously disallowed - side effects
ArrayPush | ArrayPop | MapSet => false,
ConsolePrintln | ConsoleLog | ConsoleError => false,
FileRead | FileWrite | FileOpen => false,
}
self.spec().allowed_in_condition
}
/// Phase 224-B: Allowed in loop body init expressions
@ -443,43 +684,14 @@ impl CoreMethodId {
/// Methods allowed for LoopBodyLocal initialization.
/// Similar to condition requirements but slightly more permissive.
pub fn allowed_in_init(&self) -> bool {
use CoreMethodId::*;
match self {
// String operations - allowed
StringLength | StringSubstring | StringIndexOf => true,
// String transformations - allowed for init
StringUpper | StringLower | StringTrim => true,
// Array operations - allowed
ArrayLength | ArrayGet => true,
// Map operations - allowed
MapGet | MapHas | MapKeys => true,
// ResultBox operations - allowed
ResultIsOk | ResultGetValue => true,
// String operations that create new strings - allowed
StringConcat | StringReplace | StringSplit => true,
// Math operations - allowed
IntegerAbs | IntegerMin | IntegerMax => true,
// Not allowed - side effects
ArrayPush | ArrayPop | MapSet => false,
ConsolePrintln | ConsoleLog | ConsoleError => false,
FileRead | FileWrite | FileOpen => false,
// Bool operations - technically pure but unusual in init
BoolNot | BoolAnd | BoolOr => false,
}
self.spec().allowed_in_init
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
// ===== CoreBoxId tests =====
@ -573,6 +785,7 @@ mod tests {
fn test_core_method_id_arity() {
assert_eq!(CoreMethodId::StringLength.arity(), 0);
assert_eq!(CoreMethodId::StringConcat.arity(), 1);
assert_eq!(CoreMethodId::StringIndexOfFrom.arity(), 2);
assert_eq!(CoreMethodId::MapSet.arity(), 2);
}
@ -606,6 +819,22 @@ mod tests {
assert!(count >= 27); // Phase 87: 27個以上のメソッド
}
#[test]
fn test_core_method_spec_uniqueness() {
let mut ids = HashSet::new();
let mut signatures = HashSet::new();
for spec in CORE_METHOD_SPECS {
assert!(ids.insert(spec.id), "duplicate CoreMethodId in specs");
let key = (spec.box_id, spec.name, spec.arity);
assert!(
signatures.insert(key),
"duplicate CoreMethodSpec signature: {:?}",
key
);
}
assert_eq!(ids.len(), CoreMethodId::iter().count());
}
// ===== Phase 224-B tests =====
#[test]

View File

@ -0,0 +1,43 @@
//! Core method alias table (SSOT).
#[derive(Debug, Clone, Copy)]
pub struct CoreMethodAlias {
pub alias: &'static str,
pub canonical: &'static str,
}
const CORE_METHOD_ALIASES: &[CoreMethodAlias] = &[
CoreMethodAlias {
alias: "toUpperCase",
canonical: "toUpper",
},
CoreMethodAlias {
alias: "toLowerCase",
canonical: "toLower",
},
CoreMethodAlias {
alias: "find",
canonical: "indexOf",
},
];
pub fn canonical_method_name(method_name: &str) -> &str {
CORE_METHOD_ALIASES
.iter()
.find(|alias| alias.alias == method_name)
.map(|alias| alias.canonical)
.unwrap_or(method_name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canonical_method_name_aliases() {
assert_eq!(canonical_method_name("toUpperCase"), "toUpper");
assert_eq!(canonical_method_name("toLowerCase"), "toLower");
assert_eq!(canonical_method_name("find"), "indexOf");
assert_eq!(canonical_method_name("indexOf"), "indexOf");
}
}

View File

@ -4,6 +4,7 @@
pub mod box_registry;
pub mod core_box_ids; // Phase 87: CoreBoxId/CoreMethodId 型安全enum
pub mod core_method_aliases; // Phase 29ab: Core method alias SSOT
pub mod core_services; // Phase 91: CoreServices trait 定義
pub mod deprecations;
pub mod gc;

View File

@ -23,16 +23,46 @@
* - This enables unified dispatch for both VMValue::String and VMValue::BoxRef(StringBox)
*/
use super::core_box_ids::{CoreBoxId, CoreMethodId};
use super::type_box_abi::{MethodEntry, TypeBox};
use std::collections::HashSet;
use std::sync::OnceLock;
// 最小サンプル: MapBox の TypeBox を事前登録Tier-1 PoC 用)
// --- ArrayBox ---
const ARRAY_METHODS: &[MethodEntry] = &[
MethodEntry {
name: "get",
arity: 1,
slot: 100,
},
fn core_method_entries_for_box(box_id: CoreBoxId) -> Vec<MethodEntry> {
CoreMethodId::iter()
.filter(|method_id| method_id.box_id() == box_id)
.filter_map(|method_id| {
method_id.vtable_slot().map(|slot| MethodEntry {
name: method_id.name(),
arity: method_id.arity() as u8,
slot,
})
})
.collect()
}
fn core_method_entries_for_box_signatures(
box_id: CoreBoxId,
allowed: &[(&'static str, usize)],
) -> Vec<MethodEntry> {
let core = core_method_entries_for_box(box_id);
core.into_iter()
.filter(|entry| {
allowed
.iter()
.any(|(name, arity)| entry.name == *name && entry.arity as usize == *arity)
})
.collect()
}
fn merge_method_entries(mut entries: Vec<MethodEntry>, extras: &[MethodEntry]) -> &'static [MethodEntry] {
entries.extend_from_slice(extras);
let mut seen = HashSet::new();
entries.retain(|method| seen.insert((method.name, method.arity)));
Box::leak(entries.into_boxed_slice())
}
const ARRAY_METHOD_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "set",
arity: 2,
@ -43,22 +73,6 @@ const ARRAY_METHODS: &[MethodEntry] = &[
arity: 0,
slot: 102,
},
MethodEntry {
name: "length",
arity: 0,
slot: 102,
},
// P0: vtable coverage extension
MethodEntry {
name: "push",
arity: 1,
slot: 103,
},
MethodEntry {
name: "pop",
arity: 0,
slot: 104,
},
MethodEntry {
name: "clear",
arity: 0,
@ -97,10 +111,8 @@ const ARRAY_METHODS: &[MethodEntry] = &[
slot: 111,
},
];
static ARRAYBOX_TB: TypeBox = TypeBox::new_with("ArrayBox", ARRAY_METHODS);
// --- MapBox ---
const MAP_METHODS: &[MethodEntry] = &[
const MAP_METHOD_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "size",
arity: 0,
@ -111,21 +123,6 @@ const MAP_METHODS: &[MethodEntry] = &[
arity: 0,
slot: 201,
},
MethodEntry {
name: "has",
arity: 1,
slot: 202,
},
MethodEntry {
name: "get",
arity: 1,
slot: 203,
},
MethodEntry {
name: "set",
arity: 2,
slot: 204,
},
// Extended
MethodEntry {
name: "delete",
@ -137,11 +134,6 @@ const MAP_METHODS: &[MethodEntry] = &[
arity: 1,
slot: 205,
},
MethodEntry {
name: "keys",
arity: 0,
slot: 206,
},
MethodEntry {
name: "values",
arity: 0,
@ -153,41 +145,13 @@ const MAP_METHODS: &[MethodEntry] = &[
slot: 208,
},
];
static MAPBOX_TB: TypeBox = TypeBox::new_with("MapBox", MAP_METHODS);
// --- StringBox ---
const STRING_METHODS: &[MethodEntry] = &[
const STRING_METHOD_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "len",
arity: 0,
slot: 300,
},
// P1: extend String vtable
MethodEntry {
name: "substring",
arity: 2,
slot: 301,
},
MethodEntry {
name: "concat",
arity: 1,
slot: 302,
},
MethodEntry {
name: "indexOf",
arity: 1,
slot: 303,
},
MethodEntry {
name: "replace",
arity: 2,
slot: 304,
},
MethodEntry {
name: "trim",
arity: 0,
slot: 305,
},
MethodEntry {
name: "toUpper",
arity: 0,
@ -199,40 +163,58 @@ const STRING_METHODS: &[MethodEntry] = &[
slot: 307,
},
];
static STRINGBOX_TB: TypeBox = TypeBox::new_with("StringBox", STRING_METHODS);
// --- ConsoleBox --- (WASM v2 unified dispatch 用の雛形)
// 400: log(..), 401: warn(..), 402: error(..), 403: clear()
const CONSOLE_METHODS: &[MethodEntry] = &[
MethodEntry {
name: "log",
arity: 1,
slot: 400,
},
const CONSOLE_METHOD_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "warn",
arity: 1,
slot: 401,
},
MethodEntry {
name: "error",
arity: 1,
slot: 402,
},
MethodEntry {
name: "clear",
arity: 0,
slot: 403,
},
// Phase 122: println は log のエイリアス
// JSON v0/selfhost が println を吐いても log と同じスロットを使うための alias
MethodEntry {
name: "println",
arity: 1,
slot: 400,
},
];
static CONSOLEBOX_TB: TypeBox = TypeBox::new_with("ConsoleBox", CONSOLE_METHODS);
static ARRAYBOX_TB: OnceLock<TypeBox> = OnceLock::new();
static MAPBOX_TB: OnceLock<TypeBox> = OnceLock::new();
static STRINGBOX_TB: OnceLock<TypeBox> = OnceLock::new();
static CONSOLEBOX_TB: OnceLock<TypeBox> = OnceLock::new();
fn arraybox_typebox() -> &'static TypeBox {
ARRAYBOX_TB.get_or_init(|| {
let core = core_method_entries_for_box(CoreBoxId::Array);
let methods = merge_method_entries(core, ARRAY_METHOD_EXTRAS);
TypeBox::new_with("ArrayBox", methods)
})
}
fn mapbox_typebox() -> &'static TypeBox {
MAPBOX_TB.get_or_init(|| {
let core = core_method_entries_for_box(CoreBoxId::Map);
let methods = merge_method_entries(core, MAP_METHOD_EXTRAS);
TypeBox::new_with("MapBox", methods)
})
}
fn stringbox_typebox() -> &'static TypeBox {
STRINGBOX_TB.get_or_init(|| {
let core = core_method_entries_for_box(CoreBoxId::String);
let methods = merge_method_entries(core, STRING_METHOD_EXTRAS);
TypeBox::new_with("StringBox", methods)
})
}
fn consolebox_typebox() -> &'static TypeBox {
CONSOLEBOX_TB.get_or_init(|| {
let core = core_method_entries_for_box(CoreBoxId::Console);
let methods = merge_method_entries(core, CONSOLE_METHOD_EXTRAS);
TypeBox::new_with("ConsoleBox", methods)
})
}
// --- InstanceBox ---
// Representative methods exposed via unified slots for field access and diagnostics.
@ -268,48 +250,27 @@ static INSTANCEBOX_TB: TypeBox = TypeBox::new_with("InstanceBox", INSTANCE_METHO
// Primitive types (String, Integer, Array) share the same slot numbers as their Box variants
// This enables unified dispatch for both primitives and boxes
// Primitive String uses same slots as StringBox (300+)
const PRIMITIVE_STRING_METHODS: &[MethodEntry] = &[
MethodEntry {
name: "length",
arity: 0,
slot: 300,
},
MethodEntry {
name: "substring",
arity: 2,
slot: 301,
},
MethodEntry {
name: "concat",
arity: 1,
slot: 302,
},
MethodEntry {
name: "indexOf",
arity: 1,
slot: 303,
},
MethodEntry {
name: "replace",
arity: 2,
slot: 304,
},
const PRIMITIVE_STRING_ALLOWED_SIGNATURES: &[(&str, usize)] = &[
("length", 0),
("substring", 2),
("concat", 1),
("indexOf", 1),
("replace", 2),
];
const PRIMITIVE_STRING_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "lastIndexOf",
arity: 1,
slot: 308,
},
];
static PRIMITIVE_STRING_TB: TypeBox = TypeBox::new_with("String", PRIMITIVE_STRING_METHODS);
// Primitive Array uses same slots as ArrayBox (100+)
const PRIMITIVE_ARRAY_METHODS: &[MethodEntry] = &[
MethodEntry {
name: "get",
arity: 1,
slot: 100,
},
const PRIMITIVE_ARRAY_ALLOWED_SIGNATURES: &[(&str, usize)] = &[
("get", 1),
("length", 0),
("push", 1),
];
const PRIMITIVE_ARRAY_EXTRAS: &[MethodEntry] = &[
MethodEntry {
name: "set",
arity: 2,
@ -320,30 +281,44 @@ const PRIMITIVE_ARRAY_METHODS: &[MethodEntry] = &[
arity: 0,
slot: 102,
},
MethodEntry {
name: "length",
arity: 0,
slot: 102,
},
MethodEntry {
name: "push",
arity: 1,
slot: 103,
},
];
static PRIMITIVE_ARRAY_TB: TypeBox = TypeBox::new_with("Array", PRIMITIVE_ARRAY_METHODS);
static PRIMITIVE_STRING_TB: OnceLock<TypeBox> = OnceLock::new();
static PRIMITIVE_ARRAY_TB: OnceLock<TypeBox> = OnceLock::new();
fn primitive_string_typebox() -> &'static TypeBox {
PRIMITIVE_STRING_TB.get_or_init(|| {
let core = core_method_entries_for_box_signatures(
CoreBoxId::String,
PRIMITIVE_STRING_ALLOWED_SIGNATURES,
);
let methods = merge_method_entries(core, PRIMITIVE_STRING_EXTRAS);
TypeBox::new_with("String", methods)
})
}
fn primitive_array_typebox() -> &'static TypeBox {
PRIMITIVE_ARRAY_TB.get_or_init(|| {
let core = core_method_entries_for_box_signatures(
CoreBoxId::Array,
PRIMITIVE_ARRAY_ALLOWED_SIGNATURES,
);
let methods = merge_method_entries(core, PRIMITIVE_ARRAY_EXTRAS);
TypeBox::new_with("Array", methods)
})
}
/// 型名から TypeBox を解決(雛形)。現在は常に None。
pub fn resolve_typebox_by_name(type_name: &str) -> Option<&'static TypeBox> {
match type_name {
"MapBox" => Some(&MAPBOX_TB),
"ArrayBox" => Some(&ARRAYBOX_TB),
"StringBox" => Some(&STRINGBOX_TB),
"ConsoleBox" => Some(&CONSOLEBOX_TB),
"MapBox" => Some(mapbox_typebox()),
"ArrayBox" => Some(arraybox_typebox()),
"StringBox" => Some(stringbox_typebox()),
"ConsoleBox" => Some(consolebox_typebox()),
"InstanceBox" => Some(&INSTANCEBOX_TB),
// Phase 124: Primitive types
"String" => Some(&PRIMITIVE_STRING_TB),
"Array" => Some(&PRIMITIVE_ARRAY_TB),
"String" => Some(primitive_string_typebox()),
"Array" => Some(primitive_array_typebox()),
_ => None,
}
}
@ -368,3 +343,27 @@ pub fn known_methods_for(type_name: &str) -> Option<Vec<&'static str>> {
v.dedup();
Some(v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_core_method_vtable_slots_match_registry() {
for method_id in CoreMethodId::iter() {
let Some(expected_slot) = method_id.vtable_slot() else {
continue;
};
let type_name = method_id.box_id().name();
let resolved = resolve_slot_by_name(type_name, method_id.name(), method_id.arity());
assert_eq!(
resolved,
Some(expected_slot),
"vtable slot mismatch: {}.{}({})",
type_name,
method_id.name(),
method_id.arity()
);
}
}
}