Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add javac plugin #22

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ java {

tasks.jar {
from(sourceSets["main"].output, sourceSets["shared"].output)
from(project("javac-plugin").sourceSets["main"].output)

from(rootDir.resolve("LICENSE.md"))
from(rootDir.resolve("license")) {
into("license")
Expand All @@ -171,6 +173,7 @@ tasks.jar {

tasks.getByName<Jar>("sourcesJar") {
from(sourceSets["shared"].allSource)
from(project("javac-plugin").sourceSets["main"].allSource)
from(rootDir.resolve("LGPLv2.1.md"))

isPreserveFileTimestamps = false
Expand All @@ -185,6 +188,7 @@ project.evaluationDependsOnChildren()

val shadowJar by tasks.registering(ShadowJar::class) {
from(sourceSets["main"].output, sourceSets["shared"].output)
from(project("javac-plugin").sourceSets["main"].output)
from(rootDir.resolve("LGPLv2.1.md"))

isPreserveFileTimestamps = false
Expand Down
1 change: 1 addition & 0 deletions java-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ tasks.getByName<JavaCompile>("compileCoverageJava") {

val genCtSym by tasks.registering(GenerateCtSymTask::class) {
group = "jvmdg"
lowerVersion = fromVersion
upperVersion = toVersion - 1
}

Expand Down
32 changes: 32 additions & 0 deletions javac-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

val java8 = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(8)) }.get()

dependencies {
compileOnly(rootProject.sourceSets["main"].output)
compileOnly(rootProject.sourceSets["shared"].output)

implementation(files("${java8.metadata.installationPath}/lib/tools.jar"))

testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.compileTestJava {
classpath += project(":java-api").tasks.getByName("testJar").outputs.files +
files(rootProject.sourceSets.map { it.compileClasspath }.flatten())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second part could be rootProject.tasks["jar"]outputs.files I think

Copy link
Contributor

@wagyourtail wagyourtail Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I would've passed the api jar as a flag, instead of directly putting it on the classpath. like I do in the other testing subproject

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah the second one is a good idea


javaCompiler = javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(17))
}

options.compilerArgs.add("-Xplugin:jvmdg --classVersion 52 --logLevel info")
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package xyz.wagyourtail.jvmdg.javac;

import com.sun.source.util.Plugin;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.BasicJavacTask;
import com.sun.tools.javac.main.JavaCompiler;
import xyz.wagyourtail.jvmdg.ClassDowngrader;
import xyz.wagyourtail.jvmdg.cli.Flags;
import xyz.wagyourtail.jvmdg.cli.Main;
import xyz.wagyourtail.jvmdg.compile.PathDowngrader;
import xyz.wagyourtail.jvmdg.util.Utils;

import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;

public class JvmdgJavacPlugin implements Plugin, Closeable {
private BasicJavacTask task;
private Flags flags;

@Override
public String getName() {
return "jvmdg";
}

static {
int vmVersion = Utils.classVersionToMajorVersion(Utils.getCurrentClassVersion());
if(vmVersion > 8) {
try {
//noinspection JavaReflectionMemberAccess
Method getModule = Class.class.getDeclaredMethod("getModule");
openModule(getModule.invoke(JavacTask.class));
} catch (Throwable t) {
Utils.sneakyThrow(t);
}
}
}

@Override
public void init(JavacTask t, String... args) {
this.task = (BasicJavacTask) t;

JavaCompiler compiler = JavaCompiler.instance(task.getContext());
compiler.closeables = compiler.closeables.prepend(this);

this.flags = new Flags();

try {
Main.parseArgs(args, flags);
} catch (Throwable e) {
Utils.sneakyThrow(e);
}
}

@Override
public void close() throws IOException {
runDowngrade();
}

@SuppressWarnings("UrlHashCode")
private void runDowngrade() throws IOException {
final JavaFileManager fileManager = task.getContext().get(JavaFileManager.class);

File root = new File(
fileManager.getJavaFileForOutput(
StandardLocation.CLASS_OUTPUT,
"", JavaFileObject.Kind.CLASS, null
).toUri()
).getParentFile();

Set<URL> classpathURLs = new HashSet<>();

// the set argument must be mutable, since GradleStandardJavaFileManager calls remove
for(JavaFileObject jfo : fileManager.list(StandardLocation.CLASS_PATH, "",
new HashSet<>(Collections.singletonList(JavaFileObject.Kind.CLASS)), true)) {
classpathURLs.add(jfo.toUri().toURL());
}

Path tempOutput = Files.createTempDirectory("downgrade");
tempOutput.toFile().deleteOnExit();

try(ClassDowngrader cd = ClassDowngrader.downgradeTo(flags)) {
cd.logger.debug("classpath: " + classpathURLs);

PathDowngrader.downgradePaths(
cd,
Collections.singletonList(root.toPath()),
Collections.singletonList(tempOutput),
classpathURLs
);
}

Files.walk(tempOutput)
.filter(Files::isRegularFile)
.forEach(p -> {
try {
Path target = root.toPath().resolve(tempOutput.relativize(p));
Files.createDirectories(target.getParent());
Files.move(p, target, StandardCopyOption.REPLACE_EXISTING);
} catch (Throwable t) {
Utils.sneakyThrow(t);
}
});
}

public static void openModule(Object o) throws Throwable {
Class<?> moduleClass = o.getClass();
if(!moduleClass.getName().equals("java.lang.Module")) {
throw new IllegalArgumentException("Not a module: " + o);
}
MethodHandle implAddOpens = Utils.getImplLookup().findVirtual(moduleClass, "implAddOpens",
MethodType.methodType(void.class, String.class));

@SuppressWarnings("unchecked")
Set<String> packages = (Set<String>) moduleClass.getDeclaredMethod("getPackages").invoke(o);

for(String pn : packages) {
implAddOpens.invoke(o, pn);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xyz.wagyourtail.jvmdg.javac.JvmdgJavacPlugin
18 changes: 18 additions & 0 deletions javac-plugin/src/test/java/TheTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class TheTest {
@Test
public void test() {
// if this class loads, the plugin is working

// just make sure we're running on Java 8
assertTrue(System.getProperty("java.version").startsWith("1.8"));

String h = """
Hello World!
""";
assertEquals("Hello World!\n", h);
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ plugins {
include("gradle-plugin")
include("java-api")
include("site")
include("javac-plugin")

include("testing")
include("testing:downgrade")
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public class Main {
private static Deque<File> tempFiles = new ArrayDeque<>();

public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
parseArgs(args, flags);
}

public static void parseArgs(String[] args, Flags flags) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
Arguments parser = new Arguments("JvmDowngrader", null, null, null);
Arguments input = new Arguments("--target", "input to output\n (required)\n you can use - for a temp-file if you want to chain operations", new String[]{"-t"}, new String[]{"input jar|path", "output jar|path"});
Arguments classpath = new Arguments("--classpath", "Classpath to use\n (highly recommended)", new String[]{"-cp"}, new String[]{"classpath"});
Expand Down