Home | History | Annotate | Download | only in util
      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 
     37     public int getAccess() {
     38         return access;
     39     }
     40 
     41     public String getClassName() {
     42         return thisClass;
     43     }
     44 
     45     public String getSuperName() {
     46         return superClass;
     47     }
     48 
     49     public String[] getInterfaces() {
     50         return interfaces;
     51     }
     52 
     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 
    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 
    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;
    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 
    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 
    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     {
    176         public MyByteArrayInputStream() {
    177             super(new byte[0]);
    178         }
    179 
    180         public void readFrom(byte[] buf, int pos) {
    181             this.buf = buf;
    182             this.pos = pos;
    183             count = buf.length;
    184         }
    185     }
    186 }
    187