Skip to content

Commit

Permalink
refactor into java. add agent
Browse files Browse the repository at this point in the history
  • Loading branch information
wagyourtail committed Jun 7, 2024
1 parent 6693277 commit 253d81c
Show file tree
Hide file tree
Showing 16 changed files with 600 additions and 124 deletions.
51 changes: 50 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import xyz.wagyourtail.gradle.shadow.ShadowJar
import java.net.URI

plugins {
Expand All @@ -15,6 +16,21 @@ base {

val annotations by sourceSets.creating {}

val shared by sourceSets.creating {
compileClasspath += sourceSets.main.get().compileClasspath
runtimeClasspath += sourceSets.main.get().runtimeClasspath
}

val agent by sourceSets.creating {
compileClasspath += shared.output + sourceSets.main.get().compileClasspath
runtimeClasspath += shared.output + sourceSets.main.get().runtimeClasspath
}

sourceSets.main {
compileClasspath += shared.output
runtimeClasspath += shared.output
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
Expand All @@ -33,9 +49,16 @@ repositories {

val asmVersion: String by project.properties

val shade by configurations.creating {
configurations.implementation.get().extendsFrom(this)
}

dependencies {
implementation(gradleApi())
implementation("org.ow2.asm:asm-tree:${asmVersion}")

shade("org.ow2.asm:asm:${asmVersion}")
shade("org.ow2.asm:asm-commons:${asmVersion}")
shade("org.ow2.asm:asm-tree:${asmVersion}")

testImplementation(kotlin("test"))
}
Expand All @@ -45,6 +68,7 @@ tasks.test {
}

tasks.jar {
from(shared.output)

manifest {
attributes(
Expand All @@ -68,8 +92,29 @@ val annotationJar = tasks.register<Jar>("annotationJar") {
}
}

val agentShadeJar = tasks.register<ShadowJar>("agentShadowJar") {
archiveClassifier.set("agent")
from(agent.output, shared.output)

shadowContents.add(shade)
exclude("module-info.class")

relocate("org.objectweb.asm", "xyz.wagyourtail.unimined.expect.asm")

manifest {
attributes(
"Manifest-Version" to "1.0",
"Implementation-Title" to project.name,
"Implementation-Version" to project.version,
"Premain-Class" to "xyz.wagyourtail.unimined.expect.ExpectPlatformAgent",
"Can-Redefine-Classes" to "true",
)
}
}

tasks.assemble {
dependsOn(annotationJar)
dependsOn(agentShadeJar)
}

kotlin {
Expand Down Expand Up @@ -111,6 +156,10 @@ publishing {
artifact(annotationJar) {
classifier = "annotations"
}

artifact(agentShadeJar) {
classifier = "agent"
}
}
}
}
45 changes: 45 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.util.*

plugins {
kotlin("jvm") version "1.9.22"
}

repositories {
mavenCentral()
}

val asmVersion: String = project.properties["asmVersion"]?.toString() ?: run {
projectDir.parentFile.resolve("gradle.properties").inputStream().use {
val props = Properties()
props.load(it)
props.getProperty("asmVersion") as String
}
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType<JavaCompile> {
val targetVersion = 8
if (JavaVersion.current().isJava9Compatible) {
options.release.set(targetVersion)
}
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

dependencies {
implementation(gradleApi())

// commons compress
implementation("org.apache.commons:commons-compress:1.26.1")

implementation("org.ow2.asm:asm:${asmVersion}")
implementation("org.ow2.asm:asm-commons:${asmVersion}")
implementation("org.ow2.asm:asm-tree:${asmVersion}")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package xyz.wagyourtail.gradle.shadow

import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.commons.ClassRemapper
import xyz.wagyourtail.gradle.utils.MustSet
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.FilterReader
import java.io.Reader
import java.nio.charset.StandardCharsets

class PackageRelocateReader(input: Reader): FilterReader(input) {

var remapper: PackageRelocator by MustSet()

val contents = ByteArrayOutputStream().use { out ->
out.writer(StandardCharsets.ISO_8859_1).use { writer ->
input.copyTo(writer)
}
input.close()
out.toByteArray()
}

val changedContents: Reader by lazy {
val reader = ClassReader(contents)
val writer = ClassWriter(0)
reader.accept(ClassRemapper(writer, remapper), 0)
ByteArrayInputStream(writer.toByteArray()).bufferedReader(StandardCharsets.ISO_8859_1)
}

override fun read(): Int {
return changedContents.read()
}

override fun read(cbuf: CharArray, off: Int, len: Int): Int {
return changedContents.read(cbuf, off, len)
}

override fun skip(n: Long): Long {
return changedContents.skip(n)
}

override fun ready(): Boolean {
return changedContents.ready()
}

override fun markSupported(): Boolean {
return changedContents.markSupported()
}

override fun mark(readAheadLimit: Int) {
changedContents.mark(readAheadLimit)
}

override fun reset() {
changedContents.reset()
}

override fun close() {
changedContents.close()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package xyz.wagyourtail.gradle.shadow

import org.objectweb.asm.commons.Remapper

class PackageRelocator(val map: Map<String, String>) : Remapper() {

override fun map(internalName: String): String {
for ((from, to) in map) {
if (internalName.startsWith(from)) {
return to + internalName.substring(from.length)
}
}
return internalName
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package xyz.wagyourtail.gradle.shadow

import org.gradle.api.file.FileCollection
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.jvm.tasks.Jar
import java.nio.charset.StandardCharsets

abstract class ShadowJar : Jar() {

@get:Internal
abstract val shadowContents: ListProperty<FileCollection>

@get:Input
@get:Optional
abstract val relocatePackages: MapProperty<String, String>

init {
group = "Shadow"
description = "Shadow the jar with the specified configurations"

shadowContents.convention(mutableListOf()).finalizeValueOnRead()
relocatePackages.convention(mutableMapOf()).finalizeValueOnRead()
archiveClassifier.convention("all")
}

fun relocate(from: String, to: String) {
relocatePackages.put(from, to)
}

@TaskAction
fun runTask() {
for (fileCollection in shadowContents.get()) {
for (file in fileCollection) {
if (!file.exists()) continue
if (file.isDirectory) {
// copy directory
from(file)
} else {
// copy file
from(project.zipTree(file))
}
}
}

filteringCharset = StandardCharsets.ISO_8859_1.name()
includeEmptyDirs = false

if (relocatePackages.getOrElse(emptyMap()).isNotEmpty()) {
val map = relocatePackages.get()
.mapKeys { it.key.replace('.', '/') }
.mapKeys { if (!it.key.endsWith("/")) it.key + "/" else it.key }
.mapValues { it.value.replace('.', '/') }
.mapValues { if (!it.value.endsWith("/")) it.value + "/" else it.value }
val rel = PackageRelocator(map)
eachFile {
if (!it.path.endsWith(".class")) return@eachFile
it.path = rel.map(it.path)
it.filter(mapOf("remapper" to rel), PackageRelocateReader::class.java)
}
}

// call super
copy()
}

}
27 changes: 27 additions & 0 deletions buildSrc/src/main/kotlin/xyz/wagyourtail/gradle/utils/MustSet.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package xyz.wagyourtail.gradle.utils

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class MustSet<T> : ReadWriteProperty<Any?, T> {

@Suppress("ClassName")
private object UNINITIALIZED_VALUE

private var prop: Any? = UNINITIALIZED_VALUE

@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return if (prop == UNINITIALIZED_VALUE) {
synchronized(this) {
return if (prop == UNINITIALIZED_VALUE) throw IllegalStateException("Property ${property.name} must be set before use") else prop as T
}
} else prop as T
}

override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
synchronized(this) {
prop = value
}
}
}
8 changes: 8 additions & 0 deletions expect-platform-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ tasks.register('runC', JavaExec) {
group = 'ept'
}

tasks.register('runAgentA', JavaExec) {
classpath = sourceSets.a.runtimeClasspath + sourceSets.main.runtimeClasspath
mainClass = 'xyz.wagyourtail.ept.Main'
group = 'ept'

expectPlatform.insertAgent(delegate as JavaExecSpec, "a")
}

tasks.register('jarA', ExpectPlatformJar) {
platformName = "a"
inputFiles = sourceSets.main.output
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package xyz.wagyourtail.unimined.expect;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class ExpectPlatformAgent {
private static final String EXPECT_PLATFORM = "expect.platform";
private static final String REMAP = "expect.remap";
private static final String platform = System.getProperty(EXPECT_PLATFORM);
private static final String remap = System.getProperty(REMAP);


public static void premain(String args, Instrumentation inst) {
if (platform == null) {
throw new IllegalStateException("-D" + EXPECT_PLATFORM + " not set");
}
inst.addTransformer(new ExpectPlatformTransformer());
}

public static void agentmain(String args, Instrumentation inst) {
premain(args, inst);
}

public static class ExpectPlatformTransformer implements ClassFileTransformer {
TransformPlatform transformPlatform = new TransformPlatform(platform, remap);

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
ClassReader reader = new ClassReader(classfileBuffer);
ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);

transformPlatform.transform(classNode);

ClassWriter writer = new ClassWriter(reader, 0);
classNode.accept(writer);

return writer.toByteArray();
}

}

}
Loading

0 comments on commit 253d81c

Please sign in to comment.