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