[java-decompiler] IJ-CR-18502 Don't decompile common constant variables in own declaration

Prevents variables from referencing themselves, for example: `public static final int MIN_VALUE = MIN_VALUE` in `java.lang.Integer`.

GitOrigin-RevId: b415924b4bb752f5b47e5191d6b65d9416f88117
This commit is contained in:
Bart van Helvert
2021-12-13 15:43:46 +00:00
committed by intellij-monorepo-bot
parent a415b77d56
commit 7cfa69cd39
6 changed files with 1727 additions and 43 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.java.decompiler.main;
import org.jetbrains.java.decompiler.code.CodeConstants;
@@ -614,7 +614,7 @@ public class ClassWriter {
if (attr != null) {
PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex());
buffer.append(" = ");
buffer.append(new ConstExprent(fieldType, constant.value, null).toJava(indent, tracer));
buffer.append(new ConstExprent(fieldType, constant.value, null, fd).toJava(indent, tracer));
}
}
@@ -2,10 +2,13 @@
package org.jetbrains.java.decompiler.modules.decompiler.exps;
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.collectors.BytecodeMappingTracer;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.StructMember;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
@@ -22,6 +25,21 @@ import java.util.Map.Entry;
import java.util.Set;
public class ConstExprent extends Exprent {
private static final String SHORT_SIG = "java/lang/Short";
private static final String INT_SIG = "java/lang/Integer";
private static final String LONG_SIG = "java/lang/Long";
private static final String FLOAT_SIG = "java/lang/Float";
private static final String DOUBLE_SIG = "java/lang/Double";
private static final String MATH_SIG = "java/lang/Math";
private static final String MIN_VAL = "MIN_VALUE";
private static final String MAX_VAL = "MAX_VALUE";
private static final String POS_INF = "POSITIVE_INFINITY";
private static final String NEG_INF = "NEGATIVE_INFINITY";
private static final String NAN = "NaN";
private static final String MIN_NORM = "MIN_NORMAL";
private static final String E = "E";
private static final String PI = "PI";
private static final Map<Integer, String> CHAR_ESCAPES = Map.of(
0x8, "\\b", /* \u0008: backspace BS */
0x9, "\\t", /* \u0009: horizontal tab HT */
@@ -32,6 +50,7 @@ public class ConstExprent extends Exprent {
0x5C, "\\\\" /* \u005c: backslash \ */
);
private StructMember parent;
private VarType constType;
private final Object value;
private final boolean boolPermitted;
@@ -44,6 +63,11 @@ public class ConstExprent extends Exprent {
this(constType, value, false, bytecodeOffsets);
}
public ConstExprent(VarType constType, Object value, Set<Integer> bytecodeOffsets, StructMember parent) {
this(constType, value, bytecodeOffsets);
this.parent = parent;
}
private ConstExprent(VarType constType, Object value, boolean boolPermitted, Set<Integer> bytecodeOffsets) {
super(EXPRENT_CONST);
this.constType = constType;
@@ -135,11 +159,11 @@ public class ConstExprent extends Exprent {
case CodeConstants.TYPE_SHORT:
int shortVal = (Integer)value;
if (!literal) {
if (shortVal == Short.MAX_VALUE) {
return new FieldExprent("MAX_VALUE", "java/lang/Short", true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);
if (shortVal == Short.MAX_VALUE && !inConstantVariable(SHORT_SIG, MAX_VAL)) {
return new FieldExprent(MAX_VAL, SHORT_SIG, true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (shortVal == Short.MIN_VALUE) {
return new FieldExprent("MIN_VALUE", "java/lang/Short", true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (shortVal == Short.MIN_VALUE && !inConstantVariable(SHORT_SIG, MIN_VAL)) {
return new FieldExprent(MIN_VAL, SHORT_SIG, true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
}
return new TextBuffer(value.toString());
@@ -147,11 +171,11 @@ public class ConstExprent extends Exprent {
case CodeConstants.TYPE_INT:
int intVal = (Integer)value;
if (!literal) {
if (intVal == Integer.MAX_VALUE) {
return new FieldExprent("MAX_VALUE", "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);
if (intVal == Integer.MAX_VALUE && !inConstantVariable(INT_SIG, MAX_VAL)) {
return new FieldExprent(MAX_VAL, INT_SIG, true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (intVal == Integer.MIN_VALUE) {
return new FieldExprent("MIN_VALUE", "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (intVal == Integer.MIN_VALUE && !inConstantVariable(INT_SIG, MIN_VAL)) {
return new FieldExprent(MIN_VAL, INT_SIG, true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);
}
}
return new TextBuffer(value.toString());
@@ -159,11 +183,11 @@ public class ConstExprent extends Exprent {
case CodeConstants.TYPE_LONG:
long longVal = (Long)value;
if (!literal) {
if (longVal == Long.MAX_VALUE) {
return new FieldExprent("MAX_VALUE", "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);
if (longVal == Long.MAX_VALUE && !inConstantVariable(LONG_SIG, MAX_VAL)) {
return new FieldExprent(MAX_VAL, LONG_SIG, true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (longVal == Long.MIN_VALUE) {
return new FieldExprent("MIN_VALUE", "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (longVal == Long.MIN_VALUE && !inConstantVariable(LONG_SIG, MIN_VAL)) {
return new FieldExprent(MIN_VAL, LONG_SIG, true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);
}
}
return new TextBuffer(value.toString()).append('L');
@@ -171,23 +195,23 @@ public class ConstExprent extends Exprent {
case CodeConstants.TYPE_FLOAT:
float floatVal = (Float)value;
if (!literal) {
if (Float.isNaN(floatVal)) {
return new FieldExprent("NaN", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
if (Float.isNaN(floatVal) && !inConstantVariable(FLOAT_SIG, NAN)) {
return new FieldExprent(NAN, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (floatVal == Float.POSITIVE_INFINITY) {
return new FieldExprent("POSITIVE_INFINITY", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (floatVal == Float.POSITIVE_INFINITY && !inConstantVariable(FLOAT_SIG, POS_INF)) {
return new FieldExprent(POS_INF, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (floatVal == Float.NEGATIVE_INFINITY) {
return new FieldExprent("NEGATIVE_INFINITY", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (floatVal == Float.NEGATIVE_INFINITY && !inConstantVariable(FLOAT_SIG, NEG_INF)) {
return new FieldExprent(NEG_INF, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (floatVal == Float.MAX_VALUE) {
return new FieldExprent("MAX_VALUE", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (floatVal == Float.MAX_VALUE && !inConstantVariable(FLOAT_SIG, MAX_VAL)) {
return new FieldExprent(MAX_VAL, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (floatVal == Float.MIN_VALUE) {
return new FieldExprent("MIN_VALUE", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (floatVal == Float.MIN_VALUE && !inConstantVariable(FLOAT_SIG, MIN_VAL)) {
return new FieldExprent(MIN_VAL, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (floatVal == Float.MIN_NORMAL) {
return new FieldExprent("MIN_NORMAL", "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (floatVal == Float.MIN_NORMAL && !inConstantVariable(FLOAT_SIG, MIN_NORM)) {
return new FieldExprent(MIN_NORM, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);
}
}
else if (Float.isNaN(floatVal)) {
@@ -204,29 +228,29 @@ public class ConstExprent extends Exprent {
case CodeConstants.TYPE_DOUBLE:
double doubleVal = (Double)value;
if (!literal) {
if (Double.isNaN(doubleVal)) {
return new FieldExprent("NaN", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
if (Double.isNaN(doubleVal) && !inConstantVariable(DOUBLE_SIG, NAN)) {
return new FieldExprent(NAN, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Double.POSITIVE_INFINITY) {
return new FieldExprent("POSITIVE_INFINITY", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Double.POSITIVE_INFINITY && !inConstantVariable(DOUBLE_SIG, POS_INF)) {
return new FieldExprent(POS_INF, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Double.NEGATIVE_INFINITY) {
return new FieldExprent("NEGATIVE_INFINITY", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Double.NEGATIVE_INFINITY && !inConstantVariable(DOUBLE_SIG, NEG_INF)) {
return new FieldExprent(NEG_INF, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Double.MAX_VALUE) {
return new FieldExprent("MAX_VALUE", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Double.MAX_VALUE && !inConstantVariable(DOUBLE_SIG, MAX_VAL)) {
return new FieldExprent(MAX_VAL, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Double.MIN_VALUE) {
return new FieldExprent("MIN_VALUE", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Double.MIN_VALUE && !inConstantVariable(DOUBLE_SIG, MIN_VAL)) {
return new FieldExprent(MIN_VAL, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Double.MIN_NORMAL) {
return new FieldExprent("MIN_NORMAL", "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Double.MIN_NORMAL && !inConstantVariable(DOUBLE_SIG, MIN_NORM)) {
return new FieldExprent(MIN_NORM, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Math.E) {
return new FieldExprent("E", "java/lang/Math", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Math.E && !inConstantVariable(MATH_SIG, E)) {
return new FieldExprent(E, MATH_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
else if (doubleVal == Math.PI) {
return new FieldExprent("PI", "java/lang/Math", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
else if (doubleVal == Math.PI && !inConstantVariable(MATH_SIG, PI)) {
return new FieldExprent(PI, MATH_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);
}
}
else if (Double.isNaN(doubleVal)) {
@@ -257,6 +281,13 @@ public class ConstExprent extends Exprent {
throw new RuntimeException("invalid constant type: " + constType);
}
private boolean inConstantVariable(String classSignature, String variableName) {
ClassesProcessor.ClassNode node = (ClassesProcessor.ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
return node.classStruct.qualifiedName.equals(classSignature) &&
parent instanceof StructField &&
((StructField)parent).getName().equals(variableName);
}
public boolean isNull() {
return CodeConstants.TYPE_NULL == constType.type;
}
@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.java.decompiler;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@@ -65,6 +65,10 @@ public class SingleClassesTest {
DecompilerContext.setProperty(IFernflowerPreferences.LITERALS_AS_IS, "0");
doTest("pkg/TestConstants");
}
@Test public void testInteger() {
DecompilerContext.setProperty(IFernflowerPreferences.LITERALS_AS_IS, "0");
doTest("java/lang/Integer");
}
@Test public void testEnum() { doTest("pkg/TestEnum"); }
@Test public void testDebugSymbols() { doTest("pkg/TestDebugSymbols"); }
@Test public void testInvalidMethodSignature() { doTest("InvalidMethodSignature"); }
File diff suppressed because it is too large Load Diff