1/*
2 * Copyright (c) 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 selectionresolution;
25
26import java.io.File;
27import java.io.FileOutputStream;
28import jdk.internal.org.objectweb.asm.ClassWriter;
29import jdk.internal.org.objectweb.asm.Opcodes;
30
31public abstract class ClassConstruct {
32    private final ClassWriter cw;
33    private final String name;
34    private final boolean isInterface;
35    private final int index;
36
37    /**
38     * Base constructor for building a Class or Interface
39     * @param name Name of Class/Interface, including package name
40     * @param extending Name of extending Class if any
41     * @param access Access for Class/Interface
42     * @param classFileVersion Class file version
43     * @param interfaces Interface implemented
44     */
45    public ClassConstruct(String name,
46                          String extending,
47                          int access,
48                          int classFileVersion,
49                          int index,
50                          String... interfaces) {
51        this.name = name;
52        isInterface = (access & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE;
53        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
54        cw.visit(classFileVersion, access, name, null, extending, interfaces == null ?  new String[] { } : interfaces);
55        this.index = index;
56    }
57
58    /**
59     * Get full Class/Interface name including package name, as it
60     * should appear in a classfile.
61     *
62     * @return The full Class/Interface name including package name
63     */
64    public String getName() {
65        return name;
66    }
67
68    /**
69     * Get the name of the class, including package as it would appear
70     * in Java source.
71     *
72     * @return The name of the class as it would appear in Java source.
73     */
74    public String getDottedName() {
75        return name.replace("/", ".");
76    }
77
78    public String getPackageName() {
79        final int idx = name.lastIndexOf('/');
80        if (idx != -1) {
81            return name.substring(0, name.indexOf('/'));
82        } else {
83            return null;
84        }
85    }
86
87    public String getClassName() {
88        final int idx = name.lastIndexOf('/');
89        if (idx != -1) {
90            return name.substring(name.indexOf('/'));
91        } else {
92            return name;
93        }
94    }
95
96    /**
97     * Add a method, no code associated with it yet
98     * @param name Name of method
99     * @param descriptor Descriptor for method
100     * @param access Access for the method
101     * @return Method object that can be used for constructing a method body
102     */
103    public Method addMethod(String name,
104                            String descriptor,
105                            int access) {
106        return addMethod(name, descriptor, access, null);
107    }
108
109    /**
110     * Add a method, no code associated with it yet
111     * @param name Name of method
112     * @param descriptor Descriptor for method
113     * @param access Access for the method
114     * @param execMode The execution mode for the method.
115     * @return Method object that can be used for constructing a method body
116     */
117    public Method addMethod(String name,
118                            String descriptor,
119                            int access,
120                            ClassBuilder.ExecutionMode execMode) {
121        return new Method(this, cw, name, descriptor, access, execMode);
122    }
123
124    /**
125     * Adds a m()LTestObject; method which returns null unless the method is abstract
126     * @param access Access for the method
127     */
128    public void addTestMethod(int access) {
129        Method m = new Method(this, cw, Method.defaultMethodName, Method.defaultMethodDescriptor, access, null);
130        if ((access & Opcodes.ACC_ABSTRACT) != Opcodes.ACC_ABSTRACT) {
131            m.makeDefaultMethod();
132        }
133    }
134
135    /**
136     * Construct the class to a byte[]
137     * @return byte[] with class file
138     */
139    public byte[] generateBytes() {
140        cw.visitEnd();
141        return cw.toByteArray();
142    }
143
144    /**
145     * Write out a class to a file in the specified directory.
146     *
147     * @param dir Directory to which to write out the file.
148     */
149    public void writeClass(final File dir) throws Exception {
150        final String pkgname = getPackageName();
151        final File pkgdir = pkgname != null ? new File(dir, getPackageName()) : dir;
152        pkgdir.mkdirs();
153        final File out = new File(pkgdir, getClassName() + ".class");
154        out.createNewFile();
155        try (final FileOutputStream fos = new FileOutputStream(out)) {
156            fos.write(generateBytes());
157        }
158    }
159
160    public boolean isInterface() {
161        return isInterface;
162    }
163
164    public Integer getIndex() {
165        return index;
166    }
167}
168