From 3bbb6b1058b81828eb83aa05333a2cea81526c27 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 21 Dec 2020 14:38:01 +0000 Subject: [PATCH] fix: rename all related overridden methods in deobf map file (#1058) --- .../java/jadx/core/deobf/DeobfPresets.java | 123 +++++++++--------- .../java/jadx/core/deobf/Deobfuscator.java | 56 +++++++- .../dex/visitors/OverrideMethodVisitor.java | 49 +++++-- .../jadx/core/dex/visitors/RenameVisitor.java | 8 +- .../deobf/TestRenameOverriddenMethod2.java | 53 ++++++++ .../main/java/jadx/gui/ui/RenameDialog.java | 104 +++------------ 6 files changed, 223 insertions(+), 170 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod2.java diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java index 8bcc3251b..f88a75d84 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java @@ -1,38 +1,62 @@ package jadx.core.deobf; +import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.files.FileUtils; import static java.nio.charset.StandardCharsets.UTF_8; -class DeobfPresets { +public class DeobfPresets { private static final Logger LOG = LoggerFactory.getLogger(DeobfPresets.class); private static final Charset MAP_FILE_CHARSET = UTF_8; - private final Deobfuscator deobfuscator; private final Path deobfMapFile; + private final Map pkgPresetMap = new HashMap<>(); private final Map clsPresetMap = new HashMap<>(); private final Map fldPresetMap = new HashMap<>(); private final Map mthPresetMap = new HashMap<>(); - public DeobfPresets(Deobfuscator deobfuscator, Path deobfMapFile) { - this.deobfuscator = deobfuscator; + @Nullable + public static DeobfPresets build(RootNode root) { + Path deobfMapPath = getPathDeobfMapPath(root); + if (deobfMapPath == null) { + return null; + } + return new DeobfPresets(deobfMapPath); + } + + @Nullable + private static Path getPathDeobfMapPath(RootNode root) { + List inputFiles = root.getArgs().getInputFiles(); + if (inputFiles.isEmpty()) { + return null; + } + Path inputFilePath = inputFiles.get(0).getAbsoluteFile().toPath(); + String baseName = FileUtils.getPathBaseName(inputFilePath); + return inputFilePath.getParent().resolve(baseName + ".jobf"); + } + + private DeobfPresets(Path deobfMapFile) { this.deobfMapFile = deobfMapFile; } @@ -57,17 +81,22 @@ class DeobfPresets { } String origName = va[0]; String alias = va[1]; - if (l.startsWith("p ")) { - deobfuscator.addPackagePreset(origName, alias); - } else if (l.startsWith("c ")) { - clsPresetMap.put(origName, alias); - } else if (l.startsWith("f ")) { - fldPresetMap.put(origName, alias); - } else if (l.startsWith("m ")) { - mthPresetMap.put(origName, alias); + switch (l.charAt(0)) { + case 'p': + pkgPresetMap.put(origName, alias); + break; + case 'c': + clsPresetMap.put(origName, alias); + break; + case 'f': + fldPresetMap.put(origName, alias); + break; + case 'm': + mthPresetMap.put(origName, alias); + break; } } - } catch (IOException e) { + } catch (Exception e) { LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.toAbsolutePath(), e); } } @@ -80,66 +109,28 @@ class DeobfPresets { return v; } - public void save(boolean forceSave) { - try { - if (Files.exists(deobfMapFile)) { - if (forceSave) { - dumpMapping(); - } else { - LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", - deobfMapFile.toAbsolutePath()); - } - } else { - dumpMapping(); - } - } catch (IOException e) { - LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.toAbsolutePath(), e); - } - } - - /** - * Saves DefaultDeobfuscator presets - */ - private void dumpMapping() throws IOException { + public void save() throws IOException { List list = new ArrayList<>(); - // packages - for (PackageNode p : deobfuscator.getRootPackage().getInnerPackages()) { - for (PackageNode pp : p.getInnerPackages()) { - dfsPackageName(list, p.getName(), pp); - } - if (p.hasAlias()) { - list.add(String.format("p %s = %s", p.getName(), p.getAlias())); - } + for (Map.Entry pkgEntry : pkgPresetMap.entrySet()) { + list.add(String.format("p %s = %s", pkgEntry.getKey(), pkgEntry.getValue())); } - // classes - for (DeobfClsInfo deobfClsInfo : deobfuscator.getClsMap().values()) { - if (deobfClsInfo.getAlias() != null) { - list.add(String.format("c %s = %s", - deobfClsInfo.getCls().getClassInfo().makeRawFullName(), deobfClsInfo.getAlias())); - } + for (Map.Entry clsEntry : clsPresetMap.entrySet()) { + list.add(String.format("c %s = %s", clsEntry.getKey(), clsEntry.getValue())); } - for (FieldInfo fld : deobfuscator.getFldMap().keySet()) { - list.add(String.format("f %s = %s", fld.getRawFullId(), fld.getAlias())); + for (Map.Entry fldEntry : fldPresetMap.entrySet()) { + list.add(String.format("f %s = %s", fldEntry.getKey(), fldEntry.getValue())); } - for (MethodInfo mth : deobfuscator.getMthMap().keySet()) { - list.add(String.format("m %s = %s", mth.getRawFullId(), mth.getAlias())); + for (Map.Entry mthEntry : mthPresetMap.entrySet()) { + list.add(String.format("m %s = %s", mthEntry.getKey(), mthEntry.getValue())); } Collections.sort(list); - Files.write(deobfMapFile, list, MAP_FILE_CHARSET); + Files.write(deobfMapFile, list, MAP_FILE_CHARSET, + StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); if (LOG.isDebugEnabled()) { LOG.debug("Deobfuscation map file saved as: {}", deobfMapFile); } } - private static void dfsPackageName(List list, String prefix, PackageNode node) { - for (PackageNode pp : node.getInnerPackages()) { - dfsPackageName(list, prefix + '.' + node.getName(), pp); - } - if (node.hasAlias()) { - list.add(String.format("p %s.%s = %s", prefix, node.getName(), node.getAlias())); - } - } - public String getForCls(ClassInfo cls) { return clsPresetMap.get(cls.makeRawFullName()); } @@ -158,6 +149,14 @@ class DeobfPresets { mthPresetMap.clear(); } + public Path getDeobfMapFile() { + return deobfMapFile; + } + + public Map getPkgPresetMap() { + return pkgPresetMap; + } + public Map getClsPresetMap() { return clsPresetMap; } diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 2efc962a5..843f73ae5 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -1,5 +1,6 @@ package jadx.core.deobf; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; @@ -58,28 +59,75 @@ public class Deobfuscator { private int fldIndex = 0; private int mthIndex = 0; - public Deobfuscator(JadxArgs args, RootNode root, Path deobfMapFile) { - this.args = args; + public Deobfuscator(RootNode root) { this.root = root; + this.args = root.getArgs(); this.minLength = args.getDeobfuscationMinLength(); this.maxLength = args.getDeobfuscationMaxLength(); this.useSourceNameAsAlias = args.isUseSourceNameAsClassAlias(); this.parseKotlinMetadata = args.isParseKotlinMetadata(); - this.deobfPresets = new DeobfPresets(this, deobfMapFile); + this.deobfPresets = DeobfPresets.build(root); } public void execute() { if (!args.isDeobfuscationForceSave()) { deobfPresets.load(); + for (Map.Entry pkgEntry : deobfPresets.getPkgPresetMap().entrySet()) { + addPackagePreset(pkgEntry.getKey(), pkgEntry.getValue()); + } + deobfPresets.getPkgPresetMap().clear(); // not needed anymore initIndexes(); } process(); } public void savePresets() { - deobfPresets.save(args.isDeobfuscationForceSave()); + Path deobfMapFile = deobfPresets.getDeobfMapFile(); + if (Files.exists(deobfMapFile) && !args.isDeobfuscationForceSave()) { + LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", + deobfMapFile.toAbsolutePath()); + return; + } + try { + deobfPresets.clear(); + fillDeobfPresets(); + deobfPresets.save(); + } catch (Exception e) { + LOG.error("Failed to save deobfuscation map file '{}'", deobfMapFile.toAbsolutePath(), e); + } + } + + private void fillDeobfPresets() { + for (PackageNode p : getRootPackage().getInnerPackages()) { + for (PackageNode pp : p.getInnerPackages()) { + dfsPackageName(p.getName(), pp); + } + if (p.hasAlias()) { + deobfPresets.getPkgPresetMap().put(p.getName(), p.getAlias()); + } + } + for (DeobfClsInfo deobfClsInfo : clsMap.values()) { + if (deobfClsInfo.getAlias() != null) { + deobfPresets.getClsPresetMap().put(deobfClsInfo.getCls().getClassInfo().makeRawFullName(), deobfClsInfo.getAlias()); + } + } + for (FieldInfo fld : fldMap.keySet()) { + deobfPresets.getFldPresetMap().put(fld.getRawFullId(), fld.getAlias()); + } + for (MethodInfo mth : mthMap.keySet()) { + deobfPresets.getMthPresetMap().put(mth.getRawFullId(), mth.getAlias()); + } + } + + private void dfsPackageName(String prefix, PackageNode node) { + for (PackageNode pp : node.getInnerPackages()) { + dfsPackageName(prefix + '.' + node.getName(), pp); + } + if (node.hasAlias()) { + deobfPresets.getPkgPresetMap().put(node.getName(), node.getAlias()); + } } public void clear() { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java index 50eec1d4c..30c59c29d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java @@ -2,10 +2,12 @@ package jadx.core.dex.visitors; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; @@ -74,16 +76,12 @@ public class OverrideMethodVisitor extends AbstractVisitor { for (ArgType superType : superTypes) { ClassNode classNode = cls.root().resolveClass(superType); if (classNode != null) { - for (MethodNode supMth : classNode.getMethods()) { - String mthShortId = supMth.getMethodInfo().getShortId(); - if (!supMth.getAccessFlags().isStatic() - && mthShortId.startsWith(signature) - && isMethodVisibleInCls(supMth, cls)) { - overrideList.add(supMth); - MethodOverrideAttr attr = supMth.get(AType.METHOD_OVERRIDE); - if (attr != null) { - return buildOverrideAttr(mth, overrideList, attr); - } + MethodNode ovrdMth = searchOverriddenMethod(classNode, signature); + if (ovrdMth != null && isMethodVisibleInCls(ovrdMth, cls)) { + overrideList.add(ovrdMth); + MethodOverrideAttr attr = ovrdMth.get(AType.METHOD_OVERRIDE); + if (attr != null) { + return buildOverrideAttr(mth, overrideList, attr); } } } else { @@ -102,25 +100,50 @@ public class OverrideMethodVisitor extends AbstractVisitor { return buildOverrideAttr(mth, overrideList, null); } + @Nullable + private MethodNode searchOverriddenMethod(ClassNode cls, String signature) { + for (MethodNode supMth : cls.getMethods()) { + if (!supMth.getAccessFlags().isStatic() && supMth.getMethodInfo().getShortId().startsWith(signature)) { + return supMth; + } + } + return null; + } + @Nullable private MethodOverrideAttr buildOverrideAttr(MethodNode mth, List overrideList, @Nullable MethodOverrideAttr attr) { if (overrideList.isEmpty() && attr == null) { return null; } - List cleanOverrideList = overrideList.stream().distinct().collect(Collectors.toList()); if (attr == null) { // traced to base method + List cleanOverrideList = overrideList.stream().distinct().collect(Collectors.toList()); return applyOverrideAttr(mth, cleanOverrideList, false); } // trace stopped at already processed method -> start merging - List mergedOverrideList = Utils.mergeLists(cleanOverrideList, attr.getOverrideList()); - return applyOverrideAttr(mth, mergedOverrideList, true); + List mergedOverrideList = Utils.mergeLists(overrideList, attr.getOverrideList()); + List cleanOverrideList = mergedOverrideList.stream().distinct().collect(Collectors.toList()); + return applyOverrideAttr(mth, cleanOverrideList, true); } private MethodOverrideAttr applyOverrideAttr(MethodNode mth, List overrideList, boolean update) { // don't rename method if override list contains not resolved method boolean dontRename = overrideList.stream().anyMatch(m -> !(m instanceof MethodNode)); List mthNodes = getMethodNodes(mth, overrideList); + if (update) { + // merge related methods from all override attributes + Set relatedMthSet = new HashSet<>(mthNodes); + for (MethodNode mthNode : mthNodes) { + MethodOverrideAttr ovrdAttr = mthNode.get(AType.METHOD_OVERRIDE); + if (ovrdAttr != null) { + relatedMthSet.addAll(ovrdAttr.getRelatedMthNodes()); + } + } + if (relatedMthSet.size() != mthNodes.size()) { + mthNodes = new ArrayList<>(relatedMthSet); + Collections.sort(mthNodes); + } + } int depth = 0; for (MethodNode mthNode : mthNodes) { if (dontRename) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java index 8ca47b186..478deaa83 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -1,7 +1,6 @@ package jadx.core.dex.visitors; import java.io.File; -import java.nio.file.Path; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -23,7 +22,6 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.files.FileUtils; public class RenameVisitor extends AbstractVisitor { @@ -33,12 +31,8 @@ public class RenameVisitor extends AbstractVisitor { if (inputFiles.isEmpty()) { return; } - Path inputFilePath = inputFiles.get(0).getAbsoluteFile().toPath(); - String baseName = FileUtils.getPathBaseName(inputFilePath); - Path deobfMapPath = inputFilePath.getParent().resolve(baseName + ".jobf"); - + Deobfuscator deobfuscator = new Deobfuscator(root); JadxArgs args = root.getArgs(); - Deobfuscator deobfuscator = new Deobfuscator(args, root, deobfMapPath); if (args.isDeobfuscationOn()) { deobfuscator.execute(); } diff --git a/jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod2.java b/jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod2.java new file mode 100644 index 000000000..7c8157699 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/deobf/TestRenameOverriddenMethod2.java @@ -0,0 +1,53 @@ +package jadx.tests.integration.deobf; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.attributes.AType; +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestRenameOverriddenMethod2 extends IntegrationTest { + + public static class TestCls { + + public interface I { + int call(); + } + + public static class A implements I { + @Override + public int call() { + return 1; + } + } + + public static class B implements I { + @Override + public int call() { + return 2; + } + } + } + + @Test + public void test() { + enableDeobfuscation(); + args.setDeobfuscationMinLength(100); // rename everything + + ClassNode cls = getClassNode(TestCls.class); + assertThat(cls) + .code() + .countString(2, "@Override") + .countString(3, "int mo0call()"); + + assertThat(searchCls(cls.getInnerClasses(), "I")).isNotNull() + .extracting(c -> c.searchMethodByShortName("call")).isNotNull() + .extracting(m -> m.get(AType.METHOD_OVERRIDE)).isNotNull() + .satisfies(ovrdAttr -> { + assertThat(ovrdAttr.getRelatedMthNodes()).hasSize(3); + assertThat(ovrdAttr.getOverrideList()).isEmpty(); + }); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java index ae71d1b4f..62e1e9522 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java @@ -4,13 +4,6 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.io.File; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -39,6 +32,7 @@ import jadx.api.JavaField; import jadx.api.JavaMethod; import jadx.api.JavaNode; import jadx.core.codegen.CodeWriter; +import jadx.core.deobf.DeobfPresets; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; import jadx.core.dex.nodes.MethodNode; @@ -117,85 +111,25 @@ public class RenameDialog extends JDialog { return false; // TODO: can't open dialog, 'node' is replaced with new one after reopen } - private Path getDeobfMapPath(RootNode root) { - List inputFiles = root.getArgs().getInputFiles(); - if (inputFiles.isEmpty()) { - return null; - } - File firstInputFile = inputFiles.get(0); - Path inputFilePath = firstInputFile.getAbsoluteFile().toPath(); - - String inputName = inputFilePath.getFileName().toString(); - String baseName = inputName.substring(0, inputName.lastIndexOf('.')); - return inputFilePath.getParent().resolve(baseName + ".jobf"); - } - - private String getNodeAlias(String renameText) { - String type = ""; - String id = ""; + private void updateDeobfMap(DeobfPresets deobfPresets, String renameText) { if (node instanceof JMethod) { - JavaMethod javaMethod = (JavaMethod) node.getJavaNode(); - type = "m"; - MethodNode mthNode = javaMethod.getMethodNode(); + MethodNode mthNode = ((JavaMethod) node.getJavaNode()).getMethodNode(); MethodOverrideAttr overrideAttr = mthNode.get(AType.METHOD_OVERRIDE); if (overrideAttr != null) { - // use method closest to base method - mthNode = Objects.requireNonNull(Utils.last(overrideAttr.getRelatedMthNodes())); + for (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) { + deobfPresets.getMthPresetMap().put(relatedMth.getMethodInfo().getRawFullId(), renameText); + } } - id = mthNode.getMethodInfo().getRawFullId(); + deobfPresets.getMthPresetMap().put(mthNode.getMethodInfo().getRawFullId(), renameText); } else if (node instanceof JField) { JavaField javaField = (JavaField) node.getJavaNode(); - type = "f"; - id = javaField.getFieldNode().getFieldInfo().getRawFullId(); + deobfPresets.getFldPresetMap().put(javaField.getFieldNode().getFieldInfo().getRawFullId(), renameText); } else if (node instanceof JClass) { JavaClass javaClass = (JavaClass) node.getJavaNode(); - type = "c"; - id = javaClass.getRawName(); + deobfPresets.getClsPresetMap().put(javaClass.getRawName(), renameText); } else if (node instanceof JPackage) { - type = "p"; - id = ((JPackage) node).getFullName(); + deobfPresets.getPkgPresetMap().put(((JPackage) node).getFullName(), renameText); } - return String.format("%s %s = %s", type, id, renameText); - } - - private void writeDeobfMapFile(Path deobfMapPath, List deobfMap) throws IOException { - if (deobfMapPath == null) { - LOG.error("updateDeobfMapFile(): deobfMapPath is null!"); - return; - } - Path deobfMapDir = deobfMapPath.getParent(); - Path tmpFile = Files.createTempFile(deobfMapDir, "deobf_tmp_", ".txt"); - - try (Writer writer = Files.newBufferedWriter(tmpFile, StandardCharsets.UTF_8)) { - for (String entry : deobfMap) { - writer.write(entry); - writer.write(System.lineSeparator()); - } - } - Files.move(tmpFile, deobfMapPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - LOG.info("Updated deobf file {}", deobfMapPath); - } - - @NotNull - private List readDeobfMap(Path deobfMapPath) throws IOException { - return Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8); - } - - private List updateDeobfMap(List deobfMap, String alias) { - String id = alias.substring(0, alias.indexOf('=') + 1); - int i = 0; - while (i < deobfMap.size()) { - String entry = deobfMap.get(i); - if (entry.startsWith(id)) { - LOG.debug("updateDeobfMap(): Removing entry {}", entry); - deobfMap.remove(i); - } else { - i++; - } - } - LOG.debug("updateDeobfMap(): placing alias = {}", alias); - deobfMap.add(alias); - return deobfMap; } private void rename() { @@ -223,18 +157,20 @@ public class RenameDialog extends JDialog { } private boolean refreshDeobfMapFile(String renameText, RootNode root) { - List deobfMap; - Path deobfMapPath = getDeobfMapPath(root); + DeobfPresets deobfPresets = DeobfPresets.build(root); + if (deobfPresets == null) { + return false; + } try { - deobfMap = readDeobfMap(deobfMapPath); - } catch (IOException e) { + deobfPresets.load(); + } catch (Exception e) { LOG.error("rename(): readDeobfMap() failed"); return false; } - updateDeobfMap(deobfMap, getNodeAlias(renameText)); + updateDeobfMap(deobfPresets, renameText); try { - writeDeobfMapFile(deobfMapPath, deobfMap); - } catch (IOException e) { + deobfPresets.save(); + } catch (Exception e) { LOG.error("rename(): writeDeobfMap() failed"); return false; } @@ -301,7 +237,7 @@ public class RenameDialog extends JDialog { cls.reload(); IndexJob.refreshIndex(cache, cls.getCls()); } catch (Exception e) { - LOG.error("Failed to reload class: {}", cls, e); + LOG.error("Failed to reload class: {}", cls.getFullName(), e); } }