1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Rust crate integration - plugin system for Java extensions.
//!
//! Allows Rust crates to register native method implementations
//! without JNI. Integrates with the interop layer.
//!
//! # Example
//! ```
//! use jvmrs::extensions::{ExtensionRegistry, JavaExtension};
//! use jvmrs::memory::Value;
//!
//! struct MyExt;
//! impl JavaExtension for MyExt {
//! fn id(&self) -> &str { "com.example.myext" }
//! fn version(&self) -> &str { "0.1.0" }
//! fn on_load(&self, registry: &mut ExtensionRegistry) {
//! registry.register_native("com.example.Utils.add", Box::new(|args| {
//! let a = args.get(0).map(|v| v.as_int()).unwrap_or(0);
//! let b = args.get(1).map(|v| v.as_int()).unwrap_or(0);
//! Ok(Value::Int(a + b))
//! }));
//! }
//! }
//! ExtensionRegistry::global().write().unwrap().load(&MyExt);
//! let result = ExtensionRegistry::global().read().unwrap()
//! .invoke_native("com.example.Utils.add", &[Value::Int(2), Value::Int(3)]);
//! assert_eq!(result.unwrap(), Value::Int(5));
//! ```
use crate::memory::Value;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, OnceLock, RwLock};
/// Extension that can be loaded from a Rust crate
pub trait JavaExtension: Send + Sync {
/// Unique extension id (e.g. "com.example.myext")
fn id(&self) -> &str;
/// Version string for compatibility checks
fn version(&self) -> &str;
/// Register native methods, callbacks, etc.
fn on_load(&self, registry: &mut ExtensionRegistry);
/// Optional: extension-specific metadata
fn metadata(&self) -> Option<Box<dyn Any + Send>> {
None
}
}
type NativeCallback = Arc<dyn Fn(&[Value]) -> Result<Value, String> + Send + Sync>;
/// Registry for extensions to register their capabilities
pub struct ExtensionRegistry {
native_methods: RwLock<HashMap<String, NativeCallback>>,
extensions_loaded: RwLock<Vec<String>>,
}
impl ExtensionRegistry {
pub fn new() -> Self {
Self {
native_methods: RwLock::new(HashMap::new()),
extensions_loaded: RwLock::new(Vec::new()),
}
}
/// Register a native method implementation (key: "class.name.method")
pub fn register_native(
&self,
key: &str,
f: Box<dyn Fn(&[Value]) -> Result<Value, String> + Send + Sync>,
) {
if let Ok(mut methods) = self.native_methods.write() {
methods.insert(key.to_string(), Arc::from(f));
} else {
log::error!("Failed to acquire lock for registering native method: {}", key);
}
}
/// Check if a native is registered
pub fn has_native(&self, key: &str) -> bool {
self.native_methods
.read()
.map_or(false, |methods| methods.contains_key(key))
}
/// Invoke a registered native
pub fn invoke_native(&self, key: &str, args: &[Value]) -> Result<Value, String> {
let f = self
.native_methods
.read()
.map_err(|e| format!("Failed to read native methods: {}", e))?
.get(key)
.cloned()
.ok_or_else(|| format!("Extension native not found: {}", key))?;
f(args)
}
/// Load an extension
pub fn load(&self, ext: &dyn JavaExtension) {
let mut registry = ExtensionRegistry {
native_methods: RwLock::new(HashMap::new()),
extensions_loaded: RwLock::new(Vec::new()),
};
ext.on_load(&mut registry);
let natives: Vec<_> = registry
.native_methods
.write()
.expect("ExtensionRegistry native_methods lock poisoned")
.drain()
.collect();
// Merge into self and wire to interop (no JNI) when available
let mut native = self
.native_methods
.write()
.expect("ExtensionRegistry native_methods lock poisoned");
for (k, v) in natives {
#[cfg(feature = "interop")]
{
let v_clone = v.clone();
crate::interop::register_rust_callback(&k, Box::new(move |args| v_clone(args)));
}
native.insert(k, v);
}
self.extensions_loaded
.write()
.expect("ExtensionRegistry extensions_loaded lock poisoned")
.push(ext.id().to_string());
}
/// List loaded extension ids
pub fn loaded_extensions(&self) -> Vec<String> {
self.extensions_loaded
.read()
.expect("ExtensionRegistry extensions_loaded lock poisoned")
.clone()
}
}
impl Default for ExtensionRegistry {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_REGISTRY: OnceLock<RwLock<ExtensionRegistry>> = OnceLock::new();
impl ExtensionRegistry {
/// Global extension registry (used by Interpreter when interop is enabled)
pub fn global() -> &'static RwLock<ExtensionRegistry> {
GLOBAL_REGISTRY.get_or_init(|| RwLock::new(ExtensionRegistry::new()))
}
}