Skip to content

Instantly share code, notes, and snippets.

@Col-E
Created May 3, 2020 23:23
Show Gist options
  • Save Col-E/f945f2dd7caca1d79415aa10e2a1663a to your computer and use it in GitHub Desktop.
Save Col-E/f945f2dd7caca1d79415aa10e2a1663a to your computer and use it in GitHub Desktop.
package me.coley.recaf.parse.bytecode;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MinimalReader {
// Backing content
private final byte[] code;
private final List<ConstantType> poolTypes = new ArrayList<>();
private final List<Integer> interfaceIndices = new ArrayList<>();
private final Map<Integer, String> strings = new HashMap<>();
private final Map<Integer, Integer> redirects = new HashMap<>();
private final int classIndex;
private final int superIndex;
// Public
public final int minor;
public final int major;
public final int access;
public MinimalReader(byte[] code) throws IOException {
this.code = code;
DataInputStream is = new DataInputStream(new ByteArrayInputStream(code));
if (is.readInt() != 0xCAFEBABE)
throw new IOException("Does not start with 0xCAFEBABE");
// Read version information
minor = is.readUnsignedShort();
major = is.readUnsignedShort();
// Read the constant pool
int poolIndex = 1;
int poolLength = is.readUnsignedShort();
while (poolIndex < poolLength) {
int tag = is.readUnsignedByte();
ConstantType type = ConstantType.fromTag(tag);
if (type == null)
throw new IllegalStateException("Unknown tag: " + tag);
switch(type) {
case UTF8:
strings.put(poolIndex, is.readUTF());
break;
case STRING:
case METHOD_TYPE:
case MODULE:
case PACKAGE:
case CLASS:
redirects.put(poolIndex, is.readUnsignedShort());
break;
case METHOD_HANDLE:
is.skipBytes(3);
break;
case FIELD:
case INT:
case FLOAT:
case NAME_TYPE:
case INVOKEDYNAMIC:
case DYNAMIC:
case METHOD:
case INTERFACE_METHOD:
is.skipBytes(4);
break;
case LONG:
case DOUBLE:
is.skipBytes(8);
break;
}
poolTypes.add(type);
poolIndex++;
if (type.isWide()) {
poolTypes.add(null);
poolIndex++;
}
}
// Read access and index information
access = is.readUnsignedShort();
classIndex = is.readUnsignedShort();
superIndex = is.readUnsignedShort();
// Read interfaces
int interfaceLength = is.readUnsignedShort();
for(int i = 0; i < interfaceLength; i++)
interfaceIndices.add(is.readUnsignedShort());
}
/**
* @return Class name.
*/
public String getName() {
return strings.get(redirects.get(classIndex));
}
/**
* @return Class name.
*/
public String getSuperName() {
return strings.get(redirects.get(superIndex));
}
/**
* Enumeration for handling constant pool entry types.
*
* @author Matt
*/
private enum ConstantType {
UTF8(1),
INT(3),
FLOAT(4),
LONG(5),
DOUBLE(6),
CLASS(7),
STRING(8),
FIELD(9),
METHOD(10),
INTERFACE_METHOD(11),
NAME_TYPE(12),
METHOD_HANDLE(15),
METHOD_TYPE(16),
DYNAMIC(17),
INVOKEDYNAMIC(18),
MODULE(19),
PACKAGE(20);
private static Map<Integer, ConstantType> typeMap;
private final int tag;
ConstantType(int tag) {
this.tag = tag;
register(this);
}
private static ConstantType fromTag(int tag) {
return typeMap.get(tag);
}
private void register(ConstantType type) {
if (typeMap == null) {
typeMap = new HashMap<>();
}
typeMap.put(tag, type);
}
private boolean isWide() {
return this == LONG || this == DOUBLE;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment