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