1

I'm trying to rename all classes from a compiled .jar to new names using ASM ClassRemapper almost everything is working, my app runs normally until it crashes with either ClassCastException or AbstractMethodError

ClassRemapper adapter = new ClassRemapper(classWriter, new Remapper()
{
    @Override
    public String mapType(String s)
    {
        return super.mapType(getNewName(s));
    }

    @Override
    public String mapFieldName(String owner, String name, String descriptor)
    {
        Type type = Type.getType(descriptor);
        descriptor = descriptor.replace(type.getClassName(), getNewName(type.getClassName()));

        return super.mapFieldName(getNewName(owner), name, descriptor);
    }

    @Override
    public String map(String internalName)
    {
        return getNewName(internalName);
    }

    @Override
    public String mapDesc(String descriptor)
    {
        Type type = Type.getType(descriptor);
        descriptor = descriptor.replace(type.getClassName(), getNewName(type.getClassName()));

        return super.mapDesc(descriptor);
    }

    @Override
    public String mapMethodDesc(String methodDescriptor)
    {
        Type methodType = Type.getMethodType(methodDescriptor);

        List<Type> types = new LinkedList<>();

        for (Type argumentType : methodType.getArgumentTypes())
            types.add(Type.getType(argumentType.getDescriptor().replace(argumentType.getClassName(), getNewName(argumentType.getClassName()))));

        Type returnType = Type.getReturnType(methodDescriptor);
        returnType = Type.getReturnType("()" + returnType.getDescriptor().replace(returnType.getClassName(), getNewName(returnType.getClassName())));

        return super.mapMethodDesc(Type.getMethodDescriptor(returnType, types.toArray(new Type[0])));
    }
});

I think I am missing something within methods, but I couldn't find what.

getNewName(string) is basically map.getOrDefault(string, string);

Foreign
  • 385
  • 2
  • 20
  • 1
    Most of your overrides are entirely unnecessary and may even cause contradicting results. `@Override public String map(String internalName) { return getNewName(internalName); }` is the only override you need. Depending on your renaming scheme, applying it multiple times may cause the problems. Besides that, I recommend reading [When to use LinkedList over ArrayList in Java?](https://stackoverflow.com/q/322715/2711488), as this is a typical case where `LinkedList` wastes resources without any benefit over `ArrayList`… – Holger Mar 04 '19 at 16:12
  • It's working perfectly after I removed the unnecessary overrides. I guess I should read asm' docs better. Thanks – Foreign Mar 04 '19 at 19:08

1 Answers1

2

Your are unnecessarily overriding the methods mapDesc, mapMethodDesc, and mapType, performing conversions (though not handling array types correctly), followed by invoking the super implementations provided by ClassRemapper which do already handle all cases, e.g. skipping primitive types and decomposing array types, and invoke map(String internalName) for ordinary references types. Depending on the actual renaming scheme, applying it multiple times may cause the problems.

Likewise, there is no need to override mapFieldName when you don’t want to rename fields. The inherited implementation will simply return the original name (what else could it do), whether you translate the owner type or not. But this obsolete override is harmless.

This simplifies your entire adapter to

ClassRemapper adapter = new ClassRemapper(classWriter, new Remapper()
{
    @Override
    public String map(String internalName)
    {
        return getNewName(internalName);
    }
});
Holger
  • 285,553
  • 42
  • 434
  • 765