Skip to content

Commit 459e106

Browse files
committed
Autogenerate proxy java classes.
1 parent d4e3d1f commit 459e106

File tree

5 files changed

+218
-8
lines changed

5 files changed

+218
-8
lines changed

java-spaghetti-gen/src/config.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{fs, io};
55

66
use serde_derive::Deserialize;
77

8-
fn default_proxy_path_prefix() -> String {
8+
fn default_proxy_package() -> String {
99
"java_spaghetti/proxy".to_string()
1010
}
1111

@@ -170,17 +170,19 @@ pub struct ClassConfig<'a> {
170170

171171
#[derive(Debug, Clone, Deserialize)]
172172
pub struct Config {
173-
#[serde(default = "default_proxy_path_prefix")]
174-
pub proxy_path_prefix: String,
173+
pub input: Vec<PathBuf>,
174+
pub output: PathBuf,
175175

176-
pub(crate) input: Vec<PathBuf>,
177-
pub(crate) output: PathBuf,
176+
#[serde(default = "default_proxy_package")]
177+
pub proxy_package: String,
178+
#[serde(default)]
179+
pub proxy_output: Option<PathBuf>,
178180

179181
#[serde(default)]
180-
pub(crate) logging_verbose: bool,
182+
pub logging_verbose: bool,
181183

182184
#[serde(default)]
183-
pub(crate) rules: Vec<Rule>,
185+
pub rules: Vec<Rule>,
184186
}
185187

186188
impl Config {
@@ -207,6 +209,9 @@ impl Config {
207209
}
208210

209211
config.output = resolve_file(&config.output, dir);
212+
if let Some(proxy_output) = &mut config.proxy_output {
213+
*proxy_output = resolve_file(proxy_output, dir);
214+
}
210215
for f in &mut config.input {
211216
*f = resolve_file(f, dir);
212217
}

java-spaghetti-gen/src/emit/class_proxy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl Class {
3232

3333
let java_proxy_path = format!(
3434
"{}/{}",
35-
context.config.proxy_path_prefix,
35+
context.config.proxy_package,
3636
self.java.path().as_str().replace("$", "_")
3737
);
3838

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use std::fmt::Write;
2+
use std::path::Path;
3+
4+
use cafebabe::descriptors::{FieldDescriptor, FieldType, ReturnDescriptor};
5+
6+
use super::classes::Class;
7+
use super::methods::Method;
8+
use crate::emit::Context;
9+
10+
impl Class {
11+
pub(crate) fn write_java_proxy(&self, context: &Context, methods: &[Method]) -> anyhow::Result<String> {
12+
let java_proxy_path = format!(
13+
"{}/{}",
14+
context.config.proxy_package,
15+
self.java.path().as_str().replace("$", "_")
16+
);
17+
18+
let package_name = java_proxy_path.rsplit_once('/').map(|x| x.0).unwrap_or("");
19+
let class_name = java_proxy_path.split('/').next_back().unwrap();
20+
21+
let mut w = String::new();
22+
23+
// Package declaration
24+
if !package_name.is_empty() {
25+
writeln!(w, "package {};", package_name.replace("/", "."))?;
26+
writeln!(w)?;
27+
}
28+
29+
// Class declaration
30+
let parent_type = if self.java.is_interface() {
31+
"implements"
32+
} else {
33+
"extends"
34+
};
35+
36+
writeln!(w, "@SuppressWarnings(\"rawtypes\")")?;
37+
38+
writeln!(
39+
w,
40+
"class {} {} {} {{",
41+
class_name,
42+
parent_type,
43+
self.java.path().as_str().replace('/', ".")
44+
)?;
45+
46+
// ptr field
47+
writeln!(w, " long ptr;")?;
48+
writeln!(w)?;
49+
50+
// Constructor
51+
writeln!(w, " private {class_name}(long ptr) {{")?;
52+
writeln!(w, " this.ptr = ptr;")?;
53+
writeln!(w, " }}")?;
54+
writeln!(w)?;
55+
56+
// Finalize method
57+
writeln!(w, " @Override")?;
58+
writeln!(w, " protected void finalize() throws Throwable {{")?;
59+
writeln!(w, " native_finalize(this.ptr);")?;
60+
writeln!(w, " }}")?;
61+
writeln!(w, " private native void native_finalize(long ptr);")?;
62+
writeln!(w)?;
63+
64+
// Generate methods
65+
for method in methods {
66+
let Some(_rust_name) = method.rust_name() else { continue };
67+
if method.java.is_static()
68+
|| method.java.is_static_init()
69+
|| method.java.is_constructor()
70+
|| method.java.is_final()
71+
|| method.java.is_private()
72+
{
73+
continue;
74+
}
75+
76+
let method_name = method.java.name();
77+
78+
// Method signature
79+
let return_type = match &method.java.descriptor.return_type {
80+
ReturnDescriptor::Void => "void".to_string(),
81+
ReturnDescriptor::Return(desc) => java_type_name(desc)?,
82+
};
83+
84+
let mut params = Vec::new();
85+
for (i, param) in method.java.descriptor.parameters.iter().enumerate() {
86+
let param_type = java_type_name(param)?;
87+
params.push(format!("{param_type} arg{i}"));
88+
}
89+
90+
writeln!(w, " @Override")?;
91+
writeln!(
92+
w,
93+
" public {} {}({}) {{",
94+
return_type,
95+
method_name,
96+
params.join(", ")
97+
)?;
98+
99+
// Method body - call native method
100+
let native_method_name = format!("native_{method_name}");
101+
let mut args = vec!["ptr".to_string()];
102+
for i in 0..method.java.descriptor.parameters.len() {
103+
args.push(format!("arg{i}"));
104+
}
105+
106+
if return_type == "void" {
107+
writeln!(w, " {}({});", native_method_name, args.join(", "))?;
108+
} else {
109+
writeln!(w, " return {}({});", native_method_name, args.join(", "))?;
110+
}
111+
writeln!(w, " }}")?;
112+
113+
// Native method declaration
114+
let mut native_params = vec!["long ptr".to_string()];
115+
for (i, param) in method.java.descriptor.parameters.iter().enumerate() {
116+
let param_type = java_type_name(param)?;
117+
native_params.push(format!("{param_type} arg{i}"));
118+
}
119+
120+
writeln!(
121+
w,
122+
" private native {} {}({});",
123+
return_type,
124+
native_method_name,
125+
native_params.join(", ")
126+
)?;
127+
writeln!(w)?;
128+
}
129+
130+
writeln!(w, "}}")?;
131+
132+
Ok(w)
133+
}
134+
}
135+
136+
fn java_type_name(desc: &FieldDescriptor) -> anyhow::Result<String> {
137+
let mut result = String::new();
138+
139+
let base_type = match &desc.field_type {
140+
FieldType::Byte => "byte",
141+
FieldType::Char => "char",
142+
FieldType::Double => "double",
143+
FieldType::Float => "float",
144+
FieldType::Integer => "int",
145+
FieldType::Long => "long",
146+
FieldType::Short => "short",
147+
FieldType::Boolean => "boolean",
148+
FieldType::Object(path) => {
149+
// Convert JNI path to Java path
150+
return Ok(format!(
151+
"{}{}",
152+
path.replace('/', "."),
153+
"[]".repeat(desc.dimensions as usize)
154+
));
155+
}
156+
};
157+
158+
result.push_str(base_type);
159+
160+
// Add array dimensions
161+
for _ in 0..desc.dimensions {
162+
result.push_str("[]");
163+
}
164+
165+
Ok(result)
166+
}
167+
168+
pub fn write_java_proxy_files(context: &Context, output_dir: &Path) -> anyhow::Result<()> {
169+
for (_, class) in context.all_classes.iter() {
170+
let cc = context.config.resolve_class(class.java.path().as_str());
171+
if !cc.proxy {
172+
continue;
173+
}
174+
175+
// Collect methods for this class
176+
let mut methods = Vec::new();
177+
methods.extend(class.java.methods().map(|m| Method::new(&class.java, m)));
178+
179+
let java_code = class.write_java_proxy(context, &methods)?;
180+
181+
// Calculate output file path
182+
let java_proxy_path = class.java.path().as_str().replace("$", "_");
183+
184+
let relative_path = format!("{java_proxy_path}.java");
185+
let output_file = output_dir.join(&relative_path);
186+
187+
// Create directory if it doesn't exist
188+
if let Some(parent) = output_file.parent() {
189+
std::fs::create_dir_all(parent)?;
190+
}
191+
192+
// Write Java file
193+
std::fs::write(&output_file, java_code)?;
194+
195+
println!("Generated Java proxy: {}", output_file.display());
196+
}
197+
198+
Ok(())
199+
}

java-spaghetti-gen/src/emit/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod class_proxy;
44
mod classes;
55
mod fields;
6+
pub mod java_proxy;
67
mod known_docs_url;
78
mod methods;
89
mod modules;

java-spaghetti-gen/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ pub fn run(config: impl Into<Config>) -> Result<(), Box<dyn Error>> {
3232
context.write(&mut out)?;
3333
util::write_generated(&context, &config.output, &out[..])?;
3434

35+
// Generate Java proxy files if proxy_output is specified
36+
if let Some(proxy_output) = &config.proxy_output {
37+
emit::java_proxy::write_java_proxy_files(&context, proxy_output)?;
38+
}
39+
3540
Ok(())
3641
}
3742

0 commit comments

Comments
 (0)