Created
May 3, 2020 23:23
-
-
Save Col-E/f945f2dd7caca1d79415aa10e2a1663a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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