Skip to content

Instantly share code, notes, and snippets.

@Col-E
Created May 3, 2020 23:23
Show Gist options
  • Select an option

  • Save Col-E/f945f2dd7caca1d79415aa10e2a1663a to your computer and use it in GitHub Desktop.

Select an option

Save Col-E/f945f2dd7caca1d79415aa10e2a1663a to your computer and use it in GitHub Desktop.

Revisions

  1. Col-E created this gist May 3, 2020.
    148 changes: 148 additions & 0 deletions MinReader.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,148 @@
    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;
    }
    }
    }