diff options
Diffstat (limited to 'src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java')
-rw-r--r-- | src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java new file mode 100644 index 0000000..6e448be --- /dev/null +++ b/src/main/com/tonicsystems/jarjar/util/ClassHeaderReader.java @@ -0,0 +1,186 @@ +/** + * Copyright 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tonicsystems.jarjar.util; + +import java.io.*; +import java.lang.reflect.Array; +import java.util.*; + +public class ClassHeaderReader +{ + private int access; + private String thisClass; + private String superClass; + private String[] interfaces; + + private InputStream in; + private byte[] b = new byte[0x2000]; + private int[] items = new int[1000]; + private int bsize = 0; + private MyByteArrayInputStream bin = new MyByteArrayInputStream(); + private DataInputStream data = new DataInputStream(bin); + + public int getAccess() { + return access; + } + + public String getClassName() { + return thisClass; + } + + public String getSuperName() { + return superClass; + } + + public String[] getInterfaces() { + return interfaces; + } + + public void read(InputStream in) throws IOException { + try { + this.in = in; + bsize = 0; + access = 0; + thisClass = superClass = null; + interfaces = null; + + try { + buffer(4); + } catch (IOException e) { + // ignore + } + if (b[0] != (byte)0xCA || b[1] != (byte)0xFE || b[2] != (byte)0xBA || b[3] != (byte)0xBE) + throw new ClassFormatError("Bad magic number"); + + buffer(6); + readUnsignedShort(4); // minorVersion + readUnsignedShort(6); // majorVersion + // TODO: check version + int constant_pool_count = readUnsignedShort(8); + items = (int[])resizeArray(items, constant_pool_count); + + int index = 10; + for (int i = 1; i < constant_pool_count; i++) { + int size; + buffer(index + 3); // TODO: reduce calls to buffer + int tag = b[index]; + items[i] = index + 1; + switch (tag) { + case 9: // Fieldref + case 10: // Methodref + case 11: // InterfaceMethodref + case 3: // Integer + case 4: // Float + case 12: // NameAndType + size = 4; + break; + case 5: // Long + case 6: // Double + size = 8; + i++; + break; + case 1: // Utf8 + size = 2 + readUnsignedShort(index + 1); + break; + case 7: // Class + case 8: // String + size = 2; + break; + default: + throw new IllegalStateException("Unknown constant pool tag " + tag); + } + index += size + 1; + } + buffer(index + 8); + access = readUnsignedShort(index); + thisClass = readClass(index + 2); + superClass = readClass(index + 4); + int interfaces_count = readUnsignedShort(index + 6); + + index += 8; + buffer(index + interfaces_count * 2); + interfaces = new String[interfaces_count]; + for (int i = 0; i < interfaces_count; i++) { + interfaces[i] = readClass(index); + index += 2; + } + } finally { + in.close(); + } + } + + private String readClass(int index) throws IOException { + index = readUnsignedShort(index); + if (index == 0) + return null; + index = readUnsignedShort(items[index]); + bin.readFrom(b, items[index]); + return data.readUTF(); + } + + private int readUnsignedShort(int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + private static final int CHUNK = 2048; + private void buffer(int amount) throws IOException { + if (amount > b.length) + b = (byte[])resizeArray(b, b.length * 2); + if (amount > bsize) { + int rounded = (int)(CHUNK * Math.ceil((float)amount / CHUNK)); + bsize += read(in, b, bsize, rounded - bsize); + if (amount > bsize) + throw new EOFException(); + } + } + + private static int read(InputStream in, byte[] b, int off, int len) throws IOException { + int total = 0; + while (total < len) { + int result = in.read(b, off + total, len - total); + if (result == -1) + break; + total += result; + } + return total; + } + + private static Object resizeArray(Object array, int length) + { + if (Array.getLength(array) < length) { + Object newArray = Array.newInstance(array.getClass().getComponentType(), length); + System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); + return newArray; + } else { + return array; + } + } + + private static class MyByteArrayInputStream extends ByteArrayInputStream + { + public MyByteArrayInputStream() { + super(new byte[0]); + } + + public void readFrom(byte[] buf, int pos) { + this.buf = buf; + this.pos = pos; + count = buf.length; + } + } +} |