fix: correct hex format for negative numbers (#2531)
This commit is contained in:
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.args.IntegerFormat;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class StringUtils {
|
||||
private static final StringUtils DEFAULT_INSTANCE = new StringUtils(new JadxArgs());
|
||||
@@ -386,58 +387,90 @@ public class StringUtils {
|
||||
return new SimpleDateFormat("HH:mm:ss").format(new Date());
|
||||
}
|
||||
|
||||
private String toFormatString(long l) {
|
||||
private String formatNumber(long number, int bytesLen, boolean cast) {
|
||||
String numStr;
|
||||
if (integerFormat.isHexadecimal()) {
|
||||
return "0x" + Long.toHexString(l);
|
||||
String hexStr = Long.toHexString(number);
|
||||
if (number < 0) {
|
||||
// cut leading 'f' for negative numbers to match number type length
|
||||
int len = hexStr.length();
|
||||
numStr = "0x" + hexStr.substring(len - bytesLen * 2, len);
|
||||
// force cast, because unsigned negative numbers are bigger
|
||||
// than signed max value allowed by compiler
|
||||
cast = true;
|
||||
} else {
|
||||
numStr = "0x" + hexStr;
|
||||
}
|
||||
} else {
|
||||
numStr = Long.toString(number);
|
||||
}
|
||||
return Long.toString(l);
|
||||
if (bytesLen == 8 && (number == Long.MIN_VALUE || Math.abs(number) >= Integer.MAX_VALUE)) {
|
||||
// force cast for long values bigger than min/max int
|
||||
// to resolve compiler error: "integer number too large"
|
||||
cast = true;
|
||||
}
|
||||
if (cast) {
|
||||
if (bytesLen == 8) {
|
||||
return numStr + 'L';
|
||||
}
|
||||
return getCastStr(bytesLen) + numStr;
|
||||
}
|
||||
return numStr;
|
||||
}
|
||||
|
||||
public String formatShort(long l, boolean cast) {
|
||||
if (l == Short.MAX_VALUE) {
|
||||
return "Short.MAX_VALUE";
|
||||
private static String getCastStr(int bytesLen) {
|
||||
switch (bytesLen) {
|
||||
case 1:
|
||||
return "(byte) ";
|
||||
case 2:
|
||||
return "(short) ";
|
||||
case 4:
|
||||
return "(int) ";
|
||||
case 8:
|
||||
return "(long) ";
|
||||
default:
|
||||
throw new JadxRuntimeException("Unexpected number type length: " + bytesLen);
|
||||
}
|
||||
if (l == Short.MIN_VALUE) {
|
||||
return "Short.MIN_VALUE";
|
||||
}
|
||||
String str = toFormatString(l);
|
||||
return cast ? "(short) " + str : str;
|
||||
}
|
||||
|
||||
public String formatByte(long l, boolean cast) {
|
||||
if (l == Byte.MAX_VALUE) {
|
||||
return "Byte.MAX_VALUE";
|
||||
return formatNumber(l, 1, cast);
|
||||
}
|
||||
|
||||
public String formatShort(long l, boolean cast) {
|
||||
if (integerFormat == IntegerFormat.AUTO) {
|
||||
switch ((short) l) {
|
||||
case Short.MAX_VALUE:
|
||||
return "Short.MAX_VALUE";
|
||||
case Short.MIN_VALUE:
|
||||
return "Short.MIN_VALUE";
|
||||
}
|
||||
}
|
||||
if (l == Byte.MIN_VALUE) {
|
||||
return "Byte.MIN_VALUE";
|
||||
}
|
||||
String str = toFormatString(l);
|
||||
return cast ? "(byte) " + str : str;
|
||||
return formatNumber(l, 2, cast);
|
||||
}
|
||||
|
||||
public String formatInteger(long l, boolean cast) {
|
||||
if (l == Integer.MAX_VALUE) {
|
||||
return "Integer.MAX_VALUE";
|
||||
if (integerFormat == IntegerFormat.AUTO) {
|
||||
switch ((int) l) {
|
||||
case Integer.MAX_VALUE:
|
||||
return "Integer.MAX_VALUE";
|
||||
case Integer.MIN_VALUE:
|
||||
return "Integer.MIN_VALUE";
|
||||
}
|
||||
}
|
||||
if (l == Integer.MIN_VALUE) {
|
||||
return "Integer.MIN_VALUE";
|
||||
}
|
||||
String str = toFormatString(l);
|
||||
return cast ? "(int) " + str : str;
|
||||
return formatNumber(l, 4, cast);
|
||||
}
|
||||
|
||||
public String formatLong(long l, boolean cast) {
|
||||
if (l == Long.MAX_VALUE) {
|
||||
return "Long.MAX_VALUE";
|
||||
if (integerFormat == IntegerFormat.AUTO) {
|
||||
if (l == Long.MAX_VALUE) {
|
||||
return "Long.MAX_VALUE";
|
||||
}
|
||||
if (l == Long.MIN_VALUE) {
|
||||
return "Long.MIN_VALUE";
|
||||
}
|
||||
}
|
||||
if (l == Long.MIN_VALUE) {
|
||||
return "Long.MIN_VALUE";
|
||||
}
|
||||
String str = toFormatString(l);
|
||||
if (cast || Math.abs(l) >= Integer.MAX_VALUE) {
|
||||
return str + 'L';
|
||||
}
|
||||
return str;
|
||||
return formatNumber(l, 8, cast);
|
||||
}
|
||||
|
||||
public static String formatDouble(double d) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package jadx.tests.integration.arith;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.args.IntegerFormat;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestNumbersFormat extends IntegrationTest {
|
||||
|
||||
@SuppressWarnings({ "FieldCanBeLocal", "UnusedAssignment", "unused" })
|
||||
public static class TestCls {
|
||||
private Object obj;
|
||||
|
||||
public void test() {
|
||||
obj = new byte[] { 0, -1, -0xA, (byte) 0xff, Byte.MIN_VALUE, Byte.MAX_VALUE };
|
||||
obj = new short[] { 0, -1, -0xA, (short) 0xffff, Short.MIN_VALUE, Short.MAX_VALUE };
|
||||
obj = new int[] { 0, -1, -0xA, 0xffff_ffff, Integer.MIN_VALUE, Integer.MAX_VALUE };
|
||||
obj = new long[] { 0, -1, -0xA, 0xffff_ffff_ffff_ffffL, Long.MIN_VALUE, Long.MAX_VALUE };
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
getArgs().setIntegerFormat(IntegerFormat.AUTO);
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("new byte[]{0, -1, -10, -1, -128, 127}")
|
||||
.containsOne("new short[]{0, -1, -10, -1, Short.MIN_VALUE, Short.MAX_VALUE}")
|
||||
.containsOne("new int[]{0, -1, -10, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}")
|
||||
.containsOne("new long[]{0, -1, -10, -1, Long.MIN_VALUE, Long.MAX_VALUE}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecimalFormat() {
|
||||
getArgs().setIntegerFormat(IntegerFormat.DECIMAL);
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("new byte[]{0, -1, -10, -1, -128, 127}")
|
||||
.containsOne("new short[]{0, -1, -10, -1, -32768, 32767}")
|
||||
.containsOne("new int[]{0, -1, -10, -1, -2147483648, 2147483647}")
|
||||
.containsOne("new long[]{0, -1, -10, -1, -9223372036854775808L, 9223372036854775807L}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexFormat() {
|
||||
getArgs().setIntegerFormat(IntegerFormat.HEXADECIMAL);
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("new byte[]{0x0, (byte) 0xff, (byte) 0xf6, (byte) 0xff, (byte) 0x80, 0x7f}")
|
||||
.containsOne("new short[]{0x0, (short) 0xffff, (short) 0xfff6, (short) 0xffff, (short) 0x8000, 0x7fff}")
|
||||
.containsOne("new int[]{0x0, (int) 0xffffffff, (int) 0xfffffff6, (int) 0xffffffff, (int) 0x80000000, 0x7fffffff}")
|
||||
.containsOne(
|
||||
"new long[]{0x0, 0xffffffffffffffffL, 0xfffffffffffffff6L, 0xffffffffffffffffL, 0x8000000000000000L, 0x7fffffffffffffffL}");
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ package jadx.tests.integration.arith;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
import jadx.tests.api.utils.assertj.JadxAssertions;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestSpecialValues extends IntegrationTest {
|
||||
|
||||
@@ -11,7 +12,6 @@ public class TestSpecialValues extends IntegrationTest {
|
||||
|
||||
public void test() {
|
||||
shorts(Short.MIN_VALUE, Short.MAX_VALUE);
|
||||
bytes(Byte.MIN_VALUE, Byte.MAX_VALUE);
|
||||
ints(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
longs(Long.MIN_VALUE, Long.MAX_VALUE);
|
||||
|
||||
@@ -25,9 +25,6 @@ public class TestSpecialValues extends IntegrationTest {
|
||||
private void shorts(short... v) {
|
||||
}
|
||||
|
||||
private void bytes(byte... v) {
|
||||
}
|
||||
|
||||
private void ints(int... v) {
|
||||
}
|
||||
|
||||
@@ -43,14 +40,13 @@ public class TestSpecialValues extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
JadxAssertions.assertThat(getClassNode(TestCls.class))
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne(
|
||||
"Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_NORMAL")
|
||||
.containsOne("Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "
|
||||
+ "Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_NORMAL")
|
||||
.containsOne("Short.MIN_VALUE, Short.MAX_VALUE")
|
||||
.containsOne("Byte.MIN_VALUE, Byte.MAX_VALUE")
|
||||
.containsOne("Integer.MIN_VALUE, Integer.MAX_VALUE")
|
||||
.containsOne("Long.MIN_VALUE, Long.MAX_VALUE");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package jadx.tests.integration.arith;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.NotYetImplemented;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
@@ -15,7 +14,6 @@ public class TestSpecialValues2 extends IntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@NotYetImplemented("Constant value replace")
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package jadx.tests.integration.arrays;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
import jadx.tests.api.utils.assertj.JadxAssertions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestRedundantType extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public byte[] method() {
|
||||
return new byte[] { 10, 11, 12 };
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
JadxAssertions.assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.contains("return new byte[]{10, 11, 12};");
|
||||
}
|
||||
|
||||
public static class TestByte {
|
||||
|
||||
public byte[] method() {
|
||||
byte[] arr = new byte[50];
|
||||
arr[10] = 126;
|
||||
arr[20] = 127;
|
||||
arr[30] = (byte) 128;
|
||||
arr[40] = (byte) 129;
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByte() {
|
||||
JadxAssertions.assertThat(getClassNode(TestByte.class))
|
||||
.code()
|
||||
.contains("arr[10] = 126")
|
||||
.contains("arr[20] = Byte.MAX_VALUE")
|
||||
.contains("arr[30] = Byte.MIN_VALUE")
|
||||
.contains("arr[40] = -127");
|
||||
assertThat(new TestByte().method()[40]).isEqualTo((byte) -127);
|
||||
}
|
||||
|
||||
public static class TestShort {
|
||||
|
||||
public short[] method() {
|
||||
short[] arr = new short[50];
|
||||
arr[10] = 32766;
|
||||
arr[20] = 32767;
|
||||
arr[30] = (short) 32768;
|
||||
arr[40] = (short) 32769;
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShort() {
|
||||
JadxAssertions.assertThat(getClassNode(TestShort.class))
|
||||
.code()
|
||||
.contains("arr[10] = 32766")
|
||||
.contains("arr[20] = Short.MAX_VALUE")
|
||||
.contains("arr[30] = Short.MIN_VALUE")
|
||||
.contains("arr[40] = -32767");
|
||||
assertThat(new TestShort().method()[40]).isEqualTo((short) -32767);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class TestCast extends IntegrationTest {
|
||||
}
|
||||
|
||||
public void test3(boolean a) {
|
||||
write(a ? 0 : Byte.MAX_VALUE);
|
||||
write(a ? 0 : (byte) 127);
|
||||
}
|
||||
|
||||
public void test4(boolean a) {
|
||||
@@ -49,7 +49,7 @@ public class TestCast extends IntegrationTest {
|
||||
.code()
|
||||
.contains("write(a ? (byte) 0 : (byte) 1);")
|
||||
.contains("write(a ? (byte) 0 : this.myByte);")
|
||||
.contains("write(a ? (byte) 0 : Byte.MAX_VALUE);")
|
||||
.contains("write(a ? (byte) 0 : (byte) 127);")
|
||||
.contains("write(a ? (short) 0 : (short) 1);")
|
||||
.contains("write(a ? this.myShort : (short) 0);")
|
||||
.contains("write(a ? Short.MIN_VALUE : (short) 0);");
|
||||
|
||||
@@ -3,7 +3,8 @@ package jadx.tests.integration.types;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
import jadx.tests.api.utils.assertj.JadxAssertions;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestTypeResolver9 extends IntegrationTest {
|
||||
|
||||
@@ -13,13 +14,13 @@ public class TestTypeResolver9 extends IntegrationTest {
|
||||
}
|
||||
|
||||
public int test2(byte[] array, int offset) {
|
||||
return (array[offset] * 128) + (array[offset + 1] & 0xFF);
|
||||
return array[offset] * 128 + (array[offset + 1] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
JadxAssertions.assertThat(getClassNode(TestCls.class))
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("return 16777216 * b;")
|
||||
.doesNotContain("Byte.MIN_VALUE");
|
||||
|
||||
Reference in New Issue
Block a user