blob: 8e58951143c79a8bdebda72ab2e158fd1df633e6 [file] [log] [blame]
/*
* 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;
}
}
}