Skip to content

Commit d551beb

Browse files
committed
Support for assert! (supringly involved at lot, including a new OOMIR Switch instruction, a new Kotlin-based shim of core and a lot of new pointer constant handling logic)
1 parent 361dc51 commit d551beb

File tree

12 files changed

+1529
-546
lines changed

12 files changed

+1529
-546
lines changed

Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Makefile
22

3-
.PHONY: all rust java-linker asm-processor clean clean-rust clean-java-linker clean-asm-processor
3+
.PHONY: all rust java-linker asm-processor clean clean-rust clean-java-linker clean-asm-processor library clean-library
44

5-
all: rust java-linker asm-processor
5+
all: rust java-linker asm-processor library
66

77
# --- Rust root project ---
88
rust:
@@ -25,5 +25,11 @@ asm-processor:
2525
clean-asm-processor:
2626
cd asm-processor && gradle clean
2727

28+
# --- Standard Library Shim (Gradle) ---
29+
library:
30+
cd library && gradle build
31+
clean-library:
32+
cd library && gradle clean
33+
2834
# --- Clean everything ---
29-
clean: clean-rust clean-java-linker clean-asm-processor
35+
clean: clean-rust clean-java-linker clean-asm-processor clean-library

Readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ This project includes integration tests managed by a Python script.
134134
* `asm-processor/`: Kotlin/Gradle project for the ASM-based bytecode post-processor that adds stack map frames to the generated `.class` files.
135135
* `tests/`: Integration tests.
136136
* `binary/`: Tests for compiling executable Rust crates.
137+
* `library/`: Source code for a minimal Kotlin-based implementation of the Rust core library. This serves as a temporary substitute to bootstrap the project until the backend can fully compile the Rust core library itself.
137138
* `jvm-unknown-unknown.json`: The Rust target specification file.
138139
* `Tester.py`: Python script for running integration tests.
139140
* `Makefile`, `build.sh`: Scripts for building the entire toolchain.

build.sh

Lines changed: 0 additions & 10 deletions
This file was deleted.

java-linker/src/main.rs

Lines changed: 99 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::env;
22
use std::fs;
3-
use std::io::{self, Write};
3+
use std::io::{self, Write, Read};
44
use std::path::{Path, PathBuf};
55
use std::process::Command;
66
use regex::Regex;
77
use zip::write::{SimpleFileOptions, ZipWriter};
8-
use zip::CompressionMethod;
8+
use zip::{CompressionMethod, ZipArchive};
99
use tempfile::NamedTempFile;
1010

1111
fn main() -> Result<(), i32> {
@@ -129,6 +129,23 @@ fn find_main_classes(class_files: &[String]) -> Vec<String> {
129129
main_classes
130130
}
131131

132+
fn process_jar_file(jar_path: &str) -> io::Result<Vec<(String, Vec<u8>)>> {
133+
let jar_file = fs::File::open(jar_path)?;
134+
let mut archive = ZipArchive::new(jar_file)?;
135+
let mut class_files = Vec::new();
136+
137+
for i in 0..archive.len() {
138+
let mut file = archive.by_index(i)?;
139+
if file.name().ends_with(".class") {
140+
let mut contents = Vec::new();
141+
file.read_to_end(&mut contents)?;
142+
class_files.push((file.name().to_string(), contents));
143+
}
144+
}
145+
146+
Ok(class_files)
147+
}
148+
132149
fn create_jar(
133150
input_files: &[String],
134151
output_jar_path: &str,
@@ -148,61 +165,100 @@ fn create_jar(
148165

149166
let re = Regex::new(r"^(.*?)-[0-9a-f]+(\.class)$").unwrap();
150167

151-
for input_file_path_str in input_files {
152-
let input_path = Path::new(input_file_path_str);
153-
let original_file_name = input_path.file_name()
154-
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid input path: {}", input_file_path_str)))?
155-
.to_str()
156-
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Input filename is not valid UTF-8"))?;
168+
// Track all class files we've added to avoid duplicates
169+
let mut added_class_files = std::collections::HashSet::new();
157170

158-
let jar_entry_name = if let Some(caps) = re.captures(original_file_name) {
159-
format!("{}{}", &caps[1], &caps[2])
160-
} else {
161-
original_file_name.to_string()
162-
};
171+
for input_file_path_str in input_files {
172+
if input_file_path_str.ends_with(".jar") {
173+
println!("Processing JAR file: {}", input_file_path_str);
174+
let class_files = process_jar_file(input_file_path_str)?;
175+
176+
for (class_path, class_data) in class_files {
177+
if !added_class_files.contains(&class_path) {
178+
let processed_data = if let Some(processor_path) = processor_jar_path {
179+
let temp_out_file = NamedTempFile::new()?;
180+
let temp_in_file = NamedTempFile::new()?;
181+
fs::write(&temp_in_file, &class_data)?;
163182

164-
let file_data_to_add: Vec<u8>;
165-
let mut _temp_file_handle: Option<NamedTempFile> = None;
183+
let temp_out_path = temp_out_file.path().to_path_buf();
184+
let temp_in_path = temp_in_file.path().to_path_buf();
166185

167-
if input_file_path_str.ends_with(".class") {
168-
println!("Processing class file: {}", input_file_path_str);
186+
let mut cmd = Command::new("java");
187+
cmd.arg("-jar")
188+
.arg(processor_path)
189+
.arg(&temp_in_path)
190+
.arg(&temp_out_path);
169191

170-
let file_data = if let Some(processor_path) = processor_jar_path {
171-
let temp_out_file = NamedTempFile::new()?;
172-
let temp_out_path = temp_out_file.path().to_path_buf();
192+
println!("Running processor on {}: {:?}", class_path, cmd);
193+
let output = cmd.output()?;
173194

174-
let mut cmd = Command::new("java");
175-
cmd.arg("-jar")
176-
.arg(processor_path)
177-
.arg(input_file_path_str)
178-
.arg(&temp_out_path);
195+
if !output.status.success() {
196+
eprintln!("Error processing file from JAR: {}", class_path);
197+
eprintln!("Processor STDOUT:\n{}", String::from_utf8_lossy(&output.stdout));
198+
eprintln!("Processor STDERR:\n{}", String::from_utf8_lossy(&output.stderr));
199+
return Err(io::Error::new(io::ErrorKind::Other, "ASM processor failed"));
200+
}
179201

180-
println!("Running processor: {:?}", cmd);
181-
let output = cmd.output()?;
202+
fs::read(&temp_out_path)?
203+
} else {
204+
class_data
205+
};
182206

183-
if !output.status.success() {
184-
eprintln!("Error processing file: {}", input_file_path_str);
185-
eprintln!("Processor STDOUT:\n{}", String::from_utf8_lossy(&output.stdout));
186-
eprintln!("Processor STDERR:\n{}", String::from_utf8_lossy(&output.stderr));
187-
return Err(io::Error::new(io::ErrorKind::Other, "ASM processor failed"));
207+
zip_writer.start_file(&class_path, options)?;
208+
zip_writer.write_all(&processed_data)?;
209+
added_class_files.insert(class_path.clone());
210+
println!("Added class from JAR: {}", class_path);
188211
}
212+
}
213+
} else if input_file_path_str.ends_with(".class") {
214+
let input_path = Path::new(input_file_path_str);
215+
let original_file_name = input_path.file_name()
216+
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid input path: {}", input_file_path_str)))?
217+
.to_str()
218+
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Input filename is not valid UTF-8"))?;
189219

190-
let processed_data = fs::read(&temp_out_path)?;
191-
_temp_file_handle = Some(temp_out_file);
192-
processed_data
220+
let jar_entry_name = if let Some(caps) = re.captures(original_file_name) {
221+
format!("{}{}", &caps[1], &caps[2])
193222
} else {
194-
fs::read(input_file_path_str)?
223+
original_file_name.to_string()
195224
};
196225

197-
file_data_to_add = file_data;
198-
println!("Successfully processed: {}", input_file_path_str);
226+
if !added_class_files.contains(&jar_entry_name) {
227+
println!("Processing class file: {}", input_file_path_str);
228+
229+
let file_data = if let Some(processor_path) = processor_jar_path {
230+
let temp_out_file = NamedTempFile::new()?;
231+
let temp_out_path = temp_out_file.path().to_path_buf();
232+
233+
let mut cmd = Command::new("java");
234+
cmd.arg("-jar")
235+
.arg(processor_path)
236+
.arg(input_file_path_str)
237+
.arg(&temp_out_path);
238+
239+
println!("Running processor: {:?}", cmd);
240+
let output = cmd.output()?;
241+
242+
if !output.status.success() {
243+
eprintln!("Error processing file: {}", input_file_path_str);
244+
eprintln!("Processor STDOUT:\n{}", String::from_utf8_lossy(&output.stdout));
245+
eprintln!("Processor STDERR:\n{}", String::from_utf8_lossy(&output.stderr));
246+
return Err(io::Error::new(io::ErrorKind::Other, "ASM processor failed"));
247+
}
248+
249+
fs::read(&temp_out_path)?
250+
} else {
251+
fs::read(input_file_path_str)?
252+
};
253+
254+
zip_writer.start_file(&jar_entry_name, options)?;
255+
zip_writer.write_all(&file_data)?;
256+
added_class_files.insert(jar_entry_name);
257+
println!("Successfully processed: {}", input_file_path_str);
258+
}
199259
} else {
200-
println!("Skipping non-class file: {}", input_file_path_str);
201-
continue;
260+
println!("Skipping non-class/jar file: {}", input_file_path_str);
202261
}
203-
204-
zip_writer.start_file(&jar_entry_name, options)?;
205-
zip_writer.write_all(&file_data_to_add)?;
206262
}
207263

208264
zip_writer.finish()?;

library/build.gradle.kts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
kotlin("jvm") version "2.1.20"
3+
application
4+
}
5+
6+
group = "org.rustlang"
7+
version = "0.1.0"
8+
9+
repositories {
10+
mavenCentral()
11+
}
12+
13+
kotlin {
14+
jvmToolchain(21) // latest LTS. note to self: update when they release a new LTS.
15+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.rustlang.core
2+
3+
/**
4+
* Core functions needed by the Rust JVM backend stdlib shim.
5+
*/
6+
public object Core {
7+
8+
@JvmStatic // Ensures static JVM methods are generated
9+
public fun rust_panic_fmt(message: String?) {
10+
// note to future self: consider using a more specific Rust exception type if needed
11+
throw RuntimeException("Rust panic: " + (message ?: "<no message>"))
12+
}
13+
14+
@JvmStatic
15+
public fun rust_fmt_arguments_new_const_stub(messagePiece: String?): String? {
16+
return messagePiece
17+
}
18+
}

src/lib.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! Compiler backend for rustc that generates JVM bytecode, using a two-stage lowering process:
1111
//! MIR -> OOMIR -> JVM Bytecode.
1212
13+
extern crate rustc_abi;
1314
extern crate rustc_codegen_ssa;
1415
extern crate rustc_data_structures;
1516
extern crate rustc_driver;
@@ -24,16 +25,16 @@ use rustc_codegen_ssa::{
2425
CodegenResults, CompiledModule, CrateInfo, ModuleKind, traits::CodegenBackend,
2526
};
2627

27-
use rustc_metadata::EncodedMetadata;
2828
use rustc_data_structures::fx::FxIndexMap;
29+
use rustc_metadata::EncodedMetadata;
2930
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
3031
use rustc_middle::ty::TyCtxt;
3132
use rustc_session::{Session, config::OutputFilenames};
3233
use std::{any::Any, io::Write, path::Path, vec};
3334

34-
mod oomir;
3535
mod lower1;
3636
mod lower2;
37+
mod oomir;
3738

3839
/// An instance of our Java bytecode codegen backend.
3940
struct MyBackend;
@@ -84,19 +85,24 @@ impl CodegenBackend for MyBackend {
8485
let oomir_function = lower1::mir_to_oomir(tcx, instance, &mut mir);
8586
println!("--- Finished MIR to OOMIR Lowering for function: {i} ---");
8687

87-
println!("OOMIR is: {:?}", oomir_function);
88-
89-
oomir_module.functions.insert(oomir_function.name.clone(), oomir_function);
88+
oomir_module
89+
.functions
90+
.insert(oomir_function.name.clone(), oomir_function);
9091
}
9192
}
9293

9394
println!("OOMIR module: {:?}", oomir_module);
9495

95-
println!("--- Starting OOMIR to JVM Bytecode Lowering for module: {} ---", crate_name);
96+
println!(
97+
"--- Starting OOMIR to JVM Bytecode Lowering for module: {} ---",
98+
crate_name
99+
);
96100
let bytecode = lower2::oomir_to_jvm_bytecode(&oomir_module, tcx).unwrap();
97101
//let bytecode = vec![0; 1024];
98-
println!("--- Finished OOMIR to JVM Bytecode Lowering for module: {} ---", crate_name);
99-
102+
println!(
103+
"--- Finished OOMIR to JVM Bytecode Lowering for module: {} ---",
104+
crate_name
105+
);
100106

101107
Box::new((
102108
bytecode,
@@ -117,8 +123,6 @@ impl CodegenBackend for MyBackend {
117123
.downcast::<(Vec<u8>, String, EncodedMetadata, CrateInfo)>()
118124
.expect("in join_codegen: ongoing_codegen is not bytecode vector");
119125

120-
println!("going to write: {:?}", bytecode);
121-
122126
let class_path = outputs.temp_path_ext("class", None);
123127

124128
let mut class_file =
@@ -172,7 +176,6 @@ pub fn custom_alloc_error_hook(layout: Layout) {
172176
panic!("Memory allocation failed: {} bytes", layout.size());
173177
}
174178

175-
176179
struct RlibArchiveBuilder;
177180
impl ArchiveBuilderBuilder for RlibArchiveBuilder {
178181
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
@@ -190,4 +193,4 @@ impl ArchiveBuilderBuilder for RlibArchiveBuilder {
190193
) {
191194
unimplemented!("creating dll imports is not supported");
192195
}
193-
}
196+
}

0 commit comments

Comments
 (0)