Skip to content

Conversation

@Pokechu22
Copy link
Contributor

This fixes RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes to repair a case where ProGuard adds extra values for synthetic parameters (where the correct number of parameters is poorly defined). See MinecraftForge/ForgeGradle#472.

This detects synthetic parameters by following the same logic the compiler uses to insert them. There are two places where such synthetic parameters are added:

  • Enums (for the name and ordinal), compiler source here and also noted by CFR here

  • Inner classes (for the synthetic parameter that references the outer class), compiler source here and also noted by CFR here. I exclude anonymous classes based off of this info in the compiler; it also appears that anonymous classes do not have info about the outer class so this is fine.

    This check does depend on having accurate InnerClasses information. This has been provided since some time in 1.8 (at the same time, we started getting classes with $ in the name obfuscated for inner classes, which is how it should be), and as such is available in all versions that have annotations.

While theoretically we could detect synthetic params in a much simpler manner by checking the MethodParameters attribute, Minecraft does not include that attribute.

Here is a diff of the resulting changes. There are two inner classes with constructors that have annotations on them: SPacketPlayerListItem$AddPlayerData and RenderGlobal$ContainerLocalRenderInformation (which was previously noticed in ModCoderPack/MCPBot-Issues#257). There are no enums with constructors that have annotations on them. This is the case for both Minecraft 1.12.2 (as shown in that diff) and 1.9.4 (only ran exceptor; determined by checking the output).

This fixes RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes to repair a case where ProGuard adds extra values for synthetic parameters (where the correct number of parameters is poorly defined).  See MinecraftForge/ForgeGradle#472.
@Pokechu22
Copy link
Contributor Author

Cases that this doesn't handle:

  • Anonymous classes. However, while anonymous classes can have constructors, those constructors are synthetic and do not appear in the decompiled source. More importantly, annotations do not appear to be copied from the superclass (even if marked as @Inherited), so the ProGuard issue cannot happen here. Also, being anonymous, they cannot be referenced from outside the class, so the "bad class file" issue shouldn't be possible for them.

  • Named classes declared inside methods. This could actually be an issue, since these can have constructors. They also can have a reference to the enclosing class if the method isn't static. These should be detectable via the EnclosingMethod attribute, but actually figuring out what parameters are synthetic doesn't seem possible to me (at least not without looking at the code for the method as well). This is the code responsible for adding those parameters.

    void foo(String s) {
        class Test {
            @Override
            public String toString() { return s; }
        }
        System.out.println(new Test());
    }

    The Minecraft codebase does not appear to use any of these, so investing time into implementing it doesn't seem necessary right now. Also, as with before, these can't be referenced from outside, so no bad class file issues can happen.

  • A really weird edge case: this generates an anonymous class that has 2 synthetic parameters, one for the Something.this and one for ExtremelyStupidEdgeCase.this. Fun.

    public class ExtremelyStupidEdgeCase {
        public static class Something {
            public class Inner {
                @Override
                public String toString() {
                    return "inner of " + Something.this.toString();
                }
            }
        }
        public void method() {
            Something something = new Something();
            Something.Inner inner = something.new Inner() {
                @Override
                public String toString() {
                    return "annon, of " + ExtremelyStupidEdgeCase.this.toString() + " and " + super.toString();
                }
            };
            System.out.println(inner);
        }
    }

    Interestingly, when compiling with javac -parameters, the second synthetic parameter is not marked as such.

    Since this is an anonymous class, we don't have to deal with it for this purpose. I don't think a named class could be created like this.

@LexManos LexManos merged commit ddecb64 into ModCoderPack:master Feb 18, 2018
@Pokechu22 Pokechu22 mentioned this pull request Apr 23, 2018
SizableShrimp added a commit to SizableShrimp/ForgeAutoRenamingTool that referenced this pull request May 21, 2023
ProGuard has been updated since the inception of this fix in ModCoderPack/MCInjector#3 and no longer has the original bug. Mojang also appears to be using newer versions of ProGuard where the bug is not present.
The fixer is now properly programmed to change nothing if it doesn't detect the broken PG behavior.
The fixer is now also optimized to use the visitor system instead of populating a ClassNode; it has been rewritten to do so.
SizableShrimp added a commit to MinecraftForge/ForgeAutoRenamingTool that referenced this pull request May 22, 2023
ProGuard has been updated since the inception of this fix in ModCoderPack/MCInjector#3 and no longer has the original bug. Mojang also appears to be using newer versions of ProGuard where the bug is not present.
The fixer is now properly programmed to change nothing if it doesn't detect the broken PG behavior.
The fixer is now also optimized to use the visitor system instead of populating a ClassNode; it has been rewritten to do so.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants