Skip to content

Commit

Permalink
TransformationPhase
Browse files Browse the repository at this point in the history
  • Loading branch information
mineLdiver committed Aug 21, 2023
1 parent 678522d commit 4c8f43b
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 22 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ yarn_mappings=18w49a.22
loader_version=0.14.21

# Mod Properties
mod_version=0.1.2
mod_version=0.2
maven_group=net.mine_diver
archives_base_name=SpASM
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.tree.ClassNode;

public interface ClassTransformer {
public interface ClassTransformer extends PhaseListener {
@NotNull TransformationResult transform(final @NotNull ClassLoader classLoader, final @NotNull ClassNode classNode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package net.mine_diver.spasm.api.transform;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;

import java.util.EnumSet;

public interface PhaseListener {
ImmutableSet<TransformationPhase>
DEFAULT_PHASES = Sets.immutableEnumSet(TransformationPhase.BEFORE_MIXINS),
ALL_PHASES = Sets.immutableEnumSet(EnumSet.allOf(TransformationPhase.class));


default @NotNull ImmutableSet<TransformationPhase> getPhases() {
return DEFAULT_PHASES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

import java.util.Optional;

public interface RawClassTransformer {
public interface RawClassTransformer extends PhaseListener {
@NotNull Optional<byte[]> transform(final @NotNull ClassLoader classLoader, final @NotNull String className, final byte @NotNull [] classBytes);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.mine_diver.spasm.api.transform;

import net.mine_diver.spasm.impl.SpASM;

public enum TransformationPhase {
BEFORE_MIXINS,
AFTER_MIXINS;

public static TransformationPhase getCurrent() {
return SpASM.getCurrentPhase();
}
}
52 changes: 34 additions & 18 deletions src/main/java/net/mine_diver/spasm/impl/MixinTransformerHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import lombok.val;
import net.mine_diver.spasm.api.transform.TransformationPhase;
import net.mine_diver.spasm.api.transform.TransformationResult;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
Expand All @@ -25,24 +26,39 @@ class MixinTransformerHook<T extends TreeTransformer & IMixinTransformer> extend

@Override
public byte[] transformClassBytes(String name, String transformedName, byte[] basicClass) {
if (basicClass != null && !name.startsWith("org.objectweb.asm.") && !name.startsWith("net.mine_diver.spasm.") && !name.startsWith("com.google.common.")) {
val classLoader = Thread.currentThread().getContextClassLoader();
for (int i = 0; i < RAW_TRANSFORMERS.size(); i++) {
val transformationResult = RAW_TRANSFORMERS.get(i).transform(classLoader, name, basicClass);
if (transformationResult.isPresent()) basicClass = transformationResult.get();
}
val classNode = new ClassNode();
new ClassReader(basicClass).accept(classNode, 0);
switch (TRANSFORMERS.stream()
.map(classTransformer -> classTransformer.transform(classLoader, classNode))
.reduce(PASS, TransformationResult::choose)) {
case SUCCESS:
val classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(classWriter);
basicClass = classWriter.toByteArray();
break;
}
if (shouldSkip(name, basicClass)) return super.transformClassBytes(name, transformedName, basicClass);
val classLoader = Thread.currentThread().getContextClassLoader();
basicClass = transform(name, basicClass, classLoader, TransformationPhase.BEFORE_MIXINS);
basicClass = super.transformClassBytes(name, transformedName, basicClass);
basicClass = transform(name, basicClass, classLoader, TransformationPhase.AFTER_MIXINS);
return basicClass;
}

private static boolean shouldSkip(String name, byte[] basicClass) {
return basicClass == null || name.startsWith("org.objectweb.asm.") || name.startsWith("net.mine_diver.spasm.") || name.startsWith("com.google.common.");
}

private static byte[] transform(String name, byte[] basicClass, ClassLoader classLoader, TransformationPhase phase) {
SpASM.currentPhase = phase;
for (int i = 0; i < RAW_TRANSFORMERS.size(); i++) {
val transformer = RAW_TRANSFORMERS.get(i);
if (!transformer.getPhases().contains(phase)) continue;
val transformationResult = transformer.transform(classLoader, name, basicClass);
if (transformationResult.isPresent()) basicClass = transformationResult.get();
}
val classNode = new ClassNode();
new ClassReader(basicClass).accept(classNode, 0);
switch (TRANSFORMERS.stream()
.filter(transformer -> transformer.getPhases().contains(phase))
.map(classTransformer -> classTransformer.transform(classLoader, classNode))
.reduce(PASS, TransformationResult::choose)) {
case SUCCESS:
val classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(classWriter);
basicClass = classWriter.toByteArray();
break;
}
return super.transformClassBytes(name, transformedName, basicClass);
SpASM.currentPhase = null;
return basicClass;
}
}
6 changes: 6 additions & 0 deletions src/main/java/net/mine_diver/spasm/impl/SpASM.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.fabricmc.loader.api.FabricLoader;
import net.mine_diver.spasm.api.transform.ClassTransformer;
import net.mine_diver.spasm.api.transform.RawClassTransformer;
import net.mine_diver.spasm.api.transform.TransformationPhase;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
Expand All @@ -18,6 +19,7 @@
public class SpASM implements IMixinConfigPlugin {
static final ImmutableList<ClassTransformer> TRANSFORMERS = entrypoint("transformer", ClassTransformer.class);
static final ImmutableList<RawClassTransformer> RAW_TRANSFORMERS = entrypoint("raw_transformer", RawClassTransformer.class);
static TransformationPhase currentPhase;

static {
try {
Expand All @@ -42,6 +44,10 @@ private static <T extends TreeTransformer & IMixinTransformer> void hook() throw
mixinTransformerField.set(knotClassDelegate, new MixinTransformerHook<>((T) mixinTransformerField.get(knotClassDelegate)));
}

public static TransformationPhase getCurrentPhase() {
return currentPhase;
}

@Override
public void onLoad(String mixinPackage) {}

Expand Down
22 changes: 22 additions & 0 deletions src/test/java/net/mine_diver/spasm/test/AllPhaseTransformer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.mine_diver.spasm.test;

import com.google.common.collect.ImmutableSet;
import net.mine_diver.spasm.api.transform.RawClassTransformer;
import net.mine_diver.spasm.api.transform.TransformationPhase;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Optional;

public class AllPhaseTransformer implements RawClassTransformer {
@Override
public @NotNull ImmutableSet<TransformationPhase> getPhases() {
return ALL_PHASES;
}

@Override
public @NotNull Optional<byte[]> transform(@NotNull ClassLoader classLoader, @NotNull String className, byte @NotNull [] classBytes) {
System.out.println("Phase transformer! " + className + " " + Arrays.hashCode(classBytes) + " " + TransformationPhase.getCurrent());
return Optional.empty();
}
}
3 changes: 2 additions & 1 deletion src/test/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"net.mine_diver.spasm.test.TestTransformer"
],
"spasm:raw_transformer": [
"net.mine_diver.spasm.test.TestRawTransformer"
"net.mine_diver.spasm.test.TestRawTransformer",
"net.mine_diver.spasm.test.AllPhaseTransformer"
]
},
"depends": {
Expand Down

0 comments on commit 4c8f43b

Please sign in to comment.