Transformer delegation list belongs in MixinEnvironment

This commit is contained in:
Mumfrey committed 2015-04-14 12:33:44 +01:00
1 parent 333450f64c
commit a85f2cd316
2 files changed
+122 -114

No files matched your search

@@ -27,15 +27,21 @@ package org.spongepowered.asm.mixin;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.helpers.Booleans;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.transformer.MixinTransformer;
import org.spongepowered.asm.util.PrettyPrinter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import net.minecraft.launchwrapper.IClassNameTransformer;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
@@ -277,11 +283,22 @@ public class MixinEnvironment {
}
}
/**
* Known re-entrant transformers, other re-entrant transformers will
* detected automatically
*/
private static final Set<String> excludeTransformers = Sets.<String>newHashSet(
"net.minecraftforge.fml.common.asm.transformers.EventSubscriptionTransformer",
"cpw.mods.fml.common.asm.transformers.EventSubscriptionTransformer",
"net.minecraftforge.fml.common.asm.transformers.TerminalTransformer",
"cpw.mods.fml.common.asm.transformers.TerminalTransformer"
);
// Blackboard keys
private static final String CONFIGS_KEY = "mixin.configs";
private static final String TRANSFORMER_KEY = "mixin.transformer";
/**
* Current (active) environment phase, set to NOT_INITIALISED until the
* phases have been populated
@@ -298,6 +315,11 @@ public class MixinEnvironment {
*/
private static MixinEnvironment currentEnvironment;
/**
* Logger
*/
private final Logger logger = LogManager.getLogger("mixin");
/**
* The phase for this environment
*/
@@ -318,6 +340,19 @@ public class MixinEnvironment {
*/
private Side side;
/**
* Local transformer chain, this consists of all transformers present at the
* init phase with the exclusion of the mixin transformer itself and known
* re-entrant transformers. Detected re-entrant transformers will be
* subsequently removed.
*/
private List<IClassTransformer> transformers;
/**
* Class name transformer (if present)
*/
private IClassNameTransformer nameTransformer;
private MixinEnvironment(Phase phase) {
this.phase = phase;
this.configsKey = MixinEnvironment.CONFIGS_KEY + "." + this.phase.name.toLowerCase();
@@ -473,7 +508,85 @@ public class MixinEnvironment {
transformer.audit();
}
}
/**
* Returns (and generates if necessary) the transformer delegation list for
* this environment.
*/
public List<IClassTransformer> getTransformers() {
if (this.transformers == null) {
this.buildTransformerDelegationList();
}
return Collections.unmodifiableList(this.transformers);
}
/**
* Adds a transformer to the transformer exclusions list
*
* @param name
*/
public void addTransformerExclusion(String name) {
MixinEnvironment.excludeTransformers.add(name);
// Force rebuild of the list
this.transformers = null;
}
/**
* Map a class name back to its obfuscated counterpart
*
* @param className
* @return obfuscated name for the specified deobfuscated reference
*/
public String unmap(String className) {
if (this.nameTransformer == null) {
this.buildTransformerDelegationList();
}
if (this.nameTransformer != null) {
return this.nameTransformer.unmapClassName(className);
}
return className;
}
/**
* Builds the transformer list to apply to loaded mixin bytecode. Since
* generating this list requires inspecting each transformer by name (to
* cope with the new wrapper functionality added by FML) we generate the
* list just once per environment and cache the result.
*/
private void buildTransformerDelegationList() {
this.logger.debug("Rebuilding transformer delegation list:");
this.transformers = new ArrayList<IClassTransformer>();
for (IClassTransformer transformer : Launch.classLoader.getTransformers()) {
String transformerName = transformer.getClass().getName();
boolean include = true;
for (String excludeClass : MixinEnvironment.excludeTransformers) {
if (transformerName.contains(excludeClass)) {
include = false;
break;
}
}
if (include && !transformerName.contains(MixinTransformer.class.getName())) {
this.logger.debug(" Adding: {}", transformerName);
this.transformers.add(transformer);
} else {
this.logger.debug(" Excluding: {}", transformerName);
}
}
this.logger.debug("Transformer delegation list created with {} entries", this.transformers.size());
for (IClassTransformer transformer : Launch.classLoader.getTransformers()) {
if (transformer instanceof IClassNameTransformer) {
this.logger.debug("Found name transformer: {}", transformer.getClass().getName());
this.nameTransformer = (IClassNameTransformer) transformer;
}
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -27,12 +27,7 @@ package org.spongepowered.asm.mixin.transformer;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.minecraft.launchwrapper.IClassNameTransformer;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
@@ -44,44 +39,13 @@ import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.transformer.MixinTransformer.ReEntranceState;
import com.google.common.collect.Sets;
/**
* Base class for "info" objects which use ASM tree API to do stuff, with things
*/
abstract class TreeInfo {
/**
* Known re-entrant transformers, other re-entrant transformers will
* detected automatically
*/
private static final Set<String> excludeTransformers = Sets.<String>newHashSet(
"net.minecraftforge.fml.common.asm.transformers.EventSubscriptionTransformer",
"cpw.mods.fml.common.asm.transformers.EventSubscriptionTransformer",
"net.minecraftforge.fml.common.asm.transformers.TerminalTransformer",
"cpw.mods.fml.common.asm.transformers.TerminalTransformer"
);
private static final Logger logger = LogManager.getLogger("mixin");
/**
* Local transformer chain, this consists of all transformers present at the
* init phase with the exclusion of the mixin transformer itself and known
* re-entrant transformers. Detected re-entrant transformers will be
* subsequently removed.
*/
private static List<IClassTransformer> transformers;
/**
* Class name transformer (if present)
*/
private static IClassNameTransformer nameTransformer;
/**
* Current environment
*/
private static MixinEnvironment currentEnvironment;
/**
* Re-entrance lock
*/
@@ -100,7 +64,7 @@ abstract class TreeInfo {
* @param flags
* @return ASM Tree view of the specified class
*/
static ClassNode getClassNode(byte[] classBytes, int flags) {
protected static ClassNode getClassNode(byte[] classBytes, int flags) {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(classBytes);
classReader.accept(classNode, flags);
@@ -112,11 +76,9 @@ abstract class TreeInfo {
* @param runTransformers
* @return Transformed class bytecode for the specified class
*/
static byte[] loadClass(String className, boolean runTransformers) throws ClassNotFoundException, IOException {
TreeInfo.checkEnvironment();
protected static byte[] loadClass(String className, boolean runTransformers) throws ClassNotFoundException, IOException {
String transformedName = className.replace('/', '.');
String name = TreeInfo.unmap(transformedName);
String name = MixinEnvironment.getCurrentEnvironment().unmap(transformedName);
byte[] classBytes = null;
if ((classBytes = TreeInfo.getClassBytes(name, transformedName)) == null) {
@@ -167,11 +129,9 @@ abstract class TreeInfo {
* except the excluded transformers
*/
private static byte[] applyTransformers(String name, String transformedName, byte[] basicClass) {
final List<IClassTransformer> transformers = TreeInfo.transformers;
for (Iterator<IClassTransformer> iter = transformers.iterator(); iter.hasNext();) {
IClassTransformer transformer = iter.next();
MixinEnvironment environment = MixinEnvironment.getCurrentEnvironment();
for (IClassTransformer transformer : environment.getTransformers()) {
if (TreeInfo.lock != null) {
// Clear the re-entrance semaphore
TreeInfo.lock.clear();
@@ -180,12 +140,8 @@ abstract class TreeInfo {
basicClass = transformer.transform(name, transformedName, basicClass);
if (TreeInfo.lock != null && TreeInfo.lock.isSet()) {
// If the re-entrance semaphore was raised during the last transformation then we can assume
// that this transformer is re-entrant and remove it from our transformer chain.
iter.remove();
// Also add it to the exclusion list so we can exclude it if the environment triggers a rebuild
TreeInfo.excludeTransformers.add(transformer.getClass().getName());
environment.addTransformerExclusion(transformer.getClass().getName());
TreeInfo.lock.clear();
TreeInfo.logger.info("A re-entrant transformer '{}' was detected and will no longer process meta class data",
@@ -195,65 +151,4 @@ abstract class TreeInfo {
return basicClass;
}
/**
* Map a class name back to its obfuscated counterpart
*
* @param className
* @return obfuscated name for the specified deobfuscated reference
*/
static String unmap(String className) {
IClassNameTransformer nameTransformer = TreeInfo.nameTransformer;
if (nameTransformer != null) {
return nameTransformer.unmapClassName(className);
}
return className;
}
/**
* Check whether the current environment has changed
*/
private static void checkEnvironment() {
if (MixinEnvironment.getCurrentEnvironment() != TreeInfo.currentEnvironment) {
TreeInfo.buildTransformerDelegationList();
TreeInfo.currentEnvironment = MixinEnvironment.getCurrentEnvironment();
}
}
/**
* Builds the transformer list to apply to loaded mixin bytecode. Since
* generating this list requires inspecting each transformer by name (to
* cope with the new wrapper functionality added by FML) we generate the
* list just once and cache the result.
*/
private static void buildTransformerDelegationList() {
TreeInfo.logger.debug("Building transformer delegation list:");
TreeInfo.transformers = new ArrayList<IClassTransformer>();
for (IClassTransformer transformer : Launch.classLoader.getTransformers()) {
String transformerName = transformer.getClass().getName();
boolean include = true;
for (String excludeClass : TreeInfo.excludeTransformers) {
if (transformerName.contains(excludeClass)) {
include = false;
break;
}
}
if (include && !transformerName.contains(MixinTransformer.class.getName())) {
TreeInfo.logger.debug(" Adding: {}", transformerName);
TreeInfo.transformers.add(transformer);
} else {
TreeInfo.logger.debug(" Excluding: {}", transformerName);
}
}
TreeInfo.logger.debug("Transformer delegation list created with {} entries", TreeInfo.transformers.size());
for (IClassTransformer transformer : Launch.classLoader.getTransformers()) {
if (transformer instanceof IClassNameTransformer) {
TreeInfo.logger.debug("Found name transformer: {}", transformer.getClass().getName());
TreeInfo.nameTransformer = (IClassNameTransformer) transformer;
}
}
}
}