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