diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 3502930..a51b4c1 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -13,10 +13,10 @@ jobs: contents: read steps: - uses: actions/checkout@v6 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Build and check with Gradle Wrapper run: ./gradlew check diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6b092..fae6c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Gradle ASMifier Changelog +## Unreleased + +- Adding support for any non-Java sources (tested with Kotlin). + ## Version 1.2.0 (2025-03-05) - Adding support for anonymous classes. diff --git a/README.md b/README.md index a1dc8ea..beefa63 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,21 @@ What it is --- -Convenience tool that converts `.java` files to [ASM](https://asm.ow2.io/) instructions by wrapping +Convenience tool that converts `.java` source files (and non-Java ones that still compile into JVM bytecode, such as +Kotlin's (`.kt`) ones, for example) to [ASM](https://asm.ow2.io/) instructions by wrapping the [ASMifier](https://asm.ow2.io/javadoc/org/objectweb/asm/util/ASMifier.html) tool around a Gradle incremental task -that can convert multiple `.java` files at once. +that can convert multiple source files at once. This tool, as well as [the one it's built upon](https://asm.ow2.io/javadoc/org/objectweb/asm/util/ASMifier.html), is -meant to be used as a development tool for anyone who'd like to check how does Java code translate into bytecode +meant to be used as a development tool for anyone who'd like to check how does source code translate into bytecode instructions using [ASM](https://asm.ow2.io/). What it is not --- -This is not a tool to generate production code. Its `.java` target files are in a separate location from the production -source files (similarly to the test sources, which are in a separate dir that isn't packaged into the production app). +This is not a tool to generate production code. Its source target files are in a separate location from the +production source files (similarly to the test sources, which are in a separate dir that isn't packaged into the +production app). How to use --- @@ -47,13 +49,16 @@ dependencies { } ``` -The `asmifier` dependency type is added by this plugin to ensure that its dependencies are separated from those of your -app (similarly to configurations such as `testImplementation` are only used for a specific purpose and not to get -packaged with your production code). +You can find the latest ASMifier version [here](https://central.sonatype.com/artifact/org.ow2.asm/asm-util). + +> [!NOTE] +> The `asmifier` dependency type is added by this plugin to ensure that its dependencies are separated from those +> of your app (similarly to configurations such as `testImplementation` are only used for a specific purpose and not +> to get packaged with your production code). ### Add sources to transform -The `.java` sources that will be transformed by this plugin must be placed in a src dir named `asmifier`, as +The sources that will be transformed by this plugin must be placed in a src dir named `asmifier`, as shown below. ```text @@ -63,12 +68,12 @@ app/ │ ├─ main/ │ │ ├─ java/ │ ├─ asmifier/ -│ │ ├─ java/ <-- Here is where the asmifier .java target files must be placed +│ │ ├─ java/ <-- Here is where the asmifier .java (or other JVM-supported) target files must be placed ``` ### Run the Gradle task -To transform the `.java` target files you must run the gradle task named `asmifier`, like so: +To transform the source files you must run the Gradle task named `asmifier`, like so: ```shell ./gradlew asmifier diff --git a/asmifier-plugin/build.gradle.kts b/asmifier-plugin/build.gradle.kts index d17a9db..8c244cd 100644 --- a/asmifier-plugin/build.gradle.kts +++ b/asmifier-plugin/build.gradle.kts @@ -16,10 +16,10 @@ dependencies { testImplementation(libs.assertj) } -val javaVersion = JavaVersion.VERSION_11 java { - sourceCompatibility = javaVersion - targetCompatibility = javaVersion + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } } gradlePlugin { @@ -31,17 +31,15 @@ gradlePlugin { } } -tasks.withType(Test::class.java) { +tasks.withType { useJUnitPlatform() systemProperty("asm_version", libs.versions.asm.get()) } -tasks.withType(JavaCompile::class.java) { +tasks.withType { if (name.contains("test", true)) { options.errorprone.isEnabled.set(false) - val testJavaVersion = JavaVersion.VERSION_15.toString() - sourceCompatibility = testJavaVersion - targetCompatibility = testJavaVersion } else { + options.release = 11 // Ensuring deliverable jvm compatibility options.errorprone { check("NullAway", CheckSeverity.ERROR) option("NullAway:AnnotatedPackages", "com.likethesalad.asm") diff --git a/asmifier-plugin/src/main/java/com/likethesalad/asm/AsmifierPlugin.java b/asmifier-plugin/src/main/java/com/likethesalad/asm/AsmifierPlugin.java index e95e0ef..d95be56 100644 --- a/asmifier-plugin/src/main/java/com/likethesalad/asm/AsmifierPlugin.java +++ b/asmifier-plugin/src/main/java/com/likethesalad/asm/AsmifierPlugin.java @@ -1,15 +1,16 @@ package com.likethesalad.asm; import com.likethesalad.asm.tasks.AsmifierTask; +import java.util.List; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.file.Directory; -import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.Sync; import org.gradle.api.tasks.TaskProvider; import org.jetbrains.annotations.NotNull; @@ -25,6 +26,16 @@ public void apply(Project project) { Configuration asmifierClasspath = getAsmifierClasspath(project, asmifierSourceSet); + TaskProvider asmifierTargetCollector = + project.getTasks().register("asmifierCollector", Sync.class); + asmifierTargetCollector.configure( + sync -> { + sync.from( + asmifierSourceSet.getOutput(), + copySpec -> copySpec.setIncludes(List.of("**/*.class"))); + sync.into(project.getLayout().getBuildDirectory().dir("intermediates/" + sync.getName())); + }); + TaskProvider asmifierTaskTaskProvider = project.getTasks().register(ASMIFIER_TASK_NAME, AsmifierTask.class); asmifierTaskTaskProvider.configure( @@ -36,9 +47,7 @@ public void apply(Project project) { .getLayout() .getBuildDirectory() .dir("generated/sources/" + ASMIFIER_OUTPUT_DIR_NAME)); - asmifierTask - .getTargetClasses() - .from(getTargetClassesCollection(project, asmifierSourceSet)); + asmifierTask.getTargetClasses().from(asmifierTargetCollector); asmifierTask.getClasspath().from(asmifierClasspath); }); @@ -49,13 +58,6 @@ public void apply(Project project) { asmifierTaskTaskProvider.flatMap(AsmifierTask::getOutputDir)); } - private static @NotNull FileCollection getTargetClassesCollection( - Project project, SourceSet asmifierSourceSet) { - return project - .files(asmifierSourceSet.getOutput()) - .filter(element -> !element.getName().endsWith(".class")); - } - private static void configureDumpSourceSet( Project project, SourceSet asmifierSourceSet, diff --git a/asmifier-plugin/src/main/java/com/likethesalad/asm/tasks/AsmifierTask.java b/asmifier-plugin/src/main/java/com/likethesalad/asm/tasks/AsmifierTask.java index dd0f52e..48f2228 100644 --- a/asmifier-plugin/src/main/java/com/likethesalad/asm/tasks/AsmifierTask.java +++ b/asmifier-plugin/src/main/java/com/likethesalad/asm/tasks/AsmifierTask.java @@ -80,7 +80,7 @@ public void execute(InputChanges inputChanges) { private void asmifyToFile(File outputFile, FileCollection classpath, String relativeSourcePath) { try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { asmify(classpath, relativeSourcePath, outputStream); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException( "Exception during asmifier run where the source is: " + relativeSourcePath diff --git a/asmifier-plugin/src/test/java/com/likethesalad/asm/AsmifierPluginTest.java b/asmifier-plugin/src/test/java/com/likethesalad/asm/AsmifierPluginTest.java index 9184745..d17a99f 100644 --- a/asmifier-plugin/src/test/java/com/likethesalad/asm/AsmifierPluginTest.java +++ b/asmifier-plugin/src/test/java/com/likethesalad/asm/AsmifierPluginTest.java @@ -30,15 +30,15 @@ void setUp() throws IOException { createFile( "settings.gradle.kts", """ - import org.gradle.api.initialization.resolve.RepositoriesMode - - dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - mavenCentral() - } - } - """); + import org.gradle.api.initialization.resolve.RepositoriesMode + + dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + } + } + """); } @Test @@ -46,25 +46,25 @@ void verifyFileOutput() throws IOException { createAsmifierSourceFile( "com/test/MyClass.java", """ - package com.test; - - public class MyClass { - public void someMethod() { - System.out.println("Hello World!"); - } - } - """); + package com.test; + + public class MyClass { + public void someMethod() { + System.out.println("Hello World!"); + } + } + """); createAsmifierSourceFile( "com/test/MyOtherClass.java", """ - package com.test; + package com.test; - public class MyOtherClass { - public void someOtherMethod() { - System.out.println("Hello Other World!"); - } - } - """); + public class MyOtherClass { + public void someOtherMethod() { + System.out.println("Hello Other World!"); + } + } + """); BuildResult result = asmifierRunner().build(); @@ -80,22 +80,22 @@ void verifyFileOutputWithAnonymousClass() throws IOException { createAsmifierSourceFile( "com/test/MyClass.java", """ - package com.test; - - import java.util.function.Supplier; - - public class MyClass { - public void someMethod() { - Supplier supplier = new Supplier() { - @Override - public String get() { - return "Hello World!"; - } - }; - System.out.println(supplier.get()); + package com.test; + + import java.util.function.Supplier; + + public class MyClass { + public void someMethod() { + Supplier supplier = new Supplier() { + @Override + public String get() { + return "Hello World!"; + } + }; + System.out.println(supplier.get()); + } } - } - """); + """); BuildResult result = asmifierRunner().build(); assertThat(getAsmifierOutcome(result)).isEqualTo(TaskOutcome.SUCCESS); @@ -105,31 +105,52 @@ public String get() { "asm/com/test/MyClassDump.java", "asm/com/test/MyClass$1Dump.java"); } + @Test + void verifyFileFromNonJavaSourceOutput() throws IOException { + createAsmifierSourceFile( + "com/test/MyClass.kt", + """ + package com.test + + class MyClass { + fun someMethod() { + println("Hello World!") + } + } + """); + + BuildResult result = asmifierRunner().build(); + + assertThat(getAsmifierOutcome(result)).isEqualTo(TaskOutcome.SUCCESS); + Map generatedFiles = getGeneratedFiles(); + assertThat(generatedFiles.keySet()).containsExactlyInAnyOrder("asm/com/test/MyClassDump.java"); + } + @Test void verifyIncrementalCompilation() throws IOException { Path myClassFile = createAsmifierSourceFile( "com/test/MyClass.java", """ - package com.test; + package com.test; - public class MyClass { - public void someMethod() { - System.out.println("Hello World!"); - } - } - """); + public class MyClass { + public void someMethod() { + System.out.println("Hello World!"); + } + } + """); createAsmifierSourceFile( "com/test/MySecondClass.java", """ - package com.test; + package com.test; - public class MySecondClass { - public void someSecondMethod() { - System.out.println("Hello World!"); + public class MySecondClass { + public void someSecondMethod() { + System.out.println("Hello World!"); + } } - } - """); + """); createAsmifierSourceFile( "com/test/MyThirdClass.java", """ @@ -167,26 +188,26 @@ public void someThirdMethod() { createAsmifierSourceFile( "com/test/MyThirdClass.java", """ - package com.test; + package com.test; - public class MyThirdClass { - public void someThirdMethod() { - System.out.println("Hello Changed World!"); - } + public class MyThirdClass { + public void someThirdMethod() { + System.out.println("Hello Changed World!"); } - """); + } + """); // Adding input createAsmifierSourceFile( "com/test/MyFourthClass.java", """ - package com.test; + package com.test; - public class MyFourthClass { - public void someFourthMethod() { - System.out.println("Hello World!"); - } + public class MyFourthClass { + public void someFourthMethod() { + System.out.println("Hello World!"); } - """); + } + """); // Rerun BuildResult secondResult = asmifierRunner().build(); @@ -257,15 +278,16 @@ private void createBuildFile() throws IOException { createFile( "build.gradle.kts", """ - plugins { - id("java") - id("com.likethesalad.asmifier") - } - - dependencies { - asmifier("org.ow2.asm:asm-util:%s") - } - """ + plugins { + id("java") + id("org.jetbrains.kotlin.jvm") version "2.0.0" + id("com.likethesalad.asmifier") + } + + dependencies { + asmifier("org.ow2.asm:asm-util:%s") + } + """ .formatted(System.getProperty("asm_version"))); } diff --git a/gradle.properties b/gradle.properties index 800797c..63cec08 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ #Wed Mar 05 06:05:03 UTC 2025 +org.gradle.jvmargs=-Xmx1024M description=Convert Java code to ASM instructions version=1.3.0 -group=com.likethesalad.asm +group=com.likethesalad.asm \ No newline at end of file