1/*
2 * Copyright (c) 2012, 2016, 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
29public class ClassToInterfaceConverter implements ClassFilePreprocessor {
30
31    private String whichClass;
32
33    public ClassToInterfaceConverter(String className) {
34        this.whichClass = className;
35    }
36
37    private boolean utf8Matches(ClassFile.CpEntry entry, String v) {
38        if (!(entry instanceof ClassFile.CpUtf8)) {
39            return false;
40        }
41        ClassFile.CpUtf8 utf8 = (ClassFile.CpUtf8)entry;
42        if (v.length() != utf8.bytes.length) {
43            return false;
44        }
45        for (int i = 0; i < v.length(); ++i) {
46            if (v.charAt(i) != utf8.bytes[i]) {
47                return false;
48            }
49        }
50        return true;
51    }
52
53    private void convertToInterface(ClassFile cf) {
54        cf.access_flags = 0x0601; // ACC_INTERFACE | ACC_ABSTRACT | ACC_PUBLIC
55        ArrayList<ClassFile.Method> new_methods = new ArrayList<>();
56        // Find <init> method and delete it
57        for (int i = 0; i < cf.methods.size(); ++i) {
58            ClassFile.Method method = cf.methods.get(i);
59            ClassFile.CpEntry name = cf.constant_pool.get(method.name_index);
60            if (!utf8Matches(name, "<init>")) {
61                new_methods.add(method);
62            }
63        }
64        cf.methods = new_methods;
65        //  Convert method tag. Find Methodref, which is not "<init>" and only invoked by other methods
66        //  in the interface, convert it to InterfaceMethodref
67        ArrayList<ClassFile.CpEntry> cpool = new ArrayList<>();
68        for (int i = 0; i < cf.constant_pool.size(); i++) {
69            ClassFile.CpEntry ce = cf.constant_pool.get(i);
70            if (ce instanceof ClassFile.CpMethodRef) {
71                ClassFile.CpMethodRef me = (ClassFile.CpMethodRef)ce;
72                ClassFile.CpNameAndType nameType = (ClassFile.CpNameAndType)cf.constant_pool.get(me.name_and_type_index);
73                ClassFile.CpEntry name = cf.constant_pool.get(nameType.name_index);
74                if (!utf8Matches(name, "<init>") && cf.this_class == me.class_index) {
75                    ClassFile.CpInterfaceMethodRef newEntry = new ClassFile.CpInterfaceMethodRef();
76                    newEntry.class_index = me.class_index;
77                    newEntry.name_and_type_index = me.name_and_type_index;
78                    ce = newEntry;
79                }
80            }
81            cpool.add(ce);
82        }
83        cf.constant_pool = cpool;
84    }
85
86    public byte[] preprocess(String classname, byte[] bytes) {
87        ClassFile cf = new ClassFile(bytes);
88
89        ClassFile.CpEntry entry = cf.constant_pool.get(cf.this_class);
90        ClassFile.CpEntry name = cf.constant_pool.get(
91            ((ClassFile.CpClass)entry).name_index);
92        if (utf8Matches(name, whichClass)) {
93            convertToInterface(cf);
94            return cf.toByteArray();
95        } else {
96            return bytes; // unmodified
97        }
98    }
99
100/*
101    public static void main(String argv[]) throws Exception {
102        File input = new File(argv[0]);
103        byte[] buffer = new byte[(int)input.length()];
104        new FileInputStream(input).read(buffer);
105
106        ClassFilePreprocessor cfp = new ClassToInterfaceConverter("Hello");
107        byte[] cf = cfp.preprocess(argv[0], buffer);
108        new FileOutputStream(argv[0] + ".mod").write(cf);
109    }
110*/
111}
112