|
1 |
| -# Rust → JVM Toolchain 🚀 |
2 |
| - |
3 |
| -Welcome! This project provides a toolchain for compiling Rust code into Java bytecode, allowing it to run on the JVM. |
4 |
| - |
5 |
| -## How It Works |
6 |
| - |
7 |
| -The toolchain follows these steps: |
8 |
| - |
9 |
| -1. **Parsing & MIR Generation** |
10 |
| - - Rust code is first parsed and converted into *MIR* (Mid-level Intermediate Representation) using `rustc`. |
11 |
| - |
12 |
| -2. **MIR to Java Bytecode** |
13 |
| - - The MIR for each crate is translated into a Java Classfile (containing JVM bytecode) via `rustc_codegen_jvm`, which is the core of this repository. |
14 |
| - - Source code for this component is located in the `src` folder. |
15 |
| - |
16 |
| -3. **Linking & `.jar` Generation** |
17 |
| - - Java Classfiles for all crates used in a library or executable are linked into a single `.jar` file, making it ready to run on the JVM. |
18 |
| - - This step is handled by `java-linker-rs`, a custom-built linker in this repository (found in the `java-linker` folder). |
19 |
| - |
20 |
| -## Current Capabilities |
21 |
| - |
22 |
| -- ✅ Compiling a minimal `no_std` & `no_core` Rust program with an empty `main` function. |
23 |
| -- ✅ Simple mathematical operations on `i32`s: addition, subtraction, mutliplication, division, modulo. |
24 |
| -- ✅ Calling other functions from the same library (including recursion). |
25 |
| -- ✅ if/else statements. |
26 |
| - |
27 |
| -### Next Milestone: |
28 |
| -🚧 **Full support for the `core` crate** is in progress! |
29 |
| - |
30 |
| -## How to Use the Toolchain |
31 |
| - |
32 |
| -### Prerequisites |
33 |
| -- Ensure you're on the **latest nightly build** of Rust. |
34 |
| -- Install necessary Rust components: |
35 |
| - ```sh |
36 |
| - rustup component add rust-src rustc-dev llvm-tools-preview |
37 |
| - ``` |
38 |
| -- Run the setup script: |
39 |
| - ```sh |
40 |
| - chmod +x setup.sh && ./setup.sh |
41 |
| - ``` |
42 |
| - |
43 |
| -### Compiling Rust to JVM Bytecode |
44 |
| -To use the toolchain for compiling your Rust project: |
45 |
| - ``` |
46 |
| -1. **Update the Target JSON** |
47 |
| - - Modify `jvm-unknown-unknown.json`, replacing the `../../..` (relative path to the project root from a test folder) part of the `linker` and `default-codegen-backend` lines to the absolute or relative path of where you cloned this repo. |
48 |
| -2. **Build Your Project** |
49 |
| - ```sh |
50 |
| - cargo build --target /path/to/rustc_codegen_jvm/jvm-unknown-unknown.json |
51 |
| - ``` |
52 |
| -3. **Find & Run the Generated `.jar`** |
53 |
| - - The compiled `.jar` file will be in: |
54 |
| - ``` |
55 |
| - target/jvm-unknown-unknown/debug/[cratename].jar |
56 |
| - ``` |
57 |
| - - If it's a binary (not a library), run it using: |
58 |
| - ```sh |
59 |
| - java -jar target/jvm-unknown-unknown/debug/[cratename].jar |
60 |
| - ``` |
61 |
| -
|
62 |
| -### Running Tests |
63 |
| -- If you modified the target JSON file, **revert the changes** before running tests. |
64 |
| -- Execute the test script: |
65 |
| - ```sh |
66 |
| - python3 Tester.py |
67 |
| - ``` |
68 |
| -- Look for a **success message** 🎉 |
| 1 | +# Rustc Codegen JVM 🚀 |
| 2 | + |
| 3 | +[](https://opensource.org/licenses/MIT) |
| 4 | + |
| 5 | +Welcome! This project provides a custom Rust compiler backend (`rustc_codegen_jvm`) that compiles Rust code into Java Virtual Machine (JVM) bytecode. This allows Rust programs to run on the JVM. Currently, the generated bytecode supports JRE 8 and later versions. |
| 6 | + |
| 7 | +## How It Works |
| 8 | + |
| 9 | +The toolchain transforms Rust code into executable `.jar` files through several stages: |
| 10 | + |
| 11 | +1. **Rustc Frontend (Parsing & MIR Generation)** |
| 12 | + * Your Rust code is parsed and lowered to *Mid-level Intermediate Representation (MIR)* by the standard `rustc` frontend. |
| 13 | + |
| 14 | +2. **MIR to OOMIR (Object-Oriented MIR)** |
| 15 | + * The MIR is lowered further into a custom intermediate representation called OOMIR. This step simplifies MIR constructs into a representation closer to object-oriented concepts, making the translation to JVM bytecode more manageable. (See `src/lower1.rs`). |
| 16 | + |
| 17 | +3. **OOMIR to JVM Classfile** |
| 18 | + * The OOMIR is then translated into standard Java `.class` files containing JVM bytecode. This is the core task of the `rustc_codegen_jvm` library in this repository (See `src/lower2.rs`). It utilizes the `ristretto_classfile` library for bytecode generation. |
| 19 | + |
| 20 | +4. **Classfile Post-Processing (Stack Map Frames)** |
| 21 | + * The generated `.class` files are processed by a dedicated tool (`asm-processor`, written in Kotlin using the ASM library). This tool calculates and inserts *Stack Map Frames*, which are required for class verification in modern JVM versions (Java 7+). |
| 22 | + |
| 23 | +5. **Linking & `.jar` Generation** |
| 24 | + * Finally, the processed `.class` files for the crate and its dependencies (though dependency handling is currently basic) are linked together into a single executable `.jar` file. This step is handled by `java-linker`, a custom linker built as part of this project (See `java-linker/`). It also generates the necessary `META-INF/MANIFEST.MF` file, marking the main class if applicable. |
| 25 | + |
| 26 | +## Current Capabilities |
| 27 | + |
| 28 | +This backend currently supports a subset of Rust features: |
| 29 | + |
| 30 | +* ✅ Compiling minimal `no_std` & `no_core` Rust programs (like an empty `main`) using the `jvm-unknown-unknown` target. |
| 31 | +* ✅ Compiling simple programs using basic `core` features (like the `is_even_plus_one` test) using the host target. |
| 32 | +* ✅ Basic integer arithmetic operations on `i32`: |
| 33 | + * Addition (`+`), Subtraction (`-`), Multiplication (`*`), Division (`/`), Remainder (`%`). |
| 34 | + * Checked addition and subtraction (`checked_add`, `checked_sub`) returning `(result, overflowed_bool)` tuples. |
| 35 | +* ✅ Comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`). |
| 36 | +* ✅ Bitwise operations (`&`, `|`, `^`, `<<`, `>>`). |
| 37 | +* ✅ Basic control flow: `if`/`else` statements, `panic!` / `assert!` (with simple string messages). |
| 38 | +* ✅ Calling other `static` functions within the same crate (including recursion). |
| 39 | +* ✅ Basic variable assignment (`let x = y;`). |
| 40 | +* ✅ Generating executable `.jar` files for binary crates. |
| 41 | + |
| 42 | +### Next Milestone: |
| 43 | +🚧 **Full support for the `core` crate** is the next major goal. |
| 44 | + |
| 45 | +## Prerequisites |
| 46 | + |
| 47 | +* **Rust Nightly:** Ensure you are using the latest **nightly** Rust toolchain. You can set this up with `rustup default nightly`. |
| 48 | +* **Rust Components:** Install necessary components: |
| 49 | + ```bash |
| 50 | + rustup component add rust-src rustc-dev llvm-tools-preview |
| 51 | + ``` |
| 52 | + *(Note: The `rust-toolchain.toml` file in the repository should handle this automatically if you use `cargo` within the project.)* |
| 53 | +* **Java Development Kit (JDK):** A working JDK (version 8 or later recommended) is required to run the generated `.jar` files and the `asm-processor`. Make sure `java` is in your PATH. |
| 54 | +* **Gradle:** Required to build the `asm-processor` Kotlin project. Make sure `gradle` is in your PATH. |
| 55 | + |
| 56 | +## Building the Toolchain |
| 57 | + |
| 58 | +1. **Run Setup Script (if needed):** |
| 59 | + * The `setup.sh` script mainly adds rustup components, which `rust-toolchain.toml` might already handle. Ensure the components listed in Prerequisites are installed. |
| 60 | +2. **Build All Components:** |
| 61 | + * You can use the provided Makefile or build script: |
| 62 | + ```bash |
| 63 | + # Using Make |
| 64 | + make all |
| 65 | +
|
| 66 | + # OR Using the build script |
| 67 | + chmod +x build.sh |
| 68 | + ./build.sh |
| 69 | + ``` |
| 70 | + * This will: |
| 71 | + * Build the main `rustc_codegen_jvm` library (`target/debug/librustc_codegen_jvm.dylib`). |
| 72 | + * Build the `java-linker` (`java-linker/target/debug/java-linker`). |
| 73 | + * Build the `asm-processor` (`asm-processor/build/libs/asm-processor-*.jar`). |
| 74 | + |
| 75 | +## Using the Toolchain (Compiling Another Rust Project) |
| 76 | + |
| 77 | +To compile *your own* Rust project using this backend: |
| 78 | + |
| 79 | +1. **Get the Toolchain:** Clone this repository and build it as described above. Let's assume you cloned it to `/path/to/rustc_codegen_jvm`. Ensure you build the toolchain first, as described in the previous section. |
| 80 | +
|
| 81 | +
|
| 82 | +2. **Configure Your Rust Project:** |
| 83 | + * In your *own* Rust project's directory, create a `.cargo/config.toml` file (if it doesn't exist). |
| 84 | + * Add the following content to the file: |
| 85 | + ```toml |
| 86 | + [build] |
| 87 | + rustflags = [ |
| 88 | + "-Z", "codegen-backend=/path/to/rustc_codegen_jvm/target/debug/librustc_codegen_jvm.dylib", |
| 89 | + "-C", "linker=/path/to/rustc_codegen_jvm/java-linker/target/debug/java-linker", |
| 90 | + "-C", "link-args=--asm-processor /path/to/rustc_codegen_jvm/asm-processor/build/libs/asm-processor-1.0-SNAPSHOT-all.jar" |
| 91 | + ] |
| 92 | + ``` |
| 93 | + * **Important:** Replace `/path/to/rustc_codegen_jvm/...` with the path to where you cloned the repository. If you're not on macOS, changed `.dylib` to `.so` for Linux or `.dll` for Windows. |
| 94 | + |
| 95 | +3. **Build Your Project:** |
| 96 | + * Run the standard Cargo build command, targeting the host target (using the `jvm-unknown-unknown` target is an emerging feature and means you can't even use `core`, for now, so not recommended). Cargo will read the `.cargo/config.toml` and use the specified target and flags automatically. |
| 97 | + ```bash |
| 98 | + cargo build |
| 99 | + # Or for release builds: |
| 100 | + # cargo build --release |
| 101 | + ``` |
| 102 | +
|
| 103 | +4. **Find & Run the Generated `.jar`:** |
| 104 | + * The compiled `.jar` file will be located in your project's `target` directory: |
| 105 | + * Debug: `target/debug/deps/[your_crate_name].jar` |
| 106 | + * Release: `target/release/deps/[your_crate_name].jar` |
| 107 | + * If your crate is a binary (`[[bin]]` or `src/main.rs`), run it using the `java` command: |
| 108 | + ```bash |
| 109 | + java -jar target/jvm-unknown-unknown/debug/[your_crate_name].jar |
| 110 | + ``` |
| 111 | + |
| 112 | +## Running This Project's Tests |
| 113 | + |
| 114 | +This project includes integration tests managed by a Python script. |
| 115 | + |
| 116 | +1. **Ensure Toolchain is Built:** Build the project using `make all` or `./build.sh`. |
| 117 | +2. **Check Target JSON:** Make sure the `jvm-unknown-unknown.json` file in the *root* of this repository has the **relative paths** starting with `../../../` for the linker and backend, as the tester expects this structure when running tests from subdirectories. If you changed them to absolute paths for external use, change them back temporarily. |
| 118 | +3. **Run the Tester:** |
| 119 | + ```bash |
| 120 | + python3 Tester.py |
| 121 | + # For release mode tests: |
| 122 | + # python3 Tester.py --release |
| 123 | + ``` |
| 124 | +4. **Check Output:** Look for the final "✅ All tests passed!" message. If tests fail, the script will generate `.generated` files in the respective test directories (`tests/binary/*`) containing error details or output diffs. |
| 125 | + |
| 126 | +## Project Structure |
| 127 | + |
| 128 | +* `src/`: Contains the source code for the `rustc_codegen_jvm` backend library. |
| 129 | + * `lib.rs`: Main backend integration point. |
| 130 | + * `lower1.rs`: MIR to OOMIR lowering pass. |
| 131 | + * `lower2.rs`: OOMIR to JVM bytecode generation pass. |
| 132 | + * `oomir.rs`: Definition of the Object-Oriented MIR (OOMIR). |
| 133 | +* `java-linker/`: Source code for the custom linker that creates `.jar` filesn from the generated `.class` files. |
| 134 | +* `asm-processor/`: Kotlin/Gradle project for the ASM-based bytecode post-processor that adds stack map frames to the generated `.class` files. |
| 135 | +* `tests/`: Integration tests. |
| 136 | + * `binary/`: Tests for compiling executable Rust crates. |
| 137 | +* `jvm-unknown-unknown.json`: The Rust target specification file. |
| 138 | +* `Tester.py`: Python script for running integration tests. |
| 139 | +* `Makefile`, `build.sh`: Scripts for building the entire toolchain. |
| 140 | +* `setup.sh`: Script to install Rust components. |
| 141 | +* `Cargo.toml`, `Cargo.lock`: Rust package definition and dependencies for the backend. |
| 142 | +* `LICENSE`, `LICENSE-Apache`: Project licenses. |
| 143 | + |
| 144 | +## Contributing |
| 145 | + |
| 146 | +Contributions are welcome! Feel free to open issues or pull requests. |
| 147 | + |
| 148 | +## License |
| 149 | + |
| 150 | +This project is dual-licensed under either of: |
| 151 | + |
| 152 | +* MIT License ([LICENSE-MIT](LICENSE) or http://opensource.org/licenses/MIT) |
| 153 | +* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-Apache) or http://www.apache.org/licenses/LICENSE-2.0) |
| 154 | + |
| 155 | +at your option. |
0 commit comments