diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/InnerClassGenerator.java b/src/main/java/org/spongepowered/asm/mixin/transformer/InnerClassGenerator.java index 4a5a56b9..d70c0537 100644 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/InnerClassGenerator.java +++ b/src/main/java/org/spongepowered/asm/mixin/transformer/InnerClassGenerator.java @@ -25,10 +25,12 @@ package org.spongepowered.asm.mixin.transformer; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import org.objectweb.asm.tree.InnerClassNode; import org.spongepowered.asm.logging.ILogger; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; @@ -57,7 +59,7 @@ final class InnerClassGenerator implements IClassGenerator { * Information about an inner class instance. Implements {@link Remapper} so * that it can participate in the remapping process. */ - static class InnerClassInfo extends Remapper implements ISyntheticClassInfo { + class InnerClassInfo extends Remapper implements ISyntheticClassInfo { /** * Mixin which provides this class @@ -155,10 +157,21 @@ String getNestHostName() { void accept(final ClassVisitor classVisitor) throws ClassNotFoundException, IOException { ClassNode classNode = MixinService.getService().getBytecodeProvider().getClassNode(this.originalName); + this.readInnerClasses(classNode); classNode.accept(classVisitor); this.loadCounter++; } + private void readInnerClasses(ClassNode classNode) { + // Read possibly nested inner classes + for (InnerClassNode inner : classNode.innerClasses) { + if ((inner.outerName != null && this.findRemappedName(inner.outerName) != null) + || inner.name.startsWith(this.mixin.getClassRef() + "$")) { + InnerClassGenerator.this.registerInnerClass(this.owner, this.targetClassInfo, inner.name); + } + } + } + /** * Used to remap fields which have been renamed while being applied to * the target class or by an implementation of IRemapper. @@ -198,10 +211,9 @@ public String mapMethodName(String owner, String name, String desc) { */ @Override public String map(String key) { - if (this.originalName.equals(key)) { - return this.name; - } else if (this.ownerName.equals(key)) { - return this.targetClassInfo.getName(); + String remappedName = this.findRemappedName(key); + if (remappedName != null) { + return remappedName; } return key; } @@ -213,6 +225,15 @@ public String map(String key) { public String toString() { return this.name; } + + private String findRemappedName(String originalName) { + if (this.ownerName.equals(originalName)) { + return this.targetClassInfo.getName(); + } + return InnerClassGenerator.this.innerClassNames.get( + InnerClassGenerator.innerClassCoordinate(this.owner, this.targetClassInfo, originalName) + ); + } } @@ -252,21 +273,6 @@ public void visitSource(String source, String debug) { av.visitEnd(); } - /* (non-Javadoc) - * @see org.objectweb.asm.commons.RemappingClassAdapter - * #visitInnerClass(java.lang.String, java.lang.String, - * java.lang.String, int) - */ - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - if (name.startsWith(this.info.getOriginalName() + "$")) { - throw new InvalidMixinException(this.info.getOwner(), "Found unsupported nested inner class " + name + " in " - + this.info.getOriginalName()); - } - - super.visitInnerClass(name, outerName, innerName, access); - } - } /** @@ -338,7 +344,7 @@ public String getName() { * @param innerClassName Original inner class name */ void registerInnerClass(MixinInfo owner, ClassInfo targetClass, String innerClassName) { - String coordinate = String.format("%s:%s:%s", owner, innerClassName, targetClass.getName()); + String coordinate = InnerClassGenerator.innerClassCoordinate(owner, targetClass, innerClassName); String uniqueName = this.innerClassNames.get(coordinate); if (uniqueName != null) { return; @@ -421,7 +427,8 @@ private static String getUniqueReference(String originalName, ClassInfo targetCl if (name.matches("^[0-9]+$")) { name = "Anonymous"; } - return String.format("%s$%s$%s", targetClass, name, UUID.randomUUID().toString().replace("-", "")); + UUID uuid = UUID.nameUUIDFromBytes(originalName.getBytes(StandardCharsets.UTF_8)); + return String.format("%s$%s$%s", targetClass, name, uuid.toString().replace("-", "")); } /** @@ -457,4 +464,8 @@ private static ClassVisitor createRemappingAdapter(ClassVisitor cv, Remapper rem return InnerClassGenerator.clRemapper.getConstructor(ClassVisitor.class, Remapper.class).newInstance(cv, remapper); } + private static String innerClassCoordinate(MixinInfo owner, ClassInfo targetClass, String innerClassName) { + return String.format("%s:%s:%s", owner.getClassRef(), innerClassName, targetClass.getName()); + } + } diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessor.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessor.java index 38354043..e044d2e0 100644 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessor.java +++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessor.java @@ -35,11 +35,10 @@ * mixins. * *

Such tasks as {@link MixinCoprocessorAccessor making accessor mixins - * loadable (and transforming the accessors therein)}, {@link - * MixinCoprocessorSyntheticInner exposing synthetic inner classes to all - * consumers} and {@link MixinCoprocessorNestHost applying nest member - * attributes to nest hosts which may themselves not be mixin targets} are - * handled by different coprocessors.

+ * loadable (and transforming the accessors therein)}, and {@link + * MixinCoprocessorNestHost applying nest member attributes to nest hosts + * which may themselves not be mixin targets} are handled by different + * coprocessors.

* *

These classes were previously encapsulated in a single companion class * called MixinPostProcessor, but the mixture of responsibilities of diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessorSyntheticInner.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessorSyntheticInner.java deleted file mode 100644 index ee1f3030..00000000 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinCoprocessorSyntheticInner.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of Mixin, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.asm.mixin.transformer; - -import java.util.HashSet; -import java.util.Set; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; - -/** - * Coprocessor which handles synthetic inner classes in mixins. Since synthetic - * inner classes are usually just switch tables, it is reasonably safe to share - * them amongst all mixin targets. Therefore the class is simply transformed so - * that all members are public. - * - *

Nest based-access control could potentially be used where a mixin has a - * single target, and Java 11 or higher is in use, but at the moment it's more - * straightforward to just make everything public.

- */ -class MixinCoprocessorSyntheticInner extends MixinCoprocessor { - - /** - * Synthetic inner classes in mixins - */ - private final Set syntheticInnerClasses = new HashSet(); - - MixinCoprocessorSyntheticInner() { - } - - @Override - String getName() { - return "syntheticinner"; - } - - @Override - public void onInit(MixinInfo mixin) { - for (String innerClass : mixin.getSyntheticInnerClasses()) { - this.registerSyntheticInner(innerClass.replace('/', '.')); - } - } - - void registerSyntheticInner(String className) { - this.syntheticInnerClasses.add(className); - } - - @Override - public boolean couldTransform(String className) { - return this.syntheticInnerClasses.contains(className); - } - - /** - * "Pass through" a synthetic inner class. Transforms package-private - * members in the class into public so that they are accessible from their - * new home in the target class - */ - @Override - ProcessResult process(String className, ClassNode classNode) { - if (!this.syntheticInnerClasses.contains(className)) { - return ProcessResult.NONE; - } - - classNode.access |= Opcodes.ACC_PUBLIC; - - for (FieldNode field : classNode.fields) { - if ((field.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) { - field.access |= Opcodes.ACC_PUBLIC; - } - } - - for (MethodNode method : classNode.methods) { - if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) { - method.access |= Opcodes.ACC_PUBLIC; - } - } - - return ProcessResult.PASSTHROUGH_TRANSFORMED; - } - -} diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinInfo.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinInfo.java index 1740b096..a26ac474 100644 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinInfo.java +++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinInfo.java @@ -224,14 +224,9 @@ class State { * Interfaces soft-implemented using {@link Implements} */ protected final List softImplements = new ArrayList(); - - /** - * Synthetic inner classes - */ - protected final Set syntheticInnerClasses = new HashSet(); /** - * Non-synthetic inner classes + * Inner classes */ protected final Set innerClasses = new HashSet(); @@ -286,10 +281,6 @@ List getSoftImplements() { return this.softImplements; } - Set getSyntheticInnerClasses() { - return this.syntheticInnerClasses; - } - Set getInnerClasses() { return this.innerClasses; } @@ -420,20 +411,13 @@ void readImplementations(SubType type) { } /** - * Read inner class definitions for the class and locate any synthetic - * inner classes so that we can add them to the passthrough set in our - * parent config. + * Read inner class definitions for the class and locate any of our inner classes. */ void readInnerClasses() { for (InnerClassNode inner : this.validationClassNode.innerClasses) { if ((inner.outerName != null && inner.outerName.equals(this.classInfo.getName())) || inner.name.startsWith(this.validationClassNode.name + "$")) { - boolean isStatic = (inner.access & Opcodes.ACC_STATIC) != 0; - boolean isSynthetic = (inner.access & Opcodes.ACC_SYNTHETIC) != 0; - - if (isStatic && isSynthetic) { - this.syntheticInnerClasses.add(inner.name); - } else if (!ClassInfo.isMixin(inner.name)) { + if (!ClassInfo.isMixin(inner.name)) { this.innerClasses.add(inner.name); } } @@ -466,7 +450,7 @@ class Reloaded extends State { */ @Override protected void validateChanges(SubType type, List targetClasses) { - if (!this.syntheticInnerClasses.equals(this.previous.syntheticInnerClasses)) { + if (!this.innerClasses.equals(this.previous.innerClasses)) { throw new MixinReloadException(MixinInfo.this, "Cannot change inner classes"); } if (!this.interfaces.equals(this.previous.interfaces)) { @@ -1237,16 +1221,9 @@ public List getTargetClasses() { List getSoftImplements() { return Collections.unmodifiableList(this.getState().getSoftImplements()); } - - /** - * Get the synthetic inner classes for this mixin - */ - Set getSyntheticInnerClasses() { - return Collections.unmodifiableSet(this.getState().getSyntheticInnerClasses()); - } /** - * Get the user-defined inner classes for this mixin + * Get the inner classes for this mixin */ Set getInnerClasses() { return Collections.unmodifiableSet(this.getState().getInnerClasses()); diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinProcessor.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinProcessor.java index 82ef8821..1a2985a5 100644 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinProcessor.java +++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinProcessor.java @@ -226,7 +226,6 @@ public String getErrorMessage(IMixinInfo mixin, IMixinConfig config, Phase phase this.hotSwapper = hotSwapper; this.coprocessors.add(new MixinCoprocessorPassthrough()); - this.coprocessors.add(new MixinCoprocessorSyntheticInner()); this.coprocessors.add(new MixinCoprocessorAccessor(this.sessionId)); this.coprocessors.add(nestHostCoprocessor); diff --git a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java index e41a3e2b..e66c1979 100644 --- a/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java +++ b/src/main/java/org/spongepowered/asm/mixin/transformer/MixinTargetContext.java @@ -637,6 +637,8 @@ private void transformFieldRef(MethodNode method, Iterator ite if (field != null && field.isRenamed() && field.getOriginalName().equals(fieldRef.getName()) && field.isStatic()) { fieldRef.setName(field.getName()); } + } else if (this.innerClasses.containsKey(fieldRef.getOwner())) { + fieldRef.setOwner(this.innerClasses.get(fieldRef.getOwner())); } else { if (ClassInfo.isMixin(fieldRef.getOwner())) { ClassInfo fieldOwner = ClassInfo.forName(fieldRef.getOwner());