1 /* 2 * Copyright (c) 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 */ 24 25 import static jdk.internal.org.objectweb.asm.Opcodes.*; 26 27 import java.util.ArrayList; 28 import java.util.Iterator; 29 30 /** 31 * Constructs classes and interfaces based on the information from a 32 * DefaultMethodTestCase 33 * 34 */ 35 public class ClassBuilder extends Builder { 36 private final ArrayList<ClassConstruct> classes; 37 38 // Add a class in every package to be able to instantiate package 39 // private classes from outside the package 40 private final Clazz[] helpers = new Clazz[4]; 41 private ClassConstruct callsiteClass; 42 43 public enum ExecutionMode { DIRECT, INDY, MH_INVOKE_EXACT, MH_INVOKE_GENERIC} 44 private final ExecutionMode execMode; 45 46 public ClassBuilder(SelectionResolutionTestCase testcase, 47 ExecutionMode execMode) { 48 super(testcase); 49 this.classes = new ArrayList<>(); 50 this.execMode = execMode; 51 } 52 53 public ClassConstruct[] build() throws Exception { 54 buildClassConstructs(); 55 return classes.toArray(new ClassConstruct[0]); 56 } 57 58 public ClassConstruct getCallsiteClass() { 59 return callsiteClass; 60 } 61 62 private void buildClassConstructs() throws Exception { 63 TestBuilder tb = new TestBuilder(testcase.methodref, testcase); 64 65 classes.add(new Clazz("Test", ACC_PUBLIC, -1)); 66 67 for (int classId = 0; classId < classdata.size(); classId++) { 68 ClassConstruct C; 69 String[] interfaces = getInterfaces(classId); 70 ClassData data = classdata.get(classId); 71 72 if (isClass(classId)) { 73 C = new Clazz(getName(classId), 74 getExtending(classId), 75 getClassModifiers(data), 76 classId, 77 interfaces); 78 79 addHelperMethod(classId); 80 81 } else { 82 C = new Interface(getName(classId), 83 getAccessibility(data.access), 84 classId, interfaces); 85 } 86 87 // Add a method "m()LTestObject;" if applicable 88 if (containsMethod(data)) { 89 // Method will either be abstract or concrete depending on the 90 // abstract modifier 91 C.addTestMethod(getMethodModifiers(data)); 92 } 93 94 if (classId == testcase.callsite) { 95 // Add test() method 96 tb.addTest(C, execMode); 97 callsiteClass = C; 98 } 99 100 classes.add(C); 101 } 102 classes.add(tb.getMainTestClass()); 103 104 } 105 106 private void addHelperMethod(int classId) { 107 int packageId = classdata.get(classId).packageId.ordinal(); 108 Clazz C = helpers[packageId]; 109 if (C == null) { 110 C = new Clazz(getPackageName(packageId) + "Helper", -1, ACC_PUBLIC); 111 helpers[packageId] = C; 112 classes.add(C); 113 } 114 115 Method m = C.addMethod("get" + getClassName(classId), 116 "()L" + getName(classId) + ";", 117 ACC_PUBLIC + ACC_STATIC); 118 m.makeInstantiateMethod(getName(classId)); 119 } 120 121 private String[] getInterfaces(int classId) { 122 ArrayList<String> interfaces = new ArrayList<>(); 123 124 // Figure out if we're extending/implementing an interface 125 for (final int intf : hier.interfaces()) { 126 if (hier.inherits(classId, intf)) { 127 interfaces.add(getName(intf)); 128 } 129 } 130 return interfaces.toArray(new String[0]); 131 } 132 133 private String getExtending(int classId) { 134 int extending = -1; 135 136 // See if we're extending another class 137 for (final int extendsClass : hier.classes()) { 138 if (hier.inherits(classId, extendsClass)) { 139 // Sanity check that we haven't already found an extending class 140 if (extending != -1) { 141 throw new RuntimeException("Multiple extending classes"); 142 } 143 extending = extendsClass; 144 } 145 } 146 147 return extending == -1 ? null : getName(extending); 148 } 149 150 /** 151 * Returns modifiers for a Class 152 * @param cd ClassData for the Class 153 * @return ASM modifiers for a Class 154 */ 155 private int getClassModifiers(ClassData cd) { 156 // For Classes we only care about accessibility (public, private etc) 157 return getAccessibility(cd.access) | getAbstraction(cd.abstraction); 158 } 159 160 /** 161 * Returns modifiers for Method type 162 * @param cd ClassData for the Class or Interface where the Method resides 163 * @return ASM modifiers for the Method 164 */ 165 private int getMethodModifiers(ClassData cd) { 166 int mod = 0; 167 168 // For methods we want everything 169 mod += getAccessibility(cd.methoddata.access); 170 mod += getAbstraction(cd.methoddata.context); 171 mod += getContext(cd.methoddata.context); 172 mod += getExtensibility(); 173 return mod; 174 } 175 176 177 /** 178 * Convert ClassData access type to ASM 179 * @param access 180 * @return ASM version of accessibility (public / private / protected) 181 */ 182 private int getAccessibility(MethodData.Access access) { 183 switch(access) { 184 case PACKAGE: 185 //TODO: Do I need to set this or will this be the default? 186 return 0; 187 case PRIVATE: 188 return ACC_PRIVATE; 189 case PROTECTED: 190 return ACC_PROTECTED; 191 case PUBLIC: 192 return ACC_PUBLIC; 193 default: 194 throw new RuntimeException("Illegal accessibility modifier: " + access); 195 } 196 } 197 198 /** 199 * Convert ClassData abstraction type to ASM 200 * @param abstraction 201 * @return ASM version of abstraction (abstract / non-abstract) 202 */ 203 private int getAbstraction(MethodData.Context context) { 204 return context == MethodData.Context.ABSTRACT ? ACC_ABSTRACT : 0; 205 } 206 207 /** 208 * Convert ClassData context type to ASM 209 * @param context 210 * @return ASM version of context (static / non-static) 211 */ 212 private int getContext(MethodData.Context context) { 213 return context == MethodData.Context.STATIC ? ACC_STATIC : 0; 214 } 215 216 /** 217 * Convert ClassData extensibility type to ASM 218 * @param extensibility 219 * @return ASM version of extensibility (final / non-final) 220 */ 221 private int getExtensibility() { 222 return 0; 223 } 224 225 /** 226 * Determine if we need a method at all, abstraction is set to null if this 227 * Class/Interface should not have a test method 228 * @param cd 229 * @return 230 */ 231 private boolean containsMethod(ClassData cd) { 232 return cd.methoddata != null; 233 } 234 235 }