From eaa61a1f81ab060fad7ec482c57a134b53559303 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 20 Apr 2016 15:02:39 +0200 Subject: [PATCH] [java-decompiler] skips illegal local variable names --- .../modules/decompiler/vars/VarProcessor.java | 8 +++-- .../java/decompiler/util/TextUtil.java | 32 +++++++++++++++++ .../java/decompiler/SingleClassesTest.java | 3 +- testData/classes/pkg/TestIllegalVarName.class | Bin 0 -> 1224 bytes testData/results/TestIllegalVarName.dec | 33 ++++++++++++++++++ testData/src/pkg/TestIllegalVarName.kt | 7 ++++ 6 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 testData/classes/pkg/TestIllegalVarName.class create mode 100644 testData/results/TestIllegalVarName.dec create mode 100644 testData/src/pkg/TestIllegalVarName.kt diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java index 2dc207e..2ba0490 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -21,6 +21,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.TextUtil; import java.util.*; import java.util.Map.Entry; @@ -64,8 +65,11 @@ public class VarProcessor { String name = mapVarNames.get(pair); Integer index = mapOriginalVarIndices.get(pair.var); - if (index != null && mapDebugVarNames.containsKey(index)) { - name = mapDebugVarNames.get(index); + if (index != null) { + String debugName = mapDebugVarNames.get(index); + if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getClassStruct().getBytecodeVersion())) { + name = debugName; + } } Integer counter = mapNames.get(name); diff --git a/src/org/jetbrains/java/decompiler/util/TextUtil.java b/src/org/jetbrains/java/decompiler/util/TextUtil.java index 8957fe1..21f10e4 100644 --- a/src/org/jetbrains/java/decompiler/util/TextUtil.java +++ b/src/org/jetbrains/java/decompiler/util/TextUtil.java @@ -15,13 +15,23 @@ */ package org.jetbrains.java.decompiler.util; +import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.ClassesProcessor; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import java.util.Arrays; +import java.util.HashSet; + public class TextUtil { + private static final HashSet KEYWORDS = new HashSet<>(Arrays.asList( + "abstract", "default", "if", "private", "this", "boolean", "do", "implements", "protected", "throw", "break", "double", "import", + "public", "throws", "byte", "else", "instanceof", "return", "transient", "case", "extends", "int", "short", "try", "catch", "final", + "interface", "static", "void", "char", "finally", "long", "strictfp", "volatile", "class", "float", "native", "super", "while", + "const", "for", "new", "switch", "continue", "goto", "package", "synchronized", "true", "false", "null", "assert")); + public static void writeQualifiedSuper(TextBuffer buf, String qualifier) { ClassesProcessor.ClassNode classNode = (ClassesProcessor.ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); if (!qualifier.equals(classNode.classStruct.qualifiedName)) { @@ -51,4 +61,26 @@ public class TextUtil { sTemp = ("0000" + sTemp).substring(sTemp.length()); return "\\u" + sTemp; } + + public static boolean isValidIdentifier(String id, int version) { + return isJavaIdentifier(id) && !isKeyword(id, version); + } + + private static boolean isJavaIdentifier(String id) { + if (id.isEmpty() || !Character.isJavaIdentifierStart(id.charAt(0))) { + return false; + } + + for (int i = 1; i < id.length(); i++) { + if (!Character.isJavaIdentifierPart(id.charAt(i))) { + return false; + } + } + + return true; + } + + private static boolean isKeyword(String id, int version) { + return KEYWORDS.contains(id) || version > CodeConstants.BYTECODE_JAVA_5 && "enum".equals(id); + } } \ No newline at end of file diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 5bdbdc5..dd509f6 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -86,6 +86,7 @@ public class SingleClassesTest { @Test public void testStaticNameClash() { doTest("pkg/TestStaticNameClash"); } @Test public void testExtendingSubclass() { doTest("pkg/TestExtendingSubclass"); } @Test public void testSyntheticAccess() { doTest("pkg/TestSyntheticAccess"); } + @Test public void testIllegalVarName() { doTest("pkg/TestIllegalVarName"); } private void doTest(String testFile, String... companionFiles) { ConsoleDecompiler decompiler = fixture.getDecompiler(); @@ -132,4 +133,4 @@ public class SingleClassesTest { return files; } -} +} \ No newline at end of file diff --git a/testData/classes/pkg/TestIllegalVarName.class b/testData/classes/pkg/TestIllegalVarName.class new file mode 100644 index 0000000000000000000000000000000000000000..5fd5ec6e5243567cc1230201037e04e6d262721b GIT binary patch literal 1224 zcmaJD z0>%)pS#8U-EvIVkmukE$BM33D2E$6aaCvtiJ>gVy`PD%m!(zeps%DMLl4l9WH!a6; zr6q++deN1|mTl)4hUAIxqlhxh*Ij7~$E>v*rf?+p9LqNIjw<1cvLAuLFjYR`<@#I8 zqecml_k%=N`a179H`vwcQ4BHMG;jkWlyu-oTJzVXP30>kXGy#gY~UYK^vf8&e0! zEVU`1PD*8qVXxPjF|?FsXz^ah(4tBi31rmhbyB@fR@-Dd;j@pr7T5NY@z9L6Ih57o z;XvvnBWIs<+7+#mWvWC{O)yj8Ww+53Huo}3yHynq!{p#%Z`36{W7n;4^+dPa-*{n_ zY<{Q?hS7p>c(K(eaWC-9zH2#BG_zHLA9Lj8Go;`(Fkd7gi>Z3{3K3^BBx0Dze8I V_Oa~afsb(?;l2{}vC>x_{stzQ9s2+P literal 0 HcmV?d00001 diff --git a/testData/results/TestIllegalVarName.dec b/testData/results/TestIllegalVarName.dec new file mode 100644 index 0000000..3eb1ea5 --- /dev/null +++ b/testData/results/TestIllegalVarName.dec @@ -0,0 +1,33 @@ +package pkg; + +import kotlin.Metadata; +import kotlin.jvm.internal.Intrinsics; +import org.jetbrains.annotations.NotNull; + +@Metadata( + mv = {1, 1, 0}, + bv = {1, 0, 0}, + k = 1, + d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\b\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0016\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00042\u0006\u0010\u0006\u001a\u00020\u0007¨\u0006\b"}, + d2 = {"Lpkg/TestIllegalVarName;", "", "()V", "m", "", "this", "enum", "", "java-decompiler-plugin"} +) +public final class TestIllegalVarName { + @NotNull + public final String m(@NotNull String var1, int var2) { + Intrinsics.checkParameterIsNotNull(var1, "this"); + return var1 + '/' + var2;// 5 + } +} + +class 'pkg/TestIllegalVarName' { + method 'm (Ljava/lang/String;I)Ljava/lang/String;' { + 1 16 + 3 16 + 11 17 + 1a 17 + 1d 17 + } +} + +Lines mapping: +5 <-> 18 diff --git a/testData/src/pkg/TestIllegalVarName.kt b/testData/src/pkg/TestIllegalVarName.kt new file mode 100644 index 0000000..cf47c6e --- /dev/null +++ b/testData/src/pkg/TestIllegalVarName.kt @@ -0,0 +1,7 @@ +package pkg + +class TestIllegalVarName { + fun m(`this`: String, `enum`: Int): String { + return `this` + '/' + `enum` + } +} \ No newline at end of file