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 
24 package selectionresolution;
25 
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import jdk.internal.org.objectweb.asm.ClassWriter;
29 import jdk.internal.org.objectweb.asm.Opcodes;
30 
31 public 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      */
ClassConstruct(String name, String extending, int access, int classFileVersion, int index, String... interfaces)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      */
getName()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      */
getDottedName()74     public String getDottedName() {
75         return name.replace("/", ".");
76     }
77 
getPackageName()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 
getClassName()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      */
addMethod(String name, String descriptor, int access)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      */
addMethod(String name, String descriptor, int access, ClassBuilder.ExecutionMode execMode)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      */
addTestMethod(int access)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      */
generateBytes()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      */
writeClass(final File dir)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 
isInterface()160     public boolean isInterface() {
161         return isInterface;
162     }
163 
getIndex()164     public Integer getIndex() {
165         return index;
166     }
167 }
168