1 /** 2 * Copyright 2007 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.tonicsystems.jarjar.util; 18 19 import java.io.*; 20 import java.lang.reflect.Array; 21 import java.util.*; 22 23 public class ClassHeaderReader 24 { 25 private int access; 26 private String thisClass; 27 private String superClass; 28 private String[] interfaces; 29 30 private InputStream in; 31 private byte[] b = new byte[0x2000]; 32 private int[] items = new int[1000]; 33 private int bsize = 0; 34 private MyByteArrayInputStream bin = new MyByteArrayInputStream(); 35 private DataInputStream data = new DataInputStream(bin); 36 getAccess()37 public int getAccess() { 38 return access; 39 } 40 getClassName()41 public String getClassName() { 42 return thisClass; 43 } 44 getSuperName()45 public String getSuperName() { 46 return superClass; 47 } 48 getInterfaces()49 public String[] getInterfaces() { 50 return interfaces; 51 } 52 read(InputStream in)53 public void read(InputStream in) throws IOException { 54 try { 55 this.in = in; 56 bsize = 0; 57 access = 0; 58 thisClass = superClass = null; 59 interfaces = null; 60 61 try { 62 buffer(4); 63 } catch (IOException e) { 64 // ignore 65 } 66 if (b[0] != (byte)0xCA || b[1] != (byte)0xFE || b[2] != (byte)0xBA || b[3] != (byte)0xBE) 67 throw new ClassFormatError("Bad magic number"); 68 69 buffer(6); 70 readUnsignedShort(4); // minorVersion 71 readUnsignedShort(6); // majorVersion 72 // TODO: check version 73 int constant_pool_count = readUnsignedShort(8); 74 items = (int[])resizeArray(items, constant_pool_count); 75 76 int index = 10; 77 for (int i = 1; i < constant_pool_count; i++) { 78 int size; 79 buffer(index + 3); // TODO: reduce calls to buffer 80 int tag = b[index]; 81 items[i] = index + 1; 82 switch (tag) { 83 case 9: // Fieldref 84 case 10: // Methodref 85 case 11: // InterfaceMethodref 86 case 3: // Integer 87 case 4: // Float 88 case 12: // NameAndType 89 size = 4; 90 break; 91 case 5: // Long 92 case 6: // Double 93 size = 8; 94 i++; 95 break; 96 case 1: // Utf8 97 size = 2 + readUnsignedShort(index + 1); 98 break; 99 case 7: // Class 100 case 8: // String 101 size = 2; 102 break; 103 default: 104 throw new IllegalStateException("Unknown constant pool tag " + tag); 105 } 106 index += size + 1; 107 } 108 buffer(index + 8); 109 access = readUnsignedShort(index); 110 thisClass = readClass(index + 2); 111 superClass = readClass(index + 4); 112 int interfaces_count = readUnsignedShort(index + 6); 113 114 index += 8; 115 buffer(index + interfaces_count * 2); 116 interfaces = new String[interfaces_count]; 117 for (int i = 0; i < interfaces_count; i++) { 118 interfaces[i] = readClass(index); 119 index += 2; 120 } 121 } finally { 122 in.close(); 123 } 124 } 125 readClass(int index)126 private String readClass(int index) throws IOException { 127 index = readUnsignedShort(index); 128 if (index == 0) 129 return null; 130 index = readUnsignedShort(items[index]); 131 bin.readFrom(b, items[index]); 132 return data.readUTF(); 133 } 134 readUnsignedShort(int index)135 private int readUnsignedShort(int index) { 136 byte[] b = this.b; 137 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); 138 } 139 140 private static final int CHUNK = 2048; buffer(int amount)141 private void buffer(int amount) throws IOException { 142 if (amount > b.length) 143 b = (byte[])resizeArray(b, b.length * 2); 144 if (amount > bsize) { 145 int rounded = (int)(CHUNK * Math.ceil((float)amount / CHUNK)); 146 bsize += read(in, b, bsize, rounded - bsize); 147 if (amount > bsize) 148 throw new EOFException(); 149 } 150 } 151 read(InputStream in, byte[] b, int off, int len)152 private static int read(InputStream in, byte[] b, int off, int len) throws IOException { 153 int total = 0; 154 while (total < len) { 155 int result = in.read(b, off + total, len - total); 156 if (result == -1) 157 break; 158 total += result; 159 } 160 return total; 161 } 162 resizeArray(Object array, int length)163 private static Object resizeArray(Object array, int length) 164 { 165 if (Array.getLength(array) < length) { 166 Object newArray = Array.newInstance(array.getClass().getComponentType(), length); 167 System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); 168 return newArray; 169 } else { 170 return array; 171 } 172 } 173 174 private static class MyByteArrayInputStream extends ByteArrayInputStream 175 { MyByteArrayInputStream()176 public MyByteArrayInputStream() { 177 super(new byte[0]); 178 } 179 readFrom(byte[] buf, int pos)180 public void readFrom(byte[] buf, int pos) { 181 this.buf = buf; 182 this.pos = pos; 183 count = buf.length; 184 } 185 } 186 } 187