1/*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package separate;
25
26import java.io.*;
27import java.util.*;
28
29class CfInputStream extends ByteArrayInputStream {
30    private int ct;
31    public CfInputStream(byte[] input) {
32        super(input);
33    }
34
35    byte u1() { return (byte)read(); }
36    short u2() {
37        int b0 = read() << 8;
38        int b1 = read();
39        return (short)(b0 | b1);
40    }
41    int u4() {
42        int b0 = read() << 24;
43        int b1 = read() << 16;
44        int b2 = read() << 8;
45        int b3 = read();
46        return b0 | b1 | b2 | b3;
47    }
48    byte[] array(int count) {
49        byte[] ret = new byte[count];
50        read(ret, 0, count);
51        return ret;
52    }
53};
54
55class CfOutputStream extends ByteArrayOutputStream {
56    void u1(byte b) { write((int)b); }
57    void u2(short s) {
58        write((s >> 8) & 0xff);
59        write(s & 0xff);
60    }
61    void u4(int i) {
62        write((i >> 24) & 0xff);
63        write((i >> 16) & 0xff);
64        write((i >> 8) & 0xff);
65        write(i & 0xff);
66    }
67    void array(byte[] a) {
68        write(a, 0, a.length);
69    }
70
71    public byte[] toByteArray() { return super.toByteArray(); }
72};
73
74// A quick and dirty class file parser and representation
75public class ClassFile {
76
77    int magic;
78    short minor_version;
79    short major_version;
80    ArrayList<CpEntry> constant_pool;
81    short access_flags;
82    short this_class;
83    short super_class;
84    ArrayList<Interface> interfaces;
85    ArrayList<Field> fields;
86    ArrayList<Method> methods;
87    ArrayList<Attribute> attributes;
88
89    ClassFile(byte[] cf) {
90        CfInputStream in = new CfInputStream(cf);
91
92        magic = in.u4();
93        minor_version = in.u2();
94        major_version = in.u2();
95
96        short cpCount = in.u2();
97        constant_pool = new ArrayList<>();
98        constant_pool.add(new CpNull());
99        for (int i = 1; i < cpCount; ++i) {
100            constant_pool.add(CpEntry.newCpEntry(in));
101        }
102
103        access_flags = in.u2();
104        this_class = in.u2();
105        super_class = in.u2();
106
107        short ifaceCount = in.u2();
108        interfaces = new ArrayList<>();
109        for (int i = 0; i < ifaceCount; ++i) {
110            interfaces.add(new Interface(in));
111        }
112
113        short fieldCount = in.u2();
114        fields = new ArrayList<>();
115        for (int i = 0; i < fieldCount; ++i) {
116            fields.add(new Field(in));
117        }
118
119        short methodCount = in.u2();
120        methods = new ArrayList<>();
121        for (int i = 0; i < methodCount; ++i) {
122            methods.add(new Method(in));
123        }
124
125        short attributeCount = in.u2();
126        attributes = new ArrayList<>();
127        for (int i = 0; i < attributeCount; ++i) {
128            attributes.add(new Attribute(in));
129        }
130    }
131
132    byte[] toByteArray() {
133        CfOutputStream out = new CfOutputStream();
134
135        out.u4(magic);
136        out.u2(minor_version);
137        out.u2(major_version);
138
139        out.u2((short)(constant_pool.size()));
140        for (CpEntry cp : constant_pool) {
141            cp.write(out);
142        }
143
144        out.u2(access_flags);
145        out.u2(this_class);
146        out.u2(super_class);
147
148        out.u2((short)interfaces.size());
149        for (Interface iface : interfaces) {
150            iface.write(out);
151        }
152
153        out.u2((short)fields.size());
154        for (Field field : fields) {
155            field.write(out);
156        }
157
158        out.u2((short)methods.size());
159        for (Method method : methods) {
160            method.write(out);
161        }
162
163        out.u2((short)attributes.size());
164        for (Attribute attribute : attributes) {
165            attribute.write(out);
166        }
167
168        return out.toByteArray();
169    }
170
171    static abstract class CpEntry {
172        byte tag;
173
174        CpEntry(byte t) { tag = t; }
175        void write(CfOutputStream out) {
176            out.u1(tag);
177        }
178
179        static CpEntry newCpEntry(CfInputStream in) {
180            byte tag = in.u1();
181            switch (tag) {
182                case CpUtf8.TAG: return new CpUtf8(in);
183                case CpInteger.TAG: return new CpInteger(in);
184                case CpFloat.TAG: return new CpFloat(in);
185                case CpLong.TAG: return new CpLong(in);
186                case CpDouble.TAG: return new CpDouble(in);
187                case CpClass.TAG: return new CpClass(in);
188                case CpString.TAG: return new CpString(in);
189                case CpFieldRef.TAG: return new CpFieldRef(in);
190                case CpMethodRef.TAG: return new CpMethodRef(in);
191                case CpInterfaceMethodRef.TAG:
192                    return new CpInterfaceMethodRef(in);
193                case CpNameAndType.TAG: return new CpNameAndType(in);
194                case CpMethodHandle.TAG: return new CpMethodHandle(in);
195                case CpMethodType.TAG: return new CpMethodType(in);
196                case CpInvokeDynamic.TAG: return new CpInvokeDynamic(in);
197                default: throw new RuntimeException("Bad cp entry tag: " + tag);
198            }
199        }
200    }
201
202    static class CpNull extends CpEntry {
203        CpNull() { super((byte)0); }
204        CpNull(CfInputStream in) { super((byte)0); }
205        void write(CfOutputStream out) {}
206    }
207
208    static class CpUtf8 extends CpEntry {
209        static final byte TAG = 1;
210        byte[] bytes;
211
212        CpUtf8() { super(TAG); }
213        CpUtf8(CfInputStream in) {
214            this();
215            short length = in.u2();
216            bytes = in.array(length);
217        }
218        void write(CfOutputStream out) {
219            super.write(out);
220            out.u2((short)bytes.length);
221            out.array(bytes);
222        }
223    }
224
225    static class CpU4Constant extends CpEntry {
226        byte[] bytes;
227
228        CpU4Constant(byte tag) { super(tag); }
229        CpU4Constant(byte tag, CfInputStream in) {
230            this(tag);
231            bytes = in.array(4);
232        }
233        void write(CfOutputStream out) { super.write(out); out.array(bytes); }
234    }
235    static class CpInteger extends CpU4Constant {
236        static final byte TAG = 3;
237        CpInteger() { super(TAG); }
238        CpInteger(CfInputStream in) { super(TAG, in); }
239    }
240    static class CpFloat extends CpU4Constant {
241        static final byte TAG = 4;
242        CpFloat() { super(TAG); }
243        CpFloat(CfInputStream in) { super(TAG, in); }
244    }
245
246    static class CpU8Constant extends CpEntry {
247        byte[] bytes;
248
249        CpU8Constant(byte tag) { super(tag); }
250        CpU8Constant(byte tag, CfInputStream in) {
251            this(tag);
252            bytes = in.array(8);
253        }
254        void write(CfOutputStream out) { super.write(out); out.array(bytes); }
255    }
256    static class CpLong extends CpU8Constant {
257        static final byte TAG = 5;
258        CpLong() { super(TAG); }
259        CpLong(CfInputStream in) { super(TAG, in); }
260    }
261    static class CpDouble extends CpU8Constant {
262        static final byte TAG = 6;
263        CpDouble() { super(TAG); }
264        CpDouble(CfInputStream in) { super(TAG, in); }
265    }
266
267    static class CpClass extends CpEntry {
268        static final byte TAG = 7;
269        short name_index;
270
271        CpClass() { super(TAG); }
272        CpClass(CfInputStream in) { super(TAG); name_index = in.u2(); }
273        void write(CfOutputStream out) {
274            super.write(out);
275            out.u2(name_index);
276        }
277    }
278
279    static class CpString extends CpEntry {
280        static final byte TAG = 8;
281        short string_index;
282
283        CpString() { super(TAG); }
284        CpString(CfInputStream in) { super(TAG); string_index = in.u2(); }
285        void write(CfOutputStream out) {
286            super.write(out);
287            out.u2(string_index);
288        }
289    }
290
291    static class CpRef extends CpEntry {
292        short class_index;
293        short name_and_type_index;
294
295        CpRef(byte tag) { super(tag); }
296        CpRef(byte tag, CfInputStream in) {
297            this(tag);
298            class_index = in.u2();
299            name_and_type_index = in.u2();
300        }
301        void write(CfOutputStream out) {
302            super.write(out);
303            out.u2(class_index);
304            out.u2(name_and_type_index);
305        }
306    }
307    static class CpFieldRef extends CpRef {
308        static final byte TAG = 9;
309        CpFieldRef() { super(TAG); }
310        CpFieldRef(CfInputStream in) { super(TAG, in); }
311    }
312    static class CpMethodRef extends CpRef {
313        static final byte TAG = 10;
314        CpMethodRef() { super(TAG); }
315        CpMethodRef(CfInputStream in) { super(TAG, in); }
316    }
317    static class CpInterfaceMethodRef extends CpRef {
318        static final byte TAG = 11;
319        CpInterfaceMethodRef() { super(TAG); }
320        CpInterfaceMethodRef(CfInputStream in) { super(TAG, in); }
321    }
322
323    static class CpNameAndType extends CpEntry {
324        static final byte TAG = 12;
325        short name_index;
326        short descriptor_index;
327
328        CpNameAndType() { super(TAG); }
329        CpNameAndType(CfInputStream in) {
330            this();
331            name_index = in.u2();
332            descriptor_index = in.u2();
333        }
334        void write(CfOutputStream out) {
335            super.write(out);
336            out.u2(name_index);
337            out.u2(descriptor_index);
338        }
339    }
340
341    static class CpMethodHandle extends CpEntry {
342        static final byte TAG = 15;
343        byte reference_kind;
344        short reference_index;
345
346        CpMethodHandle() { super(TAG); }
347        CpMethodHandle(CfInputStream in) {
348            this();
349            reference_kind = in.u1();
350            reference_index = in.u2();
351        }
352        void write(CfOutputStream out) {
353            super.write(out);
354            out.u1(reference_kind);
355            out.u2(reference_index);
356        }
357    }
358
359    static class CpMethodType extends CpEntry {
360        static final byte TAG = 16;
361        short descriptor_index;
362
363        CpMethodType() { super(TAG); }
364        CpMethodType(CfInputStream in) {
365            this();
366            descriptor_index = in.u2();
367        }
368        void write(CfOutputStream out) {
369            super.write(out);
370            out.u2(descriptor_index);
371        }
372    }
373
374    static class CpInvokeDynamic extends CpEntry {
375        static final byte TAG = 18;
376        short bootstrap_index;
377        short name_and_type_index;
378
379        CpInvokeDynamic() { super(TAG); }
380        CpInvokeDynamic(CfInputStream in) {
381            this();
382            bootstrap_index = in.u2();
383            name_and_type_index = in.u2();
384        }
385        void write(CfOutputStream out) {
386            super.write(out);
387            out.u2(bootstrap_index);
388            out.u2(name_and_type_index);
389        }
390    }
391
392    static class Interface {
393        short index;
394
395        Interface() {}
396        Interface(CfInputStream in) { index = in.u2(); }
397        void write(CfOutputStream out) { out.u2(index); }
398    }
399
400    static class FieldOrMethod {
401        short access_flags;
402        short name_index;
403        short descriptor_index;
404        ArrayList<Attribute> attributes;
405
406        FieldOrMethod() { attributes = new ArrayList<>(); }
407        FieldOrMethod(CfInputStream in) {
408            access_flags = in.u2();
409            name_index = in.u2();
410            descriptor_index = in.u2();
411
412            short attrCount = in.u2();
413            attributes = new ArrayList<>();
414            for (int i = 0; i < attrCount; ++i) {
415                attributes.add(new Attribute(in));
416            }
417        }
418        void write(CfOutputStream out) {
419            out.u2(access_flags);
420            out.u2(name_index);
421            out.u2(descriptor_index);
422            out.u2((short)attributes.size());
423            for (Attribute attribute : attributes) { attribute.write(out); }
424        }
425    }
426
427    static class Field extends FieldOrMethod {
428        Field() {}
429        Field(CfInputStream in) { super(in); }
430    }
431    static class Method extends FieldOrMethod {
432        Method() {}
433        Method(CfInputStream in) { super(in); }
434    }
435
436    static class Attribute {
437        short attribute_name_index;
438        byte[] info;
439
440        Attribute() { info = new byte[0]; }
441        Attribute(CfInputStream in) {
442            attribute_name_index = in.u2();
443            int length = in.u4();
444            info = in.array(length);
445        }
446        void write(CfOutputStream out) {
447            out.u2(attribute_name_index);
448            out.u4(info.length);
449            out.array(info);
450        }
451    }
452}
453