feat(java-input): support StackMapTable to get stack info for unvisited jumps (#2271)
This commit is contained in:
@@ -71,7 +71,11 @@ public class CheckCode extends AbstractVisitor {
|
||||
}
|
||||
insnNode.getRegisterArgs(list);
|
||||
for (RegisterArg arg : list) {
|
||||
if (arg.getRegNum() >= regsCount) {
|
||||
int regNum = arg.getRegNum();
|
||||
if (regNum < 0) {
|
||||
throw new JadxRuntimeException("Incorrect negative register number in instruction: " + insnNode);
|
||||
}
|
||||
if (regNum >= regsCount) {
|
||||
throw new JadxRuntimeException("Incorrect register number in instruction: " + insnNode
|
||||
+ ", expected to be less than " + regsCount);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class BlockProcessor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean deduplicateBlockInsns(BlockNode block) {
|
||||
private static boolean deduplicateBlockInsns(MethodNode mth, BlockNode block) {
|
||||
if (block.contains(AFlag.LOOP_START) || block.contains(AFlag.LOOP_END)) {
|
||||
// search for same instruction at end of all predecessors blocks
|
||||
List<BlockNode> predecessors = block.getPredecessors();
|
||||
@@ -109,7 +109,7 @@ public class BlockProcessor extends AbstractVisitor {
|
||||
List<InsnNode> insns = getLastInsns(predecessors.get(0), sameInsnCount);
|
||||
insertAtStart(block, insns);
|
||||
predecessors.forEach(pred -> getLastInsns(pred, sameInsnCount).clear());
|
||||
LOG.debug("Move duplicate insns, count: {} to block {}", sameInsnCount, block);
|
||||
mth.addDebugComment("Move duplicate insns, count: " + sameInsnCount + " to block " + block);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -318,7 +318,7 @@ public class BlockProcessor extends AbstractVisitor {
|
||||
boolean changed = false;
|
||||
List<BlockNode> basicBlocks = mth.getBasicBlocks();
|
||||
for (BlockNode basicBlock : basicBlocks) {
|
||||
if (deduplicateBlockInsns(basicBlock)) {
|
||||
if (deduplicateBlockInsns(mth, basicBlock)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package jadx.tests.integration.jbc;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.RaungTest;
|
||||
import jadx.tests.api.extensions.profiles.TestProfile;
|
||||
import jadx.tests.api.extensions.profiles.TestWithProfiles;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestStackConvert extends RaungTest {
|
||||
|
||||
@SuppressWarnings({ "UnnecessaryLocalVariable", "CallToPrintStackTrace", "printstacktrace" })
|
||||
public static class TestCls {
|
||||
public int parseIntDefault(String num, int defaultNum) {
|
||||
try {
|
||||
int defaultNum2 = Integer.parseInt(num);
|
||||
return defaultNum2;
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Before println");
|
||||
e.printStackTrace();
|
||||
return defaultNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestWithProfiles(TestProfile.JAVA11)
|
||||
public void test() {
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("Integer.parseInt(num)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaung() {
|
||||
assertThat(getClassNodeFromRaung())
|
||||
.code()
|
||||
.containsOne("Integer.parseInt(num)");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
.version 52 # Java 8
|
||||
.class public super jbc/TestStackConvert
|
||||
|
||||
.method public parseIntDefault(Ljava/lang/String;I)I
|
||||
.max stack 3
|
||||
.max locals 5
|
||||
|
||||
:L0
|
||||
.local 0 "this" Ljbc/TestStackConvert;
|
||||
.local 1 "num" Ljava/lang/String;
|
||||
.local 2 "defaultNum" I
|
||||
.line 13
|
||||
aload 1
|
||||
invokestatic java/lang/Integer parseInt (Ljava/lang/String;)I
|
||||
:L1
|
||||
.catch java/lang/NumberFormatException :L0 .. :L1 goto :L2
|
||||
ireturn
|
||||
:L3
|
||||
.line 14
|
||||
.stack full
|
||||
stack 0 java/lang/NumberFormatException
|
||||
local 0 jbc/TestStackConvert
|
||||
local 1 java/lang/String
|
||||
local 2 int
|
||||
local 3 Top
|
||||
local 4 java/lang/NumberFormatException
|
||||
.end stack
|
||||
astore 3
|
||||
.local 3 "e" Ljava/lang/NumberFormatException;
|
||||
.line 15
|
||||
aload 3
|
||||
invokevirtual java/lang/NumberFormatException printStackTrace ()V
|
||||
.line 17
|
||||
iload 2
|
||||
.end local 3 # "e"
|
||||
ireturn
|
||||
:L2
|
||||
.stack full
|
||||
stack 0 java/lang/NumberFormatException
|
||||
local 0 jbc/TestStackConvert
|
||||
local 1 java/lang/String
|
||||
local 2 int
|
||||
.end stack
|
||||
.end local 0 # "this"
|
||||
.end local 1 # "num"
|
||||
.end local 2 # "defaultNum"
|
||||
astore 4
|
||||
getstatic java/lang/System out Ljava/io/PrintStream;
|
||||
ldc "Before println"
|
||||
invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
|
||||
aload 4
|
||||
goto :L3
|
||||
.end method
|
||||
+1
-1
@@ -103,7 +103,7 @@ public class ConstPoolReader {
|
||||
}
|
||||
|
||||
private CallSite resolveMethodCallSite(int bootstrapMthIdx, int nameIdx, int descIdx) {
|
||||
JavaBootstrapMethodsAttr bootstrapMethodsAttr = clsData.loadAttribute(data, JavaAttrType.BOOTSTRAP_METHODS);
|
||||
JavaBootstrapMethodsAttr bootstrapMethodsAttr = clsData.loadClassAttribute(data, JavaAttrType.BOOTSTRAP_METHODS);
|
||||
if (bootstrapMethodsAttr == null) {
|
||||
throw new JavaClassParseException("Unexpected missing BootstrapMethods attribute");
|
||||
}
|
||||
|
||||
+5
-5
@@ -106,7 +106,7 @@ public class JavaClassData implements IClassData {
|
||||
int accessFlags = reader.readU2();
|
||||
int nameIdx = reader.readU2();
|
||||
int typeIdx = reader.readU2();
|
||||
JavaAttrStorage attributes = attributesReader.load(reader);
|
||||
JavaAttrStorage attributes = attributesReader.loadAll(reader);
|
||||
|
||||
field.setAccessFlags(accessFlags);
|
||||
field.setName(constPoolReader.getUtf8(nameIdx));
|
||||
@@ -118,7 +118,7 @@ public class JavaClassData implements IClassData {
|
||||
int accessFlags = reader.readU2();
|
||||
int nameIdx = reader.readU2();
|
||||
int descriptorIdx = reader.readU2();
|
||||
JavaAttrStorage attributes = attributesReader.load(reader);
|
||||
JavaAttrStorage attributes = attributesReader.loadAll(reader);
|
||||
|
||||
JavaMethodRef methodRef = method.getMethodRef();
|
||||
methodRef.reset();
|
||||
@@ -140,7 +140,7 @@ public class JavaClassData implements IClassData {
|
||||
@Override
|
||||
public List<IJadxAttribute> getAttributes() {
|
||||
data.absPos(offsets.getAttributesOffset());
|
||||
JavaAttrStorage attributes = attributesReader.load(data);
|
||||
JavaAttrStorage attributes = attributesReader.loadAll(data);
|
||||
int size = attributes.size();
|
||||
if (size == 0) {
|
||||
return Collections.emptyList();
|
||||
@@ -153,9 +153,9 @@ public class JavaClassData implements IClassData {
|
||||
return list;
|
||||
}
|
||||
|
||||
public <T extends IJavaAttribute> T loadAttribute(DataReader reader, JavaAttrType<T> type) {
|
||||
public <T extends IJavaAttribute> T loadClassAttribute(DataReader reader, JavaAttrType<T> type) {
|
||||
reader.absPos(offsets.getAttributesOffset());
|
||||
return attributesReader.loadOne(type, reader);
|
||||
return attributesReader.loadOne(reader, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+56
-58
@@ -2,6 +2,8 @@ package jadx.plugins.input.java.data.attributes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@@ -14,6 +16,8 @@ import jadx.plugins.input.java.data.JavaClassData;
|
||||
public class AttributesReader {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AttributesReader.class);
|
||||
|
||||
private static final Predicate<JavaAttrType<?>> LOAD_ALL = type -> true;
|
||||
|
||||
private final JavaClassData clsData;
|
||||
private final ConstPoolReader constPool;
|
||||
private final Map<Integer, JavaAttrType<?>> attrCache = new HashMap<>(JavaAttrType.size());
|
||||
@@ -23,77 +27,71 @@ public class AttributesReader {
|
||||
this.constPool = constPoolReader;
|
||||
}
|
||||
|
||||
public JavaAttrStorage load(DataReader reader) {
|
||||
int attributesCount = reader.readU2();
|
||||
if (attributesCount == 0) {
|
||||
public JavaAttrStorage loadAll(DataReader reader) {
|
||||
return loadAttributes(reader, LOAD_ALL);
|
||||
}
|
||||
|
||||
public JavaAttrStorage loadMulti(DataReader reader, Set<JavaAttrType<?>> types) {
|
||||
return loadAttributes(reader, types::contains);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load attributes into storage
|
||||
*
|
||||
* @param reader - reader pos should be set to attributes section start
|
||||
* @param condition - check if attribute should be parsed and added to storage
|
||||
*/
|
||||
private JavaAttrStorage loadAttributes(DataReader reader, Predicate<JavaAttrType<?>> condition) {
|
||||
int count = reader.readU2();
|
||||
if (count == 0) {
|
||||
return JavaAttrStorage.EMPTY;
|
||||
}
|
||||
JavaAttrStorage storage = new JavaAttrStorage();
|
||||
for (int i = 0; i < attributesCount; i++) {
|
||||
readAndAdd(storage, reader);
|
||||
for (int i = 0; i < count; i++) {
|
||||
int nameIdx = reader.readU2();
|
||||
int len = reader.readU4();
|
||||
int end = reader.getOffset() + len;
|
||||
try {
|
||||
JavaAttrType<?> attrType = resolveAttrReader(nameIdx);
|
||||
if (attrType != null && condition.test(attrType)) {
|
||||
IJavaAttributeReader attrReader = attrType.getReader();
|
||||
if (attrReader != null) {
|
||||
IJavaAttribute attrValue = attrReader.read(clsData, reader);
|
||||
if (attrValue != null) {
|
||||
storage.add(attrType, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse attribute: {}", constPool.getUtf8(nameIdx), e);
|
||||
} finally {
|
||||
reader.absPos(end);
|
||||
}
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
||||
private void readAndAdd(JavaAttrStorage storage, DataReader reader) {
|
||||
int nameIdx = reader.readU2();
|
||||
int len = reader.readU4();
|
||||
int end = reader.getOffset() + len;
|
||||
try {
|
||||
JavaAttrType<?> attrType = resolveAttrReader(nameIdx);
|
||||
if (attrType == null) {
|
||||
return;
|
||||
}
|
||||
IJavaAttributeReader attrReader = attrType.getReader();
|
||||
if (attrReader == null) {
|
||||
// ignore attribute
|
||||
return;
|
||||
}
|
||||
IJavaAttribute attrValue = attrReader.read(clsData, reader);
|
||||
if (attrValue != null) {
|
||||
storage.add(attrType, attrValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse attribute: {}", constPool.getUtf8(nameIdx), e);
|
||||
} finally {
|
||||
reader.absPos(end);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T extends IJavaAttribute> T loadOne(JavaAttrType<T> type, DataReader reader) {
|
||||
int attributesCount = reader.readU2();
|
||||
if (attributesCount == 0) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < attributesCount; i++) {
|
||||
IJavaAttribute attr = readType(type, reader);
|
||||
if (attr != null) {
|
||||
return (T) attr;
|
||||
public <T extends IJavaAttribute> @Nullable T loadOne(DataReader reader, JavaAttrType<T> type) {
|
||||
int count = reader.readU2();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int nameIdx = reader.readU2();
|
||||
int len = reader.readU4();
|
||||
int end = reader.getOffset() + len;
|
||||
try {
|
||||
JavaAttrType<?> attrType = resolveAttrReader(nameIdx);
|
||||
if (attrType == type) {
|
||||
return (T) attrType.getReader().read(clsData, reader);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse attribute: {}", constPool.getUtf8(nameIdx), e);
|
||||
} finally {
|
||||
reader.absPos(end);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private IJavaAttribute readType(JavaAttrType<?> type, DataReader reader) {
|
||||
int nameIdx = reader.readU2();
|
||||
int len = reader.readU4();
|
||||
int end = reader.getOffset() + len;
|
||||
try {
|
||||
JavaAttrType<?> attrType = resolveAttrReader(nameIdx);
|
||||
if (attrType == null || attrType != type) {
|
||||
return null;
|
||||
}
|
||||
return attrType.getReader().read(clsData, reader);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse attribute: {}", constPool.getUtf8(nameIdx), e);
|
||||
return null;
|
||||
} finally {
|
||||
reader.absPos(end);
|
||||
}
|
||||
}
|
||||
|
||||
private JavaAttrType<?> resolveAttrReader(int nameIdx) {
|
||||
return attrCache.computeIfAbsent(nameIdx, idx -> {
|
||||
String attrName = constPool.getUtf8(idx);
|
||||
|
||||
+24
-2
@@ -9,6 +9,7 @@ import jadx.api.plugins.input.data.annotations.AnnotationVisibility;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LineNumberTableAttr;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LocalVarTypesAttr;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LocalVarsAttr;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackMapTableReader;
|
||||
import jadx.plugins.input.java.data.attributes.types.CodeAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.ConstValueAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.IgnoredAttr;
|
||||
@@ -21,6 +22,7 @@ import jadx.plugins.input.java.data.attributes.types.JavaMethodParametersAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.JavaParamAnnsAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.JavaSignatureAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.JavaSourceFileAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;
|
||||
|
||||
public final class JavaAttrType<T extends IJavaAttribute> {
|
||||
|
||||
@@ -32,6 +34,7 @@ public final class JavaAttrType<T extends IJavaAttribute> {
|
||||
public static final JavaAttrType<ConstValueAttr> CONST_VALUE;
|
||||
|
||||
public static final JavaAttrType<CodeAttr> CODE;
|
||||
public static final JavaAttrType<StackMapTableAttr> STACK_MAP_TABLE;
|
||||
public static final JavaAttrType<LineNumberTableAttr> LINE_NUMBER_TABLE;
|
||||
public static final JavaAttrType<LocalVarsAttr> LOCAL_VAR_TABLE;
|
||||
public static final JavaAttrType<LocalVarTypesAttr> LOCAL_VAR_TYPE_TABLE;
|
||||
@@ -51,9 +54,12 @@ public final class JavaAttrType<T extends IJavaAttribute> {
|
||||
|
||||
public static final JavaAttrType<IgnoredAttr> DEPRECATED;
|
||||
public static final JavaAttrType<IgnoredAttr> SYNTHETIC;
|
||||
public static final JavaAttrType<IgnoredAttr> STACK_MAP_TABLE;
|
||||
public static final JavaAttrType<IgnoredAttr> ENCLOSING_METHOD;
|
||||
|
||||
public static final JavaAttrType<IgnoredAttr> MODULE;
|
||||
public static final JavaAttrType<IgnoredAttr> SOURCE_DEBUG_EXTENSION;
|
||||
public static final JavaAttrType<IgnoredAttr> NEST_HOST;
|
||||
public static final JavaAttrType<IgnoredAttr> NEST_MEMBERS;
|
||||
|
||||
static {
|
||||
NAME_TO_TYPE_MAP = new HashMap<>();
|
||||
@@ -79,17 +85,20 @@ public final class JavaAttrType<T extends IJavaAttribute> {
|
||||
SIGNATURE = bind("Signature", JavaSignatureAttr.reader());
|
||||
EXCEPTIONS = bind("Exceptions", JavaExceptionsAttr.reader());
|
||||
METHOD_PARAMETERS = bind("MethodParameters", JavaMethodParametersAttr.reader());
|
||||
STACK_MAP_TABLE = bind("StackMapTable", new StackMapTableReader());
|
||||
|
||||
// ignored
|
||||
DEPRECATED = bind("Deprecated", null); // duplicated by annotation
|
||||
SYNTHETIC = bind("Synthetic", null); // duplicated by access flag
|
||||
STACK_MAP_TABLE = bind("StackMapTable", null);
|
||||
ENCLOSING_METHOD = bind("EnclosingMethod", null);
|
||||
|
||||
// TODO: not supported yet
|
||||
RUNTIME_TYPE_ANNOTATIONS = bind("RuntimeVisibleTypeAnnotations", null);
|
||||
BUILD_TYPE_ANNOTATIONS = bind("RuntimeInvisibleTypeAnnotations", null);
|
||||
MODULE = bind("Module", null);
|
||||
NEST_HOST = bind("NestHost", null);
|
||||
NEST_MEMBERS = bind("NestMembers", null);
|
||||
SOURCE_DEBUG_EXTENSION = bind("SourceDebugExtension", null);
|
||||
}
|
||||
|
||||
private static <A extends IJavaAttribute> JavaAttrType<A> bind(String name, IJavaAttributeReader reader) {
|
||||
@@ -129,6 +138,19 @@ public final class JavaAttrType<T extends IJavaAttribute> {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
return id == ((JavaAttrType<?>) o).id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package jadx.plugins.input.java.data.attributes.stack;
|
||||
|
||||
public class StackFrame {
|
||||
private final int offset;
|
||||
private final StackFrameType type;
|
||||
|
||||
private int stackSize;
|
||||
private StackValueType[] stackValueTypes;
|
||||
private int localsCount;
|
||||
|
||||
public StackFrame(int offset, StackFrameType type) {
|
||||
this.offset = offset;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public StackFrameType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getLocalsCount() {
|
||||
return localsCount;
|
||||
}
|
||||
|
||||
public void setLocalsCount(int localsCount) {
|
||||
this.localsCount = localsCount;
|
||||
}
|
||||
|
||||
public int getStackSize() {
|
||||
return stackSize;
|
||||
}
|
||||
|
||||
public void setStackSize(int stackSize) {
|
||||
this.stackSize = stackSize;
|
||||
}
|
||||
|
||||
public StackValueType[] getStackValueTypes() {
|
||||
return stackValueTypes;
|
||||
}
|
||||
|
||||
public void setStackValueTypes(StackValueType[] stackValueTypes) {
|
||||
this.stackValueTypes = stackValueTypes;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package jadx.plugins.input.java.data.attributes.stack;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum StackFrameType {
|
||||
SAME_FRAME(0, 63),
|
||||
SAME_LOCALS_1_STACK(64, 127),
|
||||
SAME_LOCALS_1_STACK_EXTENDED(247, 247),
|
||||
CHOP(248, 250),
|
||||
SAME_FRAME_EXTENDED(251, 251),
|
||||
APPEND(252, 254),
|
||||
FULL(255, 255);
|
||||
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
StackFrameType(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
private static final StackFrameType[] MAPPING = buildMapping();
|
||||
|
||||
private static StackFrameType[] buildMapping() {
|
||||
StackFrameType[] mapping = new StackFrameType[256];
|
||||
for (StackFrameType value : StackFrameType.values()) {
|
||||
for (int i = value.start; i <= value.end; i++) {
|
||||
mapping[i] = value;
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static @Nullable StackFrameType getType(int data) {
|
||||
return MAPPING[data];
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package jadx.plugins.input.java.data.attributes.stack;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jadx.plugins.input.java.data.DataReader;
|
||||
import jadx.plugins.input.java.data.JavaClassData;
|
||||
import jadx.plugins.input.java.data.attributes.IJavaAttribute;
|
||||
import jadx.plugins.input.java.data.attributes.IJavaAttributeReader;
|
||||
import jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;
|
||||
import jadx.plugins.input.java.utils.JavaClassParseException;
|
||||
|
||||
public class StackMapTableReader implements IJavaAttributeReader {
|
||||
|
||||
@Override
|
||||
public IJavaAttribute read(JavaClassData clsData, DataReader reader) {
|
||||
int count = reader.readU2();
|
||||
Map<Integer, StackFrame> map = new HashMap<>(count, 1);
|
||||
StackFrame prevFrame = null;
|
||||
for (int i = 0; i < count; i++) {
|
||||
StackFrame frame = readFrame(reader, prevFrame);
|
||||
map.put(frame.getOffset(), frame);
|
||||
prevFrame = frame;
|
||||
}
|
||||
return new StackMapTableAttr(map);
|
||||
}
|
||||
|
||||
private static final Map<StackFrameType, Consumer<FrameContext>> FRAME_READERS = registerReaders();
|
||||
|
||||
private static Map<StackFrameType, Consumer<FrameContext>> registerReaders() {
|
||||
EnumMap<StackFrameType, Consumer<FrameContext>> map = new EnumMap<>(StackFrameType.class);
|
||||
map.put(StackFrameType.SAME_FRAME, context -> readSame(context, false));
|
||||
map.put(StackFrameType.SAME_FRAME_EXTENDED, context -> readSame(context, true));
|
||||
map.put(StackFrameType.SAME_LOCALS_1_STACK, context -> readSL1S(context, false));
|
||||
map.put(StackFrameType.SAME_LOCALS_1_STACK_EXTENDED, context -> readSL1S(context, true));
|
||||
map.put(StackFrameType.CHOP, StackMapTableReader::readChop);
|
||||
map.put(StackFrameType.APPEND, StackMapTableReader::readAppend);
|
||||
map.put(StackFrameType.FULL, StackMapTableReader::readFull);
|
||||
return map;
|
||||
}
|
||||
|
||||
private StackFrame readFrame(DataReader reader, StackFrame prevFrame) {
|
||||
int typeData = reader.readU1();
|
||||
StackFrameType frameType = StackFrameType.getType(typeData);
|
||||
Consumer<FrameContext> frameReader = FRAME_READERS.get(frameType);
|
||||
if (frameReader == null) {
|
||||
throw new JavaClassParseException("Found unsupported stack frame type: " + frameType);
|
||||
}
|
||||
FrameContext frameContext = new FrameContext(reader, typeData, prevFrame);
|
||||
frameReader.accept(frameContext);
|
||||
return Objects.requireNonNull(frameContext.getFrame());
|
||||
}
|
||||
|
||||
private static void readSame(FrameContext context, boolean extended) {
|
||||
int offsetDelta;
|
||||
StackFrameType type;
|
||||
if (extended) {
|
||||
type = StackFrameType.SAME_FRAME_EXTENDED;
|
||||
offsetDelta = context.getDataReader().readU2();
|
||||
} else {
|
||||
type = StackFrameType.SAME_FRAME;
|
||||
offsetDelta = context.getTypeData();
|
||||
}
|
||||
StackFrame frame = new StackFrame(calcOffset(context, offsetDelta), type);
|
||||
frame.setStackSize(0);
|
||||
frame.setLocalsCount(getPrevLocalsCount(context));
|
||||
context.setFrame(frame);
|
||||
}
|
||||
|
||||
private static void readSL1S(FrameContext context, boolean extended) {
|
||||
DataReader reader = context.getDataReader();
|
||||
int offsetDelta;
|
||||
StackFrameType type;
|
||||
if (extended) {
|
||||
type = StackFrameType.SAME_LOCALS_1_STACK_EXTENDED;
|
||||
offsetDelta = reader.readU2();
|
||||
} else {
|
||||
type = StackFrameType.SAME_LOCALS_1_STACK;
|
||||
offsetDelta = context.getTypeData() - 64;
|
||||
}
|
||||
StackValueType[] stackTypes = TypeInfoReader.readTypeInfoList(reader, 1);
|
||||
StackFrame frame = new StackFrame(calcOffset(context, offsetDelta), type);
|
||||
frame.setStackSize(1);
|
||||
frame.setStackValueTypes(stackTypes);
|
||||
frame.setLocalsCount(getPrevLocalsCount(context));
|
||||
context.setFrame(frame);
|
||||
}
|
||||
|
||||
private static void readChop(FrameContext context) {
|
||||
int k = 251 - context.getTypeData();
|
||||
int offsetDelta = context.getDataReader().readU2();
|
||||
StackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.CHOP);
|
||||
frame.setStackSize(0);
|
||||
frame.setLocalsCount(getPrevLocalsCount(context) - k);
|
||||
context.setFrame(frame);
|
||||
}
|
||||
|
||||
private static void readAppend(FrameContext context) {
|
||||
DataReader reader = context.getDataReader();
|
||||
int k = context.getTypeData() - 251;
|
||||
int offsetDelta = reader.readU2();
|
||||
TypeInfoReader.skipTypeInfoList(reader, k);
|
||||
StackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.APPEND);
|
||||
frame.setStackSize(0);
|
||||
frame.setLocalsCount(getPrevLocalsCount(context) - k);
|
||||
context.setFrame(frame);
|
||||
}
|
||||
|
||||
private static void readFull(FrameContext context) {
|
||||
DataReader reader = context.getDataReader();
|
||||
int offsetDelta = reader.readU2();
|
||||
int localsCount = reader.readU2();
|
||||
TypeInfoReader.skipTypeInfoList(reader, localsCount);
|
||||
int stackSize = reader.readU2();
|
||||
StackValueType[] stackTypes = TypeInfoReader.readTypeInfoList(reader, stackSize);
|
||||
|
||||
StackFrame frame = new StackFrame(calcOffset(context, offsetDelta), StackFrameType.FULL);
|
||||
frame.setLocalsCount(localsCount);
|
||||
frame.setStackSize(stackSize);
|
||||
frame.setStackValueTypes(stackTypes);
|
||||
context.setFrame(frame);
|
||||
}
|
||||
|
||||
private static int calcOffset(FrameContext context, int offsetDelta) {
|
||||
StackFrame prevFrame = context.getPrevFrame();
|
||||
if (prevFrame == null) {
|
||||
return offsetDelta;
|
||||
}
|
||||
return prevFrame.getOffset() + offsetDelta + 1;
|
||||
}
|
||||
|
||||
private static int getPrevLocalsCount(FrameContext context) {
|
||||
StackFrame prevFrame = context.getPrevFrame();
|
||||
if (prevFrame == null) {
|
||||
return 0;
|
||||
}
|
||||
return prevFrame.getLocalsCount();
|
||||
}
|
||||
|
||||
private static final class FrameContext {
|
||||
private final DataReader dataReader;
|
||||
private final int typeData;
|
||||
private final StackFrame prevFrame;
|
||||
|
||||
private StackFrame frame;
|
||||
|
||||
private FrameContext(DataReader dataReader, int typeData, StackFrame prevFrame) {
|
||||
this.dataReader = dataReader;
|
||||
this.typeData = typeData;
|
||||
this.prevFrame = prevFrame;
|
||||
}
|
||||
|
||||
public DataReader getDataReader() {
|
||||
return dataReader;
|
||||
}
|
||||
|
||||
public int getTypeData() {
|
||||
return typeData;
|
||||
}
|
||||
|
||||
public StackFrame getPrevFrame() {
|
||||
return prevFrame;
|
||||
}
|
||||
|
||||
public StackFrame getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void setFrame(StackFrame frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package jadx.plugins.input.java.data.attributes.stack;
|
||||
|
||||
public enum StackValueType {
|
||||
NARROW, // int, float, etc
|
||||
WIDE, // long, double
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package jadx.plugins.input.java.data.attributes.stack;
|
||||
|
||||
import jadx.plugins.input.java.data.DataReader;
|
||||
|
||||
public class TypeInfoReader {
|
||||
private static final int ITEM_TOP = 0;
|
||||
private static final int ITEM_INT = 1;
|
||||
private static final int ITEM_FLOAT = 2;
|
||||
|
||||
private static final int ITEM_DOUBLE = 3;
|
||||
private static final int ITEM_LONG = 4;
|
||||
|
||||
private static final int ITEM_NULL = 5;
|
||||
private static final int ITEM_UNINITIALIZED_THIS = 6;
|
||||
|
||||
private static final int ITEM_OBJECT = 7;
|
||||
private static final int ITEM_UNINITIALIZED = 8;
|
||||
|
||||
static StackValueType[] readTypeInfoList(DataReader reader, int count) {
|
||||
StackValueType[] types = new StackValueType[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
int tag = reader.readU1();
|
||||
StackValueType type;
|
||||
switch (tag) {
|
||||
case ITEM_DOUBLE:
|
||||
case ITEM_LONG:
|
||||
type = StackValueType.WIDE;
|
||||
break;
|
||||
|
||||
case ITEM_OBJECT:
|
||||
case ITEM_UNINITIALIZED:
|
||||
reader.readU2(); // ignore
|
||||
type = StackValueType.NARROW;
|
||||
break;
|
||||
|
||||
default:
|
||||
type = StackValueType.NARROW;
|
||||
break;
|
||||
}
|
||||
types[i] = type;
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
static void skipTypeInfoList(DataReader reader, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int tag = reader.readU1();
|
||||
if (tag == ITEM_OBJECT || tag == ITEM_UNINITIALIZED) {
|
||||
reader.readU2();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package jadx.plugins.input.java.data.attributes.types;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.plugins.input.java.data.attributes.IJavaAttribute;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackFrame;
|
||||
|
||||
public class StackMapTableAttr implements IJavaAttribute {
|
||||
public static final StackMapTableAttr EMPTY = new StackMapTableAttr(Collections.emptyMap());
|
||||
|
||||
private final Map<Integer, StackFrame> map;
|
||||
|
||||
public StackMapTableAttr(Map<Integer, StackFrame> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public @Nullable StackFrame getFor(int offset) {
|
||||
return map.get(offset);
|
||||
}
|
||||
}
|
||||
+37
-18
@@ -4,10 +4,15 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.input.insns.Opcode;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.plugins.input.java.data.DataReader;
|
||||
import jadx.plugins.input.java.data.JavaClassData;
|
||||
import jadx.plugins.input.java.data.code.StackState.SVType;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackFrame;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackValueType;
|
||||
import jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public class CodeDecodeState {
|
||||
@@ -15,35 +20,49 @@ public class CodeDecodeState {
|
||||
private final DataReader reader;
|
||||
private final int maxStack;
|
||||
private final Set<Integer> excHandlers;
|
||||
|
||||
private final StackMapTableAttr stackMapTable;
|
||||
private final Map<Integer, StackState> jumpStack = new HashMap<>(); // save current stack for jump target
|
||||
|
||||
private JavaInsnData insn;
|
||||
private StackState stack;
|
||||
private boolean excHandler;
|
||||
|
||||
public CodeDecodeState(JavaClassData clsData, DataReader reader, int maxStack, Set<Integer> excHandlers) {
|
||||
public CodeDecodeState(JavaClassData clsData, DataReader reader, int maxStack,
|
||||
Set<Integer> excHandlers, @Nullable StackMapTableAttr stackMapTable) {
|
||||
this.clsData = clsData;
|
||||
this.reader = reader;
|
||||
this.maxStack = maxStack;
|
||||
this.excHandlers = excHandlers;
|
||||
this.stack = new StackState(maxStack);
|
||||
this.stackMapTable = Utils.getOrElse(stackMapTable, StackMapTableAttr.EMPTY);
|
||||
}
|
||||
|
||||
public void onInsn(int offset) {
|
||||
StackState stackState = jumpStack.get(offset);
|
||||
if (stackState != null) {
|
||||
this.stack = stackState;
|
||||
StackState newStack = loadStack(offset);
|
||||
if (newStack != null) {
|
||||
this.stack = newStack;
|
||||
}
|
||||
if (excHandlers.contains(offset)) {
|
||||
clear();
|
||||
stack.push(SVType.NARROW); // push exception
|
||||
stack.push(StackValueType.NARROW); // push exception
|
||||
excHandler = true;
|
||||
} else {
|
||||
excHandler = false;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable StackState loadStack(int offset) {
|
||||
StackState stackState = jumpStack.get(offset);
|
||||
if (stackState != null) {
|
||||
return stackState.copy();
|
||||
}
|
||||
StackFrame frame = stackMapTable.getFor(offset);
|
||||
if (frame != null) {
|
||||
return new StackState(maxStack).fillFromFrame(frame);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void registerJump(int jumpOffset) {
|
||||
Integer key = jumpOffset;
|
||||
if (!jumpStack.containsKey(key)) {
|
||||
@@ -90,7 +109,7 @@ public class CodeDecodeState {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SVType peekType(int at) {
|
||||
public StackValueType peekType(int at) {
|
||||
return stack.peekTypeAt(at);
|
||||
}
|
||||
|
||||
@@ -100,21 +119,21 @@ public class CodeDecodeState {
|
||||
}
|
||||
|
||||
public CodeDecodeState push(int arg) {
|
||||
insn.setArgReg(arg, stack.push(SVType.NARROW));
|
||||
insn.setArgReg(arg, stack.push(StackValueType.NARROW));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeDecodeState push(int arg, SVType type) {
|
||||
public CodeDecodeState push(int arg, StackValueType type) {
|
||||
insn.setArgReg(arg, stack.push(type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeDecodeState pushWide(int arg) {
|
||||
insn.setArgReg(arg, stack.push(SVType.WIDE));
|
||||
insn.setArgReg(arg, stack.push(StackValueType.WIDE));
|
||||
return this;
|
||||
}
|
||||
|
||||
public int insert(int pos, SVType type) {
|
||||
public int insert(int pos, StackValueType type) {
|
||||
return stack.insert(pos, type);
|
||||
}
|
||||
|
||||
@@ -123,9 +142,9 @@ public class CodeDecodeState {
|
||||
}
|
||||
|
||||
public void discardWord() {
|
||||
SVType type = stack.peekTypeAt(0);
|
||||
StackValueType type = stack.peekTypeAt(0);
|
||||
stack.pop();
|
||||
if (type == SVType.NARROW) {
|
||||
if (type == StackValueType.NARROW) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
@@ -162,16 +181,16 @@ public class CodeDecodeState {
|
||||
return maxStack + local;
|
||||
}
|
||||
|
||||
public SVType fieldType() {
|
||||
public StackValueType fieldType() {
|
||||
String type = insn.constPoolReader().getFieldType(insn().getIndex());
|
||||
return getSVType(type);
|
||||
}
|
||||
|
||||
public SVType getSVType(String type) {
|
||||
public StackValueType getSVType(String type) {
|
||||
if (type.equals("J") || type.equals("D")) {
|
||||
return SVType.WIDE;
|
||||
return StackValueType.WIDE;
|
||||
}
|
||||
return SVType.NARROW;
|
||||
return StackValueType.NARROW;
|
||||
}
|
||||
|
||||
public int u1() {
|
||||
|
||||
+21
-11
@@ -28,6 +28,7 @@ import jadx.plugins.input.java.data.attributes.debuginfo.JavaLocalVar;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LineNumberTableAttr;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LocalVarTypesAttr;
|
||||
import jadx.plugins.input.java.data.attributes.debuginfo.LocalVarsAttr;
|
||||
import jadx.plugins.input.java.data.attributes.types.StackMapTableAttr;
|
||||
import jadx.plugins.input.java.data.code.trycatch.JavaSingleCatch;
|
||||
import jadx.plugins.input.java.data.code.trycatch.JavaTryData;
|
||||
import jadx.plugins.input.java.utils.JavaClassParseException;
|
||||
@@ -52,11 +53,14 @@ public class JavaCodeReader implements ICodeReader {
|
||||
@Override
|
||||
public void visitInstructions(Consumer<InsnData> insnConsumer) {
|
||||
Set<Integer> excHandlers = getExcHandlers();
|
||||
jumpToCodeAttributes();
|
||||
StackMapTableAttr stackMapTable = clsData.getAttributesReader().loadOne(reader, JavaAttrType.STACK_MAP_TABLE);
|
||||
|
||||
int maxStack = readMaxStack();
|
||||
reader.skip(2);
|
||||
int codeSize = reader.readU4();
|
||||
|
||||
CodeDecodeState state = new CodeDecodeState(clsData, reader, maxStack, excHandlers);
|
||||
CodeDecodeState state = new CodeDecodeState(clsData, reader, maxStack, excHandlers, stackMapTable);
|
||||
JavaInsnData insn = new JavaInsnData(state);
|
||||
state.setInsn(insn);
|
||||
int offset = 0;
|
||||
@@ -116,15 +120,17 @@ public class JavaCodeReader implements ICodeReader {
|
||||
return reader.absPos(codeOffset + 4).readU4();
|
||||
}
|
||||
|
||||
private static final Set<JavaAttrType<?>> DEBUG_INFO_ATTRIBUTES = Set.of(
|
||||
JavaAttrType.LINE_NUMBER_TABLE,
|
||||
JavaAttrType.LOCAL_VAR_TABLE,
|
||||
JavaAttrType.LOCAL_VAR_TYPE_TABLE);
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IDebugInfo getDebugInfo() {
|
||||
int maxStack = readMaxStack();
|
||||
reader.skip(2);
|
||||
reader.skip(reader.readU4());
|
||||
reader.skip(reader.readU2() * 8);
|
||||
|
||||
JavaAttrStorage attrs = clsData.getAttributesReader().load(reader);
|
||||
jumpToCodeAttributes();
|
||||
JavaAttrStorage attrs = clsData.getAttributesReader().loadMulti(reader, DEBUG_INFO_ATTRIBUTES);
|
||||
LineNumberTableAttr linesAttr = attrs.get(JavaAttrType.LINE_NUMBER_TABLE);
|
||||
LocalVarsAttr varsAttr = attrs.get(JavaAttrType.LOCAL_VAR_TABLE);
|
||||
if (linesAttr == null && varsAttr == null) {
|
||||
@@ -162,7 +168,7 @@ public class JavaCodeReader implements ICodeReader {
|
||||
|
||||
@Override
|
||||
public List<ITry> getTries() {
|
||||
skipToTries();
|
||||
jumpToTries();
|
||||
int excTableLen = reader.readU2();
|
||||
if (excTableLen == 0) {
|
||||
return Collections.emptyList();
|
||||
@@ -212,7 +218,7 @@ public class JavaCodeReader implements ICodeReader {
|
||||
}
|
||||
|
||||
private Set<Integer> getExcHandlers() {
|
||||
skipToTries();
|
||||
jumpToTries();
|
||||
int excTableLen = reader.readU2();
|
||||
if (excTableLen == 0) {
|
||||
return Collections.emptySet();
|
||||
@@ -227,9 +233,13 @@ public class JavaCodeReader implements ICodeReader {
|
||||
return set;
|
||||
}
|
||||
|
||||
private void skipToTries() {
|
||||
private void jumpToTries() {
|
||||
reader.absPos(codeOffset + 4);
|
||||
int codeSize = reader.readU4();
|
||||
reader.skip(codeSize);
|
||||
reader.skip(reader.readU4()); // code length
|
||||
}
|
||||
|
||||
private void jumpToCodeAttributes() {
|
||||
jumpToTries();
|
||||
reader.skip(reader.readU2() * 8); // exceptions table
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -4,7 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.plugins.input.insns.InsnIndexType;
|
||||
import jadx.api.plugins.input.insns.Opcode;
|
||||
import jadx.plugins.input.java.data.code.StackState.SVType;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackValueType;
|
||||
import jadx.plugins.input.java.data.code.decoders.IJavaInsnDecoder;
|
||||
import jadx.plugins.input.java.data.code.decoders.InvokeDecoder;
|
||||
import jadx.plugins.input.java.data.code.decoders.LoadConstDecoder;
|
||||
@@ -12,8 +12,8 @@ import jadx.plugins.input.java.data.code.decoders.LookupSwitchDecoder;
|
||||
import jadx.plugins.input.java.data.code.decoders.TableSwitchDecoder;
|
||||
import jadx.plugins.input.java.data.code.decoders.WideDecoder;
|
||||
|
||||
import static jadx.plugins.input.java.data.code.StackState.SVType.NARROW;
|
||||
import static jadx.plugins.input.java.data.code.StackState.SVType.WIDE;
|
||||
import static jadx.plugins.input.java.data.attributes.stack.StackValueType.NARROW;
|
||||
import static jadx.plugins.input.java.data.attributes.stack.StackValueType.WIDE;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public class JavaInsnsRegister {
|
||||
@@ -351,11 +351,11 @@ public class JavaInsnsRegister {
|
||||
};
|
||||
}
|
||||
|
||||
private static IJavaInsnDecoder oneRegWithResult(SVType type) {
|
||||
private static IJavaInsnDecoder oneRegWithResult(StackValueType type) {
|
||||
return s -> s.pop(1).push(0, type);
|
||||
}
|
||||
|
||||
private static IJavaInsnDecoder twoRegsWithResult(SVType type) {
|
||||
private static IJavaInsnDecoder twoRegsWithResult(StackValueType type) {
|
||||
return s -> s.pop(2).pop(1).push(0, type);
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ public class JavaInsnsRegister {
|
||||
private static void constInsn(JavaInsnInfo[] arr, int opcode, String name, Opcode apiOpcode, long literal) {
|
||||
register(arr, opcode, name, 0, 1, apiOpcode, InsnIndexType.NONE, state -> {
|
||||
state.insn().setLiteral(literal);
|
||||
state.push(0, apiOpcode == Opcode.CONST_WIDE ? SVType.WIDE : NARROW);
|
||||
state.push(0, apiOpcode == Opcode.CONST_WIDE ? StackValueType.WIDE : NARROW);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+19
-16
@@ -2,24 +2,18 @@ package jadx.plugins.input.java.data.code;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackFrame;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackValueType;
|
||||
|
||||
public class StackState {
|
||||
|
||||
/**
|
||||
* Stack value type
|
||||
*/
|
||||
public enum SVType {
|
||||
NARROW, // int, float, etc
|
||||
WIDE, // long, double
|
||||
}
|
||||
|
||||
private int pos = -1;
|
||||
private final SVType[] stack;
|
||||
private final StackValueType[] stack;
|
||||
|
||||
public StackState(int maxStack) {
|
||||
this.stack = new SVType[maxStack];
|
||||
this.stack = new StackValueType[maxStack];
|
||||
}
|
||||
|
||||
private StackState(int pos, SVType[] stack) {
|
||||
private StackState(int pos, StackValueType[] stack) {
|
||||
this.pos = pos;
|
||||
this.stack = stack;
|
||||
}
|
||||
@@ -28,6 +22,15 @@ public class StackState {
|
||||
return new StackState(pos, Arrays.copyOf(stack, stack.length));
|
||||
}
|
||||
|
||||
public StackState fillFromFrame(StackFrame frame) {
|
||||
int stackSize = frame.getStackSize();
|
||||
this.pos = stackSize - 1;
|
||||
if (stackSize > 0) {
|
||||
System.arraycopy(frame.getStackValueTypes(), 0, this.stack, 0, stackSize);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public int peek() {
|
||||
return pos;
|
||||
}
|
||||
@@ -36,15 +39,15 @@ public class StackState {
|
||||
return pos - at;
|
||||
}
|
||||
|
||||
public SVType peekTypeAt(int at) {
|
||||
public StackValueType peekTypeAt(int at) {
|
||||
int p = pos - at;
|
||||
if (checkStackIndex(p)) {
|
||||
return stack[p];
|
||||
}
|
||||
return SVType.NARROW;
|
||||
return StackValueType.NARROW;
|
||||
}
|
||||
|
||||
public int insert(int at, SVType type) {
|
||||
public int insert(int at, StackValueType type) {
|
||||
int p = pos - at;
|
||||
System.arraycopy(stack, p, stack, p + 1, at);
|
||||
stack[p] = type;
|
||||
@@ -52,7 +55,7 @@ public class StackState {
|
||||
return p;
|
||||
}
|
||||
|
||||
public int push(SVType type) {
|
||||
public int push(StackValueType type) {
|
||||
int p = ++pos;
|
||||
if (checkStackIndex(p)) {
|
||||
stack[p] = type;
|
||||
|
||||
+6
-6
@@ -4,9 +4,9 @@ import jadx.api.plugins.input.insns.Opcode;
|
||||
import jadx.plugins.input.java.data.ConstPoolReader;
|
||||
import jadx.plugins.input.java.data.ConstantType;
|
||||
import jadx.plugins.input.java.data.DataReader;
|
||||
import jadx.plugins.input.java.data.attributes.stack.StackValueType;
|
||||
import jadx.plugins.input.java.data.code.CodeDecodeState;
|
||||
import jadx.plugins.input.java.data.code.JavaInsnData;
|
||||
import jadx.plugins.input.java.data.code.StackState.SVType;
|
||||
import jadx.plugins.input.java.utils.JavaClassParseException;
|
||||
|
||||
public class LoadConstDecoder implements IJavaInsnDecoder {
|
||||
@@ -33,32 +33,32 @@ public class LoadConstDecoder implements IJavaInsnDecoder {
|
||||
case FLOAT:
|
||||
insn.setLiteral(constPoolReader.readU4());
|
||||
insn.setOpcode(Opcode.CONST);
|
||||
state.push(0, SVType.NARROW);
|
||||
state.push(0, StackValueType.NARROW);
|
||||
break;
|
||||
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
insn.setLiteral(constPoolReader.readU8());
|
||||
insn.setOpcode(Opcode.CONST_WIDE);
|
||||
state.push(0, SVType.WIDE);
|
||||
state.push(0, StackValueType.WIDE);
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
insn.setIndex(constPoolReader.readU2());
|
||||
insn.setOpcode(Opcode.CONST_STRING);
|
||||
state.push(0, SVType.NARROW);
|
||||
state.push(0, StackValueType.NARROW);
|
||||
break;
|
||||
|
||||
case UTF8:
|
||||
insn.setIndex(index);
|
||||
insn.setOpcode(Opcode.CONST_STRING);
|
||||
state.push(0, SVType.NARROW);
|
||||
state.push(0, StackValueType.NARROW);
|
||||
break;
|
||||
|
||||
case CLASS:
|
||||
insn.setIndex(index);
|
||||
insn.setOpcode(Opcode.CONST_CLASS);
|
||||
state.push(0, SVType.NARROW);
|
||||
state.push(0, StackValueType.NARROW);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
+1
-3
@@ -1,6 +1,5 @@
|
||||
package jadx.plugins.input.java.utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -22,8 +21,7 @@ public class DisasmUtils {
|
||||
}
|
||||
|
||||
private static String useRaung(byte[] bytes) {
|
||||
return RaungDisasm.create()
|
||||
.executeForInputStream(new ByteArrayInputStream(bytes));
|
||||
return RaungDisasm.create().executeForBytes(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class RaungConvert implements Closeable {
|
||||
this.tmpJar = Files.createTempFile("jadx-raung-", ".jar");
|
||||
RaungAsm.create()
|
||||
.output(tmpJar)
|
||||
.inputs(input)
|
||||
.inputs(raungInputs)
|
||||
.execute();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in New Issue
Block a user