diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index fdd8bb9..a4e25c9 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,14 +21,19 @@ import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute; import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericMain; import org.jetbrains.java.decompiler.struct.match.MatchEngine; import org.jetbrains.java.decompiler.struct.match.MatchNode; -import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties; import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue; import org.jetbrains.java.decompiler.util.InterpreterUtil; @@ -101,7 +106,29 @@ public class VarExprent extends Exprent { if (processor != null && processor.getVarFinal(new VarVersionPair(index, version)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } - buffer.append(ExprProcessor.getCastTypeName(getVarType())).append(" "); + boolean generic = false; + if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { + MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + StructLocalVariableTypeTableAttribute attr = (StructLocalVariableTypeTableAttribute)method.methodStruct.getAttributes() + .getWithKey(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE); + if (attr != null && processor != null) { + Integer index = processor.getVarOriginalIndex(new VarVersionPair(this.index, version)); + if (index != null) { + String signature = attr.getMapVarSignatures().get(index); + if (signature != null) { + GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature); + if (descriptor != null) { + buffer.append(GenericMain.getGenericCastTypeName(descriptor.type)); + generic = true; + } + } + } + } + } + if (!generic) { + buffer.append(ExprProcessor.getCastTypeName(getVarType())); + } + buffer.append(" "); } buffer.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name); } 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 b15fed5..fa79849 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -83,6 +83,14 @@ public class VarProcessor { } } + public Integer getVarOriginalIndex(VarVersionPair pair) { + if (varVersions == null) { + return null; + } + + return varVersions.getMapOriginalVarIndices().get(pair.var); + } + public void refreshVarNames(VarNamesCollector vc) { Map tempVarNames = new HashMap<>(mapVarNames); for (Entry ent : tempVarNames.entrySet()) { diff --git a/src/org/jetbrains/java/decompiler/struct/StructMember.java b/src/org/jetbrains/java/decompiler/struct/StructMember.java index 3bc93a1..6f6d09e 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMember.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMember.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.VBStyleCollection; @@ -62,6 +63,11 @@ public class StructMember { StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.getWithKey(name); table.addLocalVariableTable((StructLocalVariableTableAttribute)attribute); } + else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.equals(name) && attributes.containsKey(name)) { + // merge all variable tables + StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.getWithKey(name); + table.add((StructLocalVariableTypeTableAttribute)attribute); + } else { attributes.addWithKey(attribute, attribute.getName()); } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 73cc535..98a4bae 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ public class StructGeneralAttribute { public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; + public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; public static final String ATTRIBUTE_SYNTHETIC = "Synthetic"; @@ -84,6 +85,9 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(name)) { attr = new StructLocalVariableTableAttribute(); } + else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.equals(name)) { + attr = new StructLocalVariableTypeTableAttribute(); + } else if (ATTRIBUTE_BOOTSTRAP_METHODS.equals(name)) { attr = new StructBootstrapMethodsAttribute(); } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java new file mode 100644 index 0000000..6fb2f4b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.util.DataInputFullStream; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/* + u2 local_variable_type_table_length; + { u2 start_pc; + u2 length; + u2 name_index; + u2 signature_index; + u2 index; + } local_variable_type_table[local_variable_type_table_length]; +*/ +public class StructLocalVariableTypeTableAttribute extends StructGeneralAttribute { + + private Map mapVarSignatures = Collections.emptyMap(); + + @Override + public void initContent(ConstantPool pool) throws IOException { + DataInputFullStream data = stream(); + + int len = data.readUnsignedShort(); + if (len > 0) { + mapVarSignatures = new HashMap<>(len); + for (int i = 0; i < len; i++) { + data.discard(6); + int signatureIndex = data.readUnsignedShort(); + int varIndex = data.readUnsignedShort(); + mapVarSignatures.put(varIndex, pool.getPrimitiveConstant(signatureIndex).getString()); + } + } + else { + mapVarSignatures = Collections.emptyMap(); + } + } + + public void add(StructLocalVariableTypeTableAttribute attr) { + mapVarSignatures.putAll(attr.getMapVarSignatures()); + } + + public Map getMapVarSignatures() { + return mapVarSignatures; + } +} diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index e7ede30..993b9c4 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -77,6 +77,7 @@ public class SingleClassesTest { @Test public void testInnerLocalPkg() { doTest("pkg/TestInnerLocalPkg"); } @Test public void testInnerSignature() { doTest("pkg/TestInnerSignature"); } @Test public void testAnonymousSignature() { doTest("pkg/TestAnonymousSignature"); } + @Test public void testLocalsSignature() { doTest("pkg/TestLocalsSignature"); } @Test public void testParameterizedTypes() { doTest("pkg/TestParameterizedTypes"); } @Test public void testShadowing() { doTest("pkg/TestShadowing", "pkg/Shadow", "ext/Shadow"); } @Test public void testStringConcat() { doTest("pkg/TestStringConcat"); } diff --git a/testData/classes/pkg/TestLocalsSignature.class b/testData/classes/pkg/TestLocalsSignature.class new file mode 100644 index 0000000..8f6b2fb Binary files /dev/null and b/testData/classes/pkg/TestLocalsSignature.class differ diff --git a/testData/results/TestLocalsSignature.dec b/testData/results/TestLocalsSignature.dec new file mode 100644 index 0000000..2948bfd --- /dev/null +++ b/testData/results/TestLocalsSignature.dec @@ -0,0 +1,25 @@ +package pkg; + +import java.util.ArrayList; +import java.util.List; + +public class TestLocalsSignature { + public static void main(String[] args) { + List s = new ArrayList();// 24 + s.add("xxx");// 25 + }// 26 +} + +class 'pkg/TestLocalsSignature' { + method 'main ([Ljava/lang/String;)V' { + 7 7 + 9 8 + b 8 + 11 9 + } +} + +Lines mapping: +24 <-> 8 +25 <-> 9 +26 <-> 10 diff --git a/testData/src/pkg/TestLocalsSignature.java b/testData/src/pkg/TestLocalsSignature.java new file mode 100644 index 0000000..9a52d54 --- /dev/null +++ b/testData/src/pkg/TestLocalsSignature.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pkg; + +import java.util.ArrayList; +import java.util.List; + +public class TestLocalsSignature { + public static void main(String[] args) { + List s = new ArrayList(); + s.add("xxx"); + } +} \ No newline at end of file