1 /* 2 * Copyright (c) 2013, 2015, 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 * @test 26 * @bug 8009649 8129962 27 * @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods 28 * @library /tools/javac/lib 29 * @modules jdk.jdeps/com.sun.tools.classfile 30 * jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.file 32 * jdk.compiler/com.sun.tools.javac.util 33 * @build combo.ComboTestHelper 34 * @run main TestLambdaBytecode 35 */ 36 37 import com.sun.tools.classfile.Attribute; 38 import com.sun.tools.classfile.BootstrapMethods_attribute; 39 import com.sun.tools.classfile.ClassFile; 40 import com.sun.tools.classfile.Code_attribute; 41 import com.sun.tools.classfile.ConstantPool.*; 42 import com.sun.tools.classfile.Instruction; 43 import com.sun.tools.classfile.Method; 44 45 import java.io.IOException; 46 import java.io.InputStream; 47 48 import combo.ComboInstance; 49 import combo.ComboParameter; 50 import combo.ComboTask.Result; 51 import combo.ComboTestHelper; 52 53 import javax.tools.JavaFileObject; 54 55 public class TestLambdaBytecode extends ComboInstance<TestLambdaBytecode> { 56 57 static final int MF_ARITY = 3; 58 static final String MH_SIG = "()V"; 59 60 enum ClassKind implements ComboParameter { 61 CLASS("class"), 62 INTERFACE("interface"); 63 64 String classStr; 65 66 ClassKind(String classStr) { 67 this.classStr = classStr; 68 } 69 70 @Override 71 public String expand(String optParameter) { 72 return classStr; 73 } 74 } 75 76 enum AccessKind implements ComboParameter { 77 PUBLIC("public"), 78 PRIVATE("private"); 79 80 String accessStr; 81 82 AccessKind(String accessStr) { 83 this.accessStr = accessStr; 84 } 85 86 @Override 87 public String expand(String optParameter) { 88 return accessStr; 89 } 90 } 91 92 enum StaticKind implements ComboParameter { 93 STATIC("static"), 94 INSTANCE(""); 95 96 String staticStr; 97 98 StaticKind(String staticStr) { 99 this.staticStr = staticStr; 100 } 101 102 @Override 103 public String expand(String optParameter) { 104 return staticStr; 105 } 106 } 107 108 enum DefaultKind implements ComboParameter { 109 DEFAULT("default"), 110 NO_DEFAULT(""); 111 112 String defaultStr; 113 114 DefaultKind(String defaultStr) { 115 this.defaultStr = defaultStr; 116 } 117 118 @Override 119 public String expand(String optParameter) { 120 return defaultStr; 121 } 122 } 123 124 static class MethodKind { 125 ClassKind ck; 126 AccessKind ak; 127 StaticKind sk; 128 DefaultKind dk; 129 130 MethodKind(ClassKind ck, AccessKind ak, StaticKind sk, DefaultKind dk) { 131 this.ck = ck; 132 this.ak = ak; 133 this.sk = sk; 134 this.dk = dk; 135 } 136 137 boolean inInterface() { 138 return ck == ClassKind.INTERFACE; 139 } 140 141 boolean isPrivate() { 142 return ak == AccessKind.PRIVATE; 143 } 144 145 boolean isStatic() { 146 return sk == StaticKind.STATIC; 147 } 148 149 boolean isDefault() { 150 return dk == DefaultKind.DEFAULT; 151 } 152 153 boolean isOK() { 154 if (isDefault() && (!inInterface() || isStatic())) { 155 return false; 156 } else if (inInterface() && 157 ((!isStatic() && !isDefault()) || isPrivate())) { 158 return false; 159 } else { 160 return true; 161 } 162 } 163 } 164 165 public static void main(String... args) throws Exception { 166 new ComboTestHelper<TestLambdaBytecode>() 167 .withDimension("CLASSKIND", (x, ck) -> x.ck = ck, ClassKind.values()) 168 .withArrayDimension("ACCESS", (x, acc, idx) -> x.accessKinds[idx] = acc, 2, AccessKind.values()) 169 .withArrayDimension("STATIC", (x, sk, idx) -> x.staticKinds[idx] = sk, 2, StaticKind.values()) 170 .withArrayDimension("DEFAULT", (x, dk, idx) -> x.defaultKinds[idx] = dk, 2, DefaultKind.values()) 171 .run(TestLambdaBytecode::new, TestLambdaBytecode::init); 172 } 173 174 ClassKind ck; 175 AccessKind[] accessKinds = new AccessKind[2]; 176 StaticKind[] staticKinds = new StaticKind[2]; 177 DefaultKind[] defaultKinds = new DefaultKind[2]; 178 MethodKind mk1, mk2; 179 180 void init() { 181 mk1 = new MethodKind(ck, accessKinds[0], staticKinds[0], defaultKinds[0]); 182 mk2 = new MethodKind(ck, accessKinds[1], staticKinds[1], defaultKinds[1]); 183 } 184 185 String source_template = 186 "#{CLASSKIND} Test {\n" + 187 " #{ACCESS[0]} #{STATIC[0]} #{DEFAULT[0]} void test() { Runnable r = ()->{ target(); }; }\n" + 188 " #{ACCESS[1]} #{STATIC[1]} #{DEFAULT[1]} void target() { }\n" + 189 "}\n"; 190 191 @Override 192 public void doWork() throws IOException { 193 newCompilationTask() 194 .withSourceFromTemplate(source_template) 195 .generate(this::verifyBytecode); 196 } 197 198 void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) { 199 if (res.hasErrors()) { 200 boolean errorExpected = !mk1.isOK() || !mk2.isOK(); 201 errorExpected |= mk1.isStatic() && !mk2.isStatic(); 202 203 if (!errorExpected) { 204 fail("Diags found when compiling instance; " + res.compilationInfo()); 205 } 206 return; 207 } 208 try (InputStream is = res.get().iterator().next().openInputStream()) { 209 ClassFile cf = ClassFile.read(is); 210 Method testMethod = null; 211 for (Method m : cf.methods) { 212 if (m.getName(cf.constant_pool).equals("test")) { 213 testMethod = m; 214 break; 215 } 216 } 217 if (testMethod == null) { 218 fail("Test method not found"); 219 return; 220 } 221 Code_attribute ea = 222 (Code_attribute)testMethod.attributes.get(Attribute.Code); 223 if (testMethod == null) { 224 fail("Code attribute for test() method not found"); 225 return; 226 } 227 228 int bsmIdx = -1; 229 230 for (Instruction i : ea.getInstructions()) { 231 if (i.getMnemonic().equals("invokedynamic")) { 232 CONSTANT_InvokeDynamic_info indyInfo = 233 (CONSTANT_InvokeDynamic_info)cf 234 .constant_pool.get(i.getShort(1)); 235 bsmIdx = indyInfo.bootstrap_method_attr_index; 236 if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType())) { 237 fail("type mismatch for CONSTANT_InvokeDynamic_info " + 238 res.compilationInfo() + "\n" + indyInfo.getNameAndTypeInfo().getType() + 239 "\n" + makeIndyType()); 240 return; 241 } 242 } 243 } 244 if (bsmIdx == -1) { 245 fail("Missing invokedynamic in generated code"); 246 return; 247 } 248 249 BootstrapMethods_attribute bsm_attr = 250 (BootstrapMethods_attribute)cf 251 .getAttribute(Attribute.BootstrapMethods); 252 if (bsm_attr.bootstrap_method_specifiers.length != 1) { 253 fail("Bad number of method specifiers " + 254 "in BootstrapMethods attribute"); 255 return; 256 } 257 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = 258 bsm_attr.bootstrap_method_specifiers[0]; 259 260 if (bsm_spec.bootstrap_arguments.length != MF_ARITY) { 261 fail("Bad number of static invokedynamic args " + 262 "in BootstrapMethod attribute"); 263 return; 264 } 265 266 CONSTANT_MethodHandle_info mh = 267 (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_arguments[1]); 268 269 boolean kindOK; 270 switch (mh.reference_kind) { 271 case REF_invokeStatic: kindOK = mk2.isStatic(); break; 272 case REF_invokeSpecial: kindOK = !mk2.isStatic(); break; 273 case REF_invokeInterface: kindOK = mk2.inInterface(); break; 274 default: 275 kindOK = false; 276 } 277 278 if (!kindOK) { 279 fail("Bad invoke kind in implementation method handle"); 280 return; 281 } 282 283 if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) { 284 fail("Type mismatch in implementation method handle"); 285 return; 286 } 287 } catch (Exception e) { 288 e.printStackTrace(); 289 fail("error reading " + res.compilationInfo() + ": " + e); 290 } 291 } 292 293 String makeIndyType() { 294 StringBuilder buf = new StringBuilder(); 295 buf.append("("); 296 if (!mk2.isStatic()) { 297 buf.append("LTest;"); 298 } 299 buf.append(")Ljava/lang/Runnable;"); 300 return buf.toString(); 301 } 302 }