Skip to content

Commit 78b2ea8

Browse files
committed
Enhance overflow handling in arithmetic operations and implement an asm-processor that creates stack frames (so we can use Java 8)
1 parent 19f27a2 commit 78b2ea8

File tree

11 files changed

+550
-76
lines changed

11 files changed

+550
-76
lines changed

.gitignore

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
# Ignores relvant to Rust projects within this tree
12
**/target/*
2-
**/*.class
3-
**/*.txt
3+
4+
# Ignores relevant to Kotlin projects within this tree
5+
**/.gradle/*
6+
**/build/*
7+
**/.kotlin/*
8+
9+
# Files generated by Tester.py
410
**/*.generated
5-
**/*.bc
6-
**/*.jar
11+
12+
# ICE files generated by Rustc
13+
**/*.txt
14+
15+
# Crash logs generated by the JVM
16+
**/*.log

Makefile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Makefile
2+
3+
.PHONY: all rust java-linker asm-processor clean clean-rust clean-java-linker clean-asm-processor
4+
5+
all: rust java-linker asm-processor
6+
7+
# --- Rust root project ---
8+
rust:
9+
cargo build
10+
11+
clean-rust:
12+
cargo clean
13+
14+
# --- Java Linker Rust Subproject ---
15+
java-linker:
16+
cd java-linker && cargo build
17+
18+
clean-java-linker:
19+
cd java-linker && cargo clean
20+
21+
# --- ASM Processor (Gradle) ---
22+
asm-processor:
23+
cd asm-processor && gradle shadowJar
24+
25+
clean-asm-processor:
26+
cd asm-processor && gradle clean
27+
28+
# --- Clean everything ---
29+
clean: clean-rust clean-java-linker clean-asm-processor

Tester.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import subprocess
44
import sys
5+
import argparse
56

67
def read_from_file(path: str) -> str:
78
with open(path, "r") as f:
@@ -19,7 +20,7 @@ def write_to_file(path: str, content: str):
1920
with open(path, "w") as f:
2021
f.write(content)
2122

22-
def process_test(test_dir: str):
23+
def process_test(test_dir: str, release_mode: bool):
2324
test_name = os.path.basename(test_dir)
2425
normalized = normalize_name(test_name)
2526
print(f"|-- Test '{test_name}' ({normalized})")
@@ -36,14 +37,14 @@ def process_test(test_dir: str):
3637

3738
# Run cargo build.
3839
print("|--- ⚒️ Building with Cargo...")
39-
# see if the file no_jvm_target.flag exists in the test directory
40+
build_cmd = ["cargo", "build", "--release"] if release_mode else ["cargo", "build"]
4041
no_jvm_target = os.path.join(test_dir, "no_jvm_target.flag")
4142
if os.path.exists(no_jvm_target):
4243
print("|---- ⚠️ Skipping JVM target build due to no_jvm_target.flag")
43-
proc = run_command(["cargo", "build"], cwd=test_dir)
4444
else:
4545
print("|---- 🛠️ Building with JVM target...")
46-
proc = run_command(["cargo", "build", "--target", "../../../jvm-unknown-unknown.json"], cwd=test_dir)
46+
build_cmd.extend(["--target", "../../../jvm-unknown-unknown.json"])
47+
proc = run_command(build_cmd, cwd=test_dir)
4748
if proc.returncode != 0:
4849
fail_path = os.path.join(test_dir, "cargo-build-fail.generated")
4950
output = f"STDOUT:\n{proc.stdout}\n\nSTDERR:\n{proc.stderr}"
@@ -53,26 +54,22 @@ def process_test(test_dir: str):
5354

5455
# Run java with the generated jar.
5556
print("|--- 🤖 Running with Java...")
56-
# if no_jvm_target flag is set, we first need to move target/debug/deps/{test_name}-{hash}.jar to target/jvm-unknown-unknown/debug/{test_name}.jar
57-
# we might need to make the directory first
57+
target_dir = "release" if release_mode else "debug"
5858
if os.path.exists(no_jvm_target):
5959
print("|---- ⚠️ Doing some needed moving around due to no_jvm_target.flag")
60-
# move the jar file to the target/jvm-unknown-unknown/debug directory
61-
jar_path = os.path.join(test_dir, "target", "debug", "deps", f"{test_name}-*.jar")
62-
# find the jar file
60+
jar_path = os.path.join(test_dir, "target", target_dir, "deps", f"{test_name}-*.jar")
6361
jar_file = None
64-
for file in os.listdir(os.path.join(test_dir, "target", "debug", "deps")):
62+
for file in os.listdir(os.path.join(test_dir, "target", target_dir, "deps")):
6563
if file.startswith(test_name) and file.endswith(".jar"):
6664
jar_file = file
6765
break
6866
if jar_file is None:
69-
print("|---- ❌ No jar file found in target/debug/deps")
67+
print("|---- ❌ No jar file found in target/{target_dir}/deps")
7068
return False
71-
# move the file
72-
os.makedirs(os.path.join(test_dir, "target", "jvm-unknown-unknown", "debug"), exist_ok=True)
73-
os.rename(os.path.join(test_dir, "target", "debug", "deps", jar_file), os.path.join(test_dir, "target", "jvm-unknown-unknown", "debug", f"{test_name}.jar"))
74-
jar_path = os.path.join(test_dir, "target", "jvm-unknown-unknown", "debug", f"{test_name}.jar")
75-
proc = run_command(["java", "-jar", jar_path])
69+
os.makedirs(os.path.join(test_dir, "target", "jvm-unknown-unknown", target_dir), exist_ok=True)
70+
os.rename(os.path.join(test_dir, "target", target_dir, "deps", jar_file), os.path.join(test_dir, "target", "jvm-unknown-unknown", target_dir, f"{test_name}.jar"))
71+
jar_path = os.path.join(test_dir, "target", "jvm-unknown-unknown", target_dir, f"{test_name}.jar")
72+
proc = run_command(["java", "-jar", jar_path])
7673
if proc.returncode != 0:
7774
fail_path = os.path.join(test_dir, "java-fail.generated")
7875
output = f"STDOUT:\n{proc.stdout}\n\nSTDERR:\n{proc.stderr}"
@@ -100,9 +97,16 @@ def process_test(test_dir: str):
10097
return True
10198

10299
def main():
100+
parser = argparse.ArgumentParser(description="Tester for Rustc's JVM Codegen Backend")
101+
parser.add_argument("--release", action="store_true", help="Run cargo in release mode")
102+
args = parser.parse_args()
103+
103104
print("🧪 Tester for Rustc's JVM Codegen Backend started!")
104105
overall_success = True
105106

107+
if args.release:
108+
print("|- ⚒️ Running in release mode")
109+
106110
print(" ")
107111

108112
# Process binary tests.
@@ -113,7 +117,7 @@ def main():
113117
binary_tests = []
114118
print(f"|- 📦 Running {len(binary_tests)} binary build tests...")
115119
for idx, test_dir in enumerate(binary_tests):
116-
if not process_test(test_dir):
120+
if not process_test(test_dir, args.release):
117121
overall_success = False
118122

119123
print("")

asm-processor/build.gradle.kts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
plugins {
2+
kotlin("jvm") version "2.1.20"
3+
id("com.gradleup.shadow") version "8.3.6"
4+
application
5+
}
6+
7+
group = "com.integralpilot"
8+
version = "1.0-SNAPSHOT"
9+
10+
repositories {
11+
mavenCentral()
12+
}
13+
14+
dependencies {
15+
// ASM Core library
16+
implementation("org.ow2.asm:asm:9.8")
17+
18+
testImplementation(kotlin("test"))
19+
}
20+
21+
application {
22+
mainClass.set("asmprocessor.ProcessorKt")
23+
}
24+
25+
tasks.test {
26+
useJUnitPlatform()
27+
}
28+
kotlin {
29+
jvmToolchain(21) // latest LTS. note to self: update when they release a new LTS.
30+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package asmprocessor
2+
3+
import org.objectweb.asm.ClassReader
4+
import org.objectweb.asm.ClassWriter
5+
import java.nio.file.Files
6+
import java.nio.file.Paths
7+
import java.nio.file.StandardOpenOption
8+
import kotlin.system.exitProcess
9+
10+
fun main(args: Array<String>) {
11+
if (args.size != 2) {
12+
System.err.println("Usage: kotlin asmprocessor.ProcessorKt <input.class> <output.class>")
13+
exitProcess(1)
14+
}
15+
val inputFile = args[0]
16+
val outputFile = args[1]
17+
18+
try {
19+
println("Reading: $inputFile")
20+
val originalBytecode: ByteArray = Files.readAllBytes(Paths.get(inputFile))
21+
22+
val cr = ClassReader(originalBytecode)
23+
// Combine COMPUTE_FRAMES and COMPUTE_MAXS to generate new frames and adjust max values.
24+
val cw = ClassWriter(ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
25+
26+
// Use ClassReader.EXPAND_FRAMES to ensure full frame expansion before computing new frames.
27+
cr.accept(cw, ClassReader.EXPAND_FRAMES)
28+
29+
val modifiedBytecode: ByteArray = cw.toByteArray()
30+
31+
println("Writing processed bytecode to: $outputFile")
32+
Files.write(
33+
Paths.get(outputFile),
34+
modifiedBytecode,
35+
StandardOpenOption.CREATE,
36+
StandardOpenOption.TRUNCATE_EXISTING
37+
)
38+
39+
println("Done.")
40+
41+
} catch (e: Exception) {
42+
System.err.println("Error processing class file: ${e.message}")
43+
e.printStackTrace() // Print full stack trace for debugging
44+
exitProcess(1)
45+
}
46+
}

build.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
cargo clean
2+
cargo build
13
pushd java-linker
4+
cargo clean
25
cargo build
36
popd
4-
cargo build
7+
pushd asm-processor
8+
gradle clean
9+
gradle shadowJar
10+
popd

0 commit comments

Comments
 (0)