LambdaToMethod.java revision 2673:bf8500822576
1/* 2 * Copyright (c) 2010, 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.tools.javac.comp; 26 27import com.sun.tools.javac.tree.*; 28import com.sun.tools.javac.tree.JCTree.*; 29import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; 30import com.sun.tools.javac.tree.TreeMaker; 31import com.sun.tools.javac.tree.TreeTranslator; 32import com.sun.tools.javac.code.Attribute; 33import com.sun.tools.javac.code.Scope.WriteableScope; 34import com.sun.tools.javac.code.Symbol; 35import com.sun.tools.javac.code.Symbol.ClassSymbol; 36import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; 37import com.sun.tools.javac.code.Symbol.MethodSymbol; 38import com.sun.tools.javac.code.Symbol.TypeSymbol; 39import com.sun.tools.javac.code.Symbol.VarSymbol; 40import com.sun.tools.javac.code.Symtab; 41import com.sun.tools.javac.code.Type; 42import com.sun.tools.javac.code.Type.MethodType; 43import com.sun.tools.javac.code.Types; 44import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*; 45import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; 46import com.sun.tools.javac.jvm.*; 47import com.sun.tools.javac.util.*; 48import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 49import com.sun.source.tree.MemberReferenceTree.ReferenceMode; 50 51import java.util.EnumMap; 52import java.util.HashMap; 53import java.util.HashSet; 54import java.util.LinkedHashMap; 55import java.util.Map; 56import java.util.Set; 57 58import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; 59import static com.sun.tools.javac.code.Flags.*; 60import static com.sun.tools.javac.code.Kinds.Kind.*; 61import static com.sun.tools.javac.code.TypeTag.*; 62import static com.sun.tools.javac.tree.JCTree.Tag.*; 63 64/** 65 * This pass desugars lambda expressions into static methods 66 * 67 * <p><b>This is NOT part of any supported API. 68 * If you write code that depends on this, you do so at your own risk. 69 * This code and its internal interfaces are subject to change or 70 * deletion without notice.</b> 71 */ 72public class LambdaToMethod extends TreeTranslator { 73 74 private Attr attr; 75 private JCDiagnostic.Factory diags; 76 private Log log; 77 private Lower lower; 78 private Names names; 79 private Symtab syms; 80 private Resolve rs; 81 private TreeMaker make; 82 private Types types; 83 private TransTypes transTypes; 84 private Env<AttrContext> attrEnv; 85 86 /** the analyzer scanner */ 87 private LambdaAnalyzerPreprocessor analyzer; 88 89 /** map from lambda trees to translation contexts */ 90 private Map<JCTree, TranslationContext<?>> contextMap; 91 92 /** current translation context (visitor argument) */ 93 private TranslationContext<?> context; 94 95 /** info about the current class being processed */ 96 private KlassInfo kInfo; 97 98 /** dump statistics about lambda code generation */ 99 private boolean dumpLambdaToMethodStats; 100 101 /** force serializable representation, for stress testing **/ 102 private final boolean forceSerializable; 103 104 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */ 105 public static final int FLAG_SERIALIZABLE = 1 << 0; 106 107 /** Flag for alternate metafactories indicating the lambda object has multiple targets */ 108 public static final int FLAG_MARKERS = 1 << 1; 109 110 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */ 111 public static final int FLAG_BRIDGES = 1 << 2; 112 113 // <editor-fold defaultstate="collapsed" desc="Instantiating"> 114 protected static final Context.Key<LambdaToMethod> unlambdaKey = new Context.Key<>(); 115 116 public static LambdaToMethod instance(Context context) { 117 LambdaToMethod instance = context.get(unlambdaKey); 118 if (instance == null) { 119 instance = new LambdaToMethod(context); 120 } 121 return instance; 122 } 123 private LambdaToMethod(Context context) { 124 context.put(unlambdaKey, this); 125 diags = JCDiagnostic.Factory.instance(context); 126 log = Log.instance(context); 127 lower = Lower.instance(context); 128 names = Names.instance(context); 129 syms = Symtab.instance(context); 130 rs = Resolve.instance(context); 131 make = TreeMaker.instance(context); 132 types = Types.instance(context); 133 transTypes = TransTypes.instance(context); 134 analyzer = new LambdaAnalyzerPreprocessor(); 135 Options options = Options.instance(context); 136 dumpLambdaToMethodStats = options.isSet("dumpLambdaToMethodStats"); 137 attr = Attr.instance(context); 138 forceSerializable = options.isSet("forceSerializable"); 139 } 140 // </editor-fold> 141 142 private class KlassInfo { 143 144 /** 145 * list of methods to append 146 */ 147 private ListBuffer<JCTree> appendedMethodList; 148 149 /** 150 * list of deserialization cases 151 */ 152 private final Map<String, ListBuffer<JCStatement>> deserializeCases; 153 154 /** 155 * deserialize method symbol 156 */ 157 private final MethodSymbol deserMethodSym; 158 159 /** 160 * deserialize method parameter symbol 161 */ 162 private final VarSymbol deserParamSym; 163 164 private final JCClassDecl clazz; 165 166 private KlassInfo(JCClassDecl clazz) { 167 this.clazz = clazz; 168 appendedMethodList = new ListBuffer<>(); 169 deserializeCases = new HashMap<>(); 170 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType, 171 List.<Type>nil(), syms.methodClass); 172 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym); 173 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), 174 syms.serializedLambdaType, deserMethodSym); 175 } 176 177 private void addMethod(JCTree decl) { 178 appendedMethodList = appendedMethodList.prepend(decl); 179 } 180 } 181 182 // <editor-fold defaultstate="collapsed" desc="translate methods"> 183 @Override 184 public <T extends JCTree> T translate(T tree) { 185 TranslationContext<?> newContext = contextMap.get(tree); 186 return translate(tree, newContext != null ? newContext : context); 187 } 188 189 <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) { 190 TranslationContext<?> prevContext = context; 191 try { 192 context = newContext; 193 return super.translate(tree); 194 } 195 finally { 196 context = prevContext; 197 } 198 } 199 200 <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) { 201 ListBuffer<T> buf = new ListBuffer<>(); 202 for (T tree : trees) { 203 buf.append(translate(tree, newContext)); 204 } 205 return buf.toList(); 206 } 207 208 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 209 this.make = make; 210 this.attrEnv = env; 211 this.context = null; 212 this.contextMap = new HashMap<>(); 213 return translate(cdef); 214 } 215 // </editor-fold> 216 217 // <editor-fold defaultstate="collapsed" desc="visitor methods"> 218 /** 219 * Visit a class. 220 * Maintain the translatedMethodList across nested classes. 221 * Append the translatedMethodList to the class after it is translated. 222 * @param tree 223 */ 224 @Override 225 public void visitClassDef(JCClassDecl tree) { 226 if (tree.sym.owner.kind == PCK) { 227 //analyze class 228 tree = analyzer.analyzeAndPreprocessClass(tree); 229 } 230 KlassInfo prevKlassInfo = kInfo; 231 try { 232 kInfo = new KlassInfo(tree); 233 super.visitClassDef(tree); 234 if (!kInfo.deserializeCases.isEmpty()) { 235 int prevPos = make.pos; 236 try { 237 make.at(tree); 238 kInfo.addMethod(makeDeserializeMethod(tree.sym)); 239 } finally { 240 make.at(prevPos); 241 } 242 } 243 //add all translated instance methods here 244 List<JCTree> newMethods = kInfo.appendedMethodList.toList(); 245 tree.defs = tree.defs.appendList(newMethods); 246 for (JCTree lambda : newMethods) { 247 tree.sym.members().enter(((JCMethodDecl)lambda).sym); 248 } 249 result = tree; 250 } finally { 251 kInfo = prevKlassInfo; 252 } 253 } 254 255 /** 256 * Translate a lambda into a method to be inserted into the class. 257 * Then replace the lambda site with an invokedynamic call of to lambda 258 * meta-factory, which will use the lambda method. 259 * @param tree 260 */ 261 @Override 262 public void visitLambda(JCLambda tree) { 263 LambdaTranslationContext localContext = (LambdaTranslationContext)context; 264 MethodSymbol sym = (MethodSymbol)localContext.translatedSym; 265 MethodType lambdaType = (MethodType) sym.type; 266 267 { 268 Symbol owner = localContext.owner; 269 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<>(); 270 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<>(); 271 272 for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) { 273 if (tc.position.onLambda == tree) { 274 lambdaTypeAnnos.append(tc); 275 } else { 276 ownerTypeAnnos.append(tc); 277 } 278 } 279 if (lambdaTypeAnnos.nonEmpty()) { 280 owner.setTypeAttributes(ownerTypeAnnos.toList()); 281 sym.setTypeAttributes(lambdaTypeAnnos.toList()); 282 } 283 } 284 285 //create the method declaration hoisting the lambda body 286 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field), 287 sym.name, 288 make.QualIdent(lambdaType.getReturnType().tsym), 289 List.<JCTypeParameter>nil(), 290 localContext.syntheticParams, 291 lambdaType.getThrownTypes() == null ? 292 List.<JCExpression>nil() : 293 make.Types(lambdaType.getThrownTypes()), 294 null, 295 null); 296 lambdaDecl.sym = sym; 297 lambdaDecl.type = lambdaType; 298 299 //translate lambda body 300 //As the lambda body is translated, all references to lambda locals, 301 //captured variables, enclosing members are adjusted accordingly 302 //to refer to the static method parameters (rather than i.e. acessing to 303 //captured members directly). 304 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); 305 306 //Add the method to the list of methods to be added to this class. 307 kInfo.addMethod(lambdaDecl); 308 309 //now that we have generated a method for the lambda expression, 310 //we can translate the lambda into a method reference pointing to the newly 311 //created method. 312 // 313 //Note that we need to adjust the method handle so that it will match the 314 //signature of the SAM descriptor - this means that the method reference 315 //should be added the following synthetic arguments: 316 // 317 // * the "this" argument if it is an instance method 318 // * enclosing locals captured by the lambda expression 319 320 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>(); 321 322 if (localContext.methodReferenceReceiver != null) { 323 syntheticInits.append(localContext.methodReferenceReceiver); 324 } else if (!sym.isStatic()) { 325 syntheticInits.append(makeThis( 326 sym.owner.enclClass().asType(), 327 localContext.owner.enclClass())); 328 } 329 330 //add captured locals 331 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { 332 if (fv != localContext.self) { 333 JCTree captured_local = make.Ident(fv).setType(fv.type); 334 syntheticInits.append((JCExpression) captured_local); 335 } 336 } 337 338 //then, determine the arguments to the indy call 339 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev); 340 341 //build a sam instance using an indy call to the meta-factory 342 int refKind = referenceKind(sym); 343 344 //convert to an invokedynamic call 345 result = makeMetafactoryIndyCall(context, refKind, sym, indy_args); 346 } 347 348 private JCIdent makeThis(Type type, Symbol owner) { 349 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, 350 names._this, 351 type, 352 owner); 353 return make.Ident(_this); 354 } 355 356 /** 357 * Translate a method reference into an invokedynamic call to the 358 * meta-factory. 359 * @param tree 360 */ 361 @Override 362 public void visitReference(JCMemberReference tree) { 363 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; 364 365 //first determine the method symbol to be used to generate the sam instance 366 //this is either the method reference symbol, or the bridged reference symbol 367 Symbol refSym = localContext.isSignaturePolymorphic() 368 ? localContext.sigPolySym 369 : tree.sym; 370 371 //the qualifying expression is treated as a special captured arg 372 JCExpression init; 373 switch(tree.kind) { 374 375 case IMPLICIT_INNER: /** Inner :: new */ 376 case SUPER: /** super :: instMethod */ 377 init = makeThis( 378 localContext.owner.enclClass().asType(), 379 localContext.owner.enclClass()); 380 break; 381 382 case BOUND: /** Expr :: instMethod */ 383 init = tree.getQualifierExpression(); 384 init = attr.makeNullCheck(init); 385 break; 386 387 case UNBOUND: /** Type :: instMethod */ 388 case STATIC: /** Type :: staticMethod */ 389 case TOPLEVEL: /** Top level :: new */ 390 case ARRAY_CTOR: /** ArrayType :: new */ 391 init = null; 392 break; 393 394 default: 395 throw new InternalError("Should not have an invalid kind"); 396 } 397 398 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev); 399 400 401 //build a sam instance using an indy call to the meta-factory 402 result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args); 403 } 404 405 /** 406 * Translate identifiers within a lambda to the mapped identifier 407 * @param tree 408 */ 409 @Override 410 public void visitIdent(JCIdent tree) { 411 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { 412 super.visitIdent(tree); 413 } else { 414 int prevPos = make.pos; 415 try { 416 make.at(tree); 417 418 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 419 JCTree ltree = lambdaContext.translate(tree); 420 if (ltree != null) { 421 result = ltree; 422 } else { 423 //access to untranslated symbols (i.e. compile-time constants, 424 //members defined inside the lambda body, etc.) ) 425 super.visitIdent(tree); 426 } 427 } finally { 428 make.at(prevPos); 429 } 430 } 431 } 432 433 @Override 434 public void visitVarDef(JCVariableDecl tree) { 435 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; 436 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { 437 tree.init = translate(tree.init); 438 tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); 439 result = tree; 440 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) { 441 JCExpression init = translate(tree.init); 442 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym); 443 int prevPos = make.pos; 444 try { 445 result = make.at(tree).VarDef(xsym, init); 446 } finally { 447 make.at(prevPos); 448 } 449 // Replace the entered symbol for this variable 450 WriteableScope sc = tree.sym.owner.members(); 451 if (sc != null) { 452 sc.remove(tree.sym); 453 sc.enter(xsym); 454 } 455 } else { 456 super.visitVarDef(tree); 457 } 458 } 459 460 // </editor-fold> 461 462 // <editor-fold defaultstate="collapsed" desc="Translation helper methods"> 463 464 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) { 465 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? 466 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) : 467 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally); 468 } 469 470 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) { 471 Type restype = lambdaMethodDecl.type.getReturnType(); 472 boolean isLambda_void = expr.type.hasTag(VOID); 473 boolean isTarget_void = restype.hasTag(VOID); 474 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 475 int prevPos = make.pos; 476 try { 477 if (isTarget_void) { 478 //target is void: 479 // BODY; 480 JCStatement stat = make.at(expr).Exec(expr); 481 return make.Block(0, List.<JCStatement>of(stat)); 482 } else if (isLambda_void && isTarget_Void) { 483 //void to Void conversion: 484 // BODY; return null; 485 ListBuffer<JCStatement> stats = new ListBuffer<>(); 486 stats.append(make.at(expr).Exec(expr)); 487 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 488 return make.Block(0, stats.toList()); 489 } else { 490 //non-void to non-void conversion: 491 // return (TYPE)BODY; 492 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); 493 return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr))); 494 } 495 } finally { 496 make.at(prevPos); 497 } 498 } 499 500 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) { 501 final Type restype = lambdaMethodDecl.type.getReturnType(); 502 final boolean isTarget_void = restype.hasTag(VOID); 503 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 504 505 class LambdaBodyTranslator extends TreeTranslator { 506 507 @Override 508 public void visitClassDef(JCClassDecl tree) { 509 //do NOT recurse on any inner classes 510 result = tree; 511 } 512 513 @Override 514 public void visitLambda(JCLambda tree) { 515 //do NOT recurse on any nested lambdas 516 result = tree; 517 } 518 519 @Override 520 public void visitReturn(JCReturn tree) { 521 boolean isLambda_void = tree.expr == null; 522 if (isTarget_void && !isLambda_void) { 523 //Void to void conversion: 524 // { TYPE $loc = RET-EXPR; return; } 525 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); 526 JCVariableDecl varDef = make.VarDef(loc, tree.expr); 527 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null))); 528 } else if (!isTarget_void || !isLambda_void) { 529 //non-void to non-void conversion: 530 // return (TYPE)RET-EXPR; 531 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype); 532 result = tree; 533 } else { 534 result = tree; 535 } 536 537 } 538 } 539 540 JCBlock trans_block = new LambdaBodyTranslator().translate(block); 541 if (completeNormally && isTarget_Void) { 542 //there's no return statement and the lambda (possibly inferred) 543 //return type is java.lang.Void; emit a synthetic return statement 544 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 545 } 546 return trans_block; 547 } 548 549 private JCMethodDecl makeDeserializeMethod(Symbol kSym) { 550 ListBuffer<JCCase> cases = new ListBuffer<>(); 551 ListBuffer<JCBreak> breaks = new ListBuffer<>(); 552 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) { 553 JCBreak br = make.Break(null); 554 breaks.add(br); 555 List<JCStatement> stmts = entry.getValue().append(br).toList(); 556 cases.add(make.Case(make.Literal(entry.getKey()), stmts)); 557 } 558 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList()); 559 for (JCBreak br : breaks) { 560 br.target = sw; 561 } 562 JCBlock body = make.Block(0L, List.<JCStatement>of( 563 sw, 564 make.Throw(makeNewClass( 565 syms.illegalArgumentExceptionType, 566 List.<JCExpression>of(make.Literal("Invalid lambda deserialization")))))); 567 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()), 568 names.deserializeLambda, 569 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym), 570 List.<JCTypeParameter>nil(), 571 List.of(make.VarDef(kInfo.deserParamSym, null)), 572 List.<JCExpression>nil(), 573 body, 574 null); 575 deser.sym = kInfo.deserMethodSym; 576 deser.type = kInfo.deserMethodSym.type; 577 //System.err.printf("DESER: '%s'\n", deser); 578 return deser; 579 } 580 581 /** Make an attributed class instance creation expression. 582 * @param ctype The class type. 583 * @param args The constructor arguments. 584 * @param cons The constructor symbol 585 */ 586 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) { 587 JCNewClass tree = make.NewClass(null, 588 null, make.QualIdent(ctype.tsym), args, null); 589 tree.constructor = cons; 590 tree.type = ctype; 591 return tree; 592 } 593 594 /** Make an attributed class instance creation expression. 595 * @param ctype The class type. 596 * @param args The constructor arguments. 597 */ 598 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) { 599 return makeNewClass(ctype, args, 600 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil())); 601 } 602 603 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym, 604 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) { 605 String functionalInterfaceClass = classSig(targetType); 606 String functionalInterfaceMethodName = samSym.getSimpleName().toString(); 607 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type)); 608 String implClass = classSig(types.erasure(refSym.owner.type)); 609 String implMethodName = refSym.getQualifiedName().toString(); 610 String implMethodSignature = typeSig(types.erasure(refSym.type)); 611 612 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind)); 613 ListBuffer<JCExpression> serArgs = new ListBuffer<>(); 614 int i = 0; 615 for (Type t : indyType.getParameterTypes()) { 616 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList(); 617 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList(); 618 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg))); 619 ++i; 620 } 621 JCStatement stmt = make.If( 622 deserTest(deserTest(deserTest(deserTest(deserTest( 623 kindTest, 624 "getFunctionalInterfaceClass", functionalInterfaceClass), 625 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName), 626 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature), 627 "getImplClass", implClass), 628 "getImplMethodSignature", implMethodSignature), 629 make.Return(makeIndyCall( 630 pos, 631 syms.lambdaMetafactory, 632 names.altMetafactory, 633 staticArgs, indyType, serArgs.toList(), samSym.name)), 634 null); 635 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName); 636 if (stmts == null) { 637 stmts = new ListBuffer<>(); 638 kInfo.deserializeCases.put(implMethodName, stmts); 639 } 640 /**** 641 System.err.printf("+++++++++++++++++\n"); 642 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass); 643 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName); 644 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature); 645 System.err.printf("*implMethodKind: %d\n", implMethodKind); 646 System.err.printf("*implClass: '%s'\n", implClass); 647 System.err.printf("*implMethodName: '%s'\n", implMethodName); 648 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature); 649 ****/ 650 stmts.append(stmt); 651 } 652 653 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) { 654 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2); 655 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType); 656 testExpr.setType(syms.booleanType); 657 return testExpr; 658 } 659 660 private JCExpression deserTest(JCExpression prev, String func, String lit) { 661 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass); 662 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil()); 663 JCMethodInvocation eqtest = make.Apply( 664 List.<JCExpression>nil(), 665 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt), 666 List.<JCExpression>of(make.Literal(lit))); 667 eqtest.setType(syms.booleanType); 668 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest); 669 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType); 670 compound.setType(syms.booleanType); 671 return compound; 672 } 673 674 private JCExpression deserGetter(String func, Type type) { 675 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil()); 676 } 677 678 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) { 679 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass); 680 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil()); 681 return make.Apply( 682 List.<JCExpression>nil(), 683 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt), 684 args).setType(type); 685 } 686 687 /** 688 * Create new synthetic method with given flags, name, type, owner 689 */ 690 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) { 691 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner); 692 } 693 694 /** 695 * Create new synthetic variable with given flags, name, type, owner 696 */ 697 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) { 698 return makeSyntheticVar(flags, names.fromString(name), type, owner); 699 } 700 701 /** 702 * Create new synthetic variable with given flags, name, type, owner 703 */ 704 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { 705 return new VarSymbol(flags | SYNTHETIC, name, type, owner); 706 } 707 708 /** 709 * Set varargsElement field on a given tree (must be either a new class tree 710 * or a method call tree) 711 */ 712 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) { 713 if (varargsElement != null) { 714 switch (tree.getTag()) { 715 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break; 716 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break; 717 default: throw new AssertionError(); 718 } 719 } 720 } 721 722 /** 723 * Convert method/constructor arguments by inserting appropriate cast 724 * as required by type-erasure - this is needed when bridging a lambda/method 725 * reference, as the bridged signature might require downcast to be compatible 726 * with the generated signature. 727 */ 728 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) { 729 Assert.check(meth.kind == MTH); 730 List<Type> formals = types.erasure(meth.type).getParameterTypes(); 731 if (varargsElement != null) { 732 Assert.check((meth.flags() & VARARGS) != 0); 733 } 734 return transTypes.translateArgs(args, formals, varargsElement, attrEnv); 735 } 736 737 // </editor-fold> 738 739 /** 740 * Converts a method reference which cannot be used directly into a lambda 741 */ 742 private class MemberReferenceToLambda { 743 744 private final JCMemberReference tree; 745 private final ReferenceTranslationContext localContext; 746 private final Symbol owner; 747 private final ListBuffer<JCExpression> args = new ListBuffer<>(); 748 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 749 750 private JCExpression receiverExpression = null; 751 752 MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) { 753 this.tree = tree; 754 this.localContext = localContext; 755 this.owner = owner; 756 } 757 758 JCLambda lambda() { 759 int prevPos = make.pos; 760 try { 761 make.at(tree); 762 Type samDesc = localContext.bridgedRefSig(); 763 List<Type> samPTypes = samDesc.getParameterTypes(); 764 765 // an extra argument is prepended in the case where the member 766 // reference is an unbound instance method reference (in which 767 // case the receiver expression in passed. 768 VarSymbol rcvr; 769 switch (tree.kind) { 770 case BOUND: 771 rcvr = addParameter("rec$", tree.getQualifierExpression().type, false); 772 receiverExpression = attr.makeNullCheck(tree.getQualifierExpression()); 773 break; 774 case UNBOUND: 775 rcvr = addParameter("rec$", samPTypes.head, false); 776 samPTypes = samPTypes.tail; 777 break; 778 default: 779 rcvr = null; 780 break; 781 } 782 783 // generate the parameter list for the coverted member reference. 784 // the signature will match the signature of the target sam descriptor 785 786 List<Type> refPTypes = tree.sym.type.getParameterTypes(); 787 int refSize = refPTypes.size(); 788 int samSize = samPTypes.size(); 789 // Last parameter to copy from referenced method 790 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; 791 792 List<Type> l = refPTypes; 793 // Use parameter types of the referenced method, excluding final var args 794 for (int i = 0; l.nonEmpty() && i < last; ++i) { 795 addParameter("x$" + i, l.head, true); 796 l = l.tail; 797 } 798 // Flatten out the var args 799 for (int i = last; i < samSize; ++i) { 800 addParameter("xva$" + i, tree.varargsElement, true); 801 } 802 803 //body generation - this can be either a method call or a 804 //new instance creation expression, depending on the member reference kind 805 JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE) 806 ? expressionInvoke(rcvr) 807 : expressionNew(); 808 809 JCLambda slam = make.Lambda(params.toList(), expr); 810 slam.targets = tree.targets; 811 slam.type = tree.type; 812 slam.pos = tree.pos; 813 return slam; 814 } finally { 815 make.at(prevPos); 816 } 817 } 818 819 JCExpression getReceiverExpression() { 820 return receiverExpression; 821 } 822 823 private JCExpression makeReceiver(VarSymbol rcvr) { 824 if (rcvr == null) return null; 825 JCExpression rcvrExpr = make.Ident(rcvr); 826 Type rcvrType = tree.sym.enclClass().type; 827 if (rcvrType == syms.arrayClass.type) { 828 // Map the receiver type to the actually type, not just "array" 829 rcvrType = tree.getQualifierExpression().type; 830 } 831 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) { 832 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType); 833 } 834 return rcvrExpr; 835 } 836 837 /** 838 * determine the receiver of the method call - the receiver can 839 * be a type qualifier, the synthetic receiver parameter or 'super'. 840 */ 841 private JCExpression expressionInvoke(VarSymbol rcvr) { 842 JCExpression qualifier = 843 tree.sym.isStatic() ? 844 make.Type(tree.sym.owner.type) : 845 (rcvr != null) ? 846 makeReceiver(rcvr) : 847 tree.getQualifierExpression(); 848 849 //create the qualifier expression 850 JCFieldAccess select = make.Select(qualifier, tree.sym.name); 851 select.sym = tree.sym; 852 select.type = tree.sym.erasure(types); 853 854 //create the method call expression 855 JCExpression apply = make.Apply(List.<JCExpression>nil(), select, 856 convertArgs(tree.sym, args.toList(), tree.varargsElement)). 857 setType(tree.sym.erasure(types).getReturnType()); 858 859 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType()); 860 setVarargsIfNeeded(apply, tree.varargsElement); 861 return apply; 862 } 863 864 /** 865 * Lambda body to use for a 'new'. 866 */ 867 private JCExpression expressionNew() { 868 if (tree.kind == ReferenceKind.ARRAY_CTOR) { 869 //create the array creation expression 870 JCNewArray newArr = make.NewArray( 871 make.Type(types.elemtype(tree.getQualifierExpression().type)), 872 List.of(make.Ident(params.first())), 873 null); 874 newArr.type = tree.getQualifierExpression().type; 875 return newArr; 876 } else { 877 //create the instance creation expression 878 //note that method reference syntax does not allow an explicit 879 //enclosing class (so the enclosing class is null) 880 JCNewClass newClass = make.NewClass(null, 881 List.<JCExpression>nil(), 882 make.Type(tree.getQualifierExpression().type), 883 convertArgs(tree.sym, args.toList(), tree.varargsElement), 884 null); 885 newClass.constructor = tree.sym; 886 newClass.constructorType = tree.sym.erasure(types); 887 newClass.type = tree.getQualifierExpression().type; 888 setVarargsIfNeeded(newClass, tree.varargsElement); 889 return newClass; 890 } 891 } 892 893 private VarSymbol addParameter(String name, Type p, boolean genArg) { 894 VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner); 895 vsym.pos = tree.pos; 896 params.append(make.VarDef(vsym, null)); 897 if (genArg) { 898 args.append(make.Ident(vsym)); 899 } 900 return vsym; 901 } 902 } 903 904 private MethodType typeToMethodType(Type mt) { 905 Type type = types.erasure(mt); 906 return new MethodType(type.getParameterTypes(), 907 type.getReturnType(), 908 type.getThrownTypes(), 909 syms.methodClass); 910 } 911 912 /** 913 * Generate an indy method call to the meta factory 914 */ 915 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context, 916 int refKind, Symbol refSym, List<JCExpression> indy_args) { 917 JCFunctionalExpression tree = context.tree; 918 //determine the static bsm args 919 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym); 920 List<Object> staticArgs = List.<Object>of( 921 typeToMethodType(samSym.type), 922 new Pool.MethodHandle(refKind, refSym, types), 923 typeToMethodType(tree.getDescriptorType(types))); 924 925 //computed indy arg types 926 ListBuffer<Type> indy_args_types = new ListBuffer<>(); 927 for (JCExpression arg : indy_args) { 928 indy_args_types.append(arg.type); 929 } 930 931 //finally, compute the type of the indy call 932 MethodType indyType = new MethodType(indy_args_types.toList(), 933 tree.type, 934 List.<Type>nil(), 935 syms.methodClass); 936 937 Name metafactoryName = context.needsAltMetafactory() ? 938 names.altMetafactory : names.metafactory; 939 940 if (context.needsAltMetafactory()) { 941 ListBuffer<Object> markers = new ListBuffer<>(); 942 for (Type t : tree.targets.tail) { 943 if (t.tsym != syms.serializableType.tsym) { 944 markers.append(t.tsym); 945 } 946 } 947 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0; 948 boolean hasMarkers = markers.nonEmpty(); 949 boolean hasBridges = context.bridges.nonEmpty(); 950 if (hasMarkers) { 951 flags |= FLAG_MARKERS; 952 } 953 if (hasBridges) { 954 flags |= FLAG_BRIDGES; 955 } 956 staticArgs = staticArgs.append(flags); 957 if (hasMarkers) { 958 staticArgs = staticArgs.append(markers.length()); 959 staticArgs = staticArgs.appendList(markers.toList()); 960 } 961 if (hasBridges) { 962 staticArgs = staticArgs.append(context.bridges.length() - 1); 963 for (Symbol s : context.bridges) { 964 Type s_erasure = s.erasure(types); 965 if (!types.isSameType(s_erasure, samSym.erasure(types))) { 966 staticArgs = staticArgs.append(s.erasure(types)); 967 } 968 } 969 } 970 if (context.isSerializable()) { 971 int prevPos = make.pos; 972 try { 973 make.at(kInfo.clazz); 974 addDeserializationCase(refKind, refSym, tree.type, samSym, 975 tree, staticArgs, indyType); 976 } finally { 977 make.at(prevPos); 978 } 979 } 980 } 981 982 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name); 983 } 984 985 /** 986 * Generate an indy method call with given name, type and static bootstrap 987 * arguments types 988 */ 989 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, 990 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs, 991 Name methName) { 992 int prevPos = make.pos; 993 try { 994 make.at(pos); 995 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType, 996 syms.stringType, 997 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs)); 998 999 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, 1000 bsmName, bsm_staticArgs, List.<Type>nil()); 1001 1002 DynamicMethodSymbol dynSym = 1003 new DynamicMethodSymbol(methName, 1004 syms.noSymbol, 1005 bsm.isStatic() ? 1006 ClassFile.REF_invokeStatic : 1007 ClassFile.REF_invokeVirtual, 1008 (MethodSymbol)bsm, 1009 indyType, 1010 staticArgs.toArray()); 1011 1012 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); 1013 qualifier.sym = dynSym; 1014 qualifier.type = indyType.getReturnType(); 1015 1016 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs); 1017 proxyCall.type = indyType.getReturnType(); 1018 return proxyCall; 1019 } finally { 1020 make.at(prevPos); 1021 } 1022 } 1023 //where 1024 private List<Type> bsmStaticArgToTypes(List<Object> args) { 1025 ListBuffer<Type> argtypes = new ListBuffer<>(); 1026 for (Object arg : args) { 1027 argtypes.append(bsmStaticArgToType(arg)); 1028 } 1029 return argtypes.toList(); 1030 } 1031 1032 private Type bsmStaticArgToType(Object arg) { 1033 Assert.checkNonNull(arg); 1034 if (arg instanceof ClassSymbol) { 1035 return syms.classType; 1036 } else if (arg instanceof Integer) { 1037 return syms.intType; 1038 } else if (arg instanceof Long) { 1039 return syms.longType; 1040 } else if (arg instanceof Float) { 1041 return syms.floatType; 1042 } else if (arg instanceof Double) { 1043 return syms.doubleType; 1044 } else if (arg instanceof String) { 1045 return syms.stringType; 1046 } else if (arg instanceof Pool.MethodHandle) { 1047 return syms.methodHandleType; 1048 } else if (arg instanceof MethodType) { 1049 return syms.methodTypeType; 1050 } else { 1051 Assert.error("bad static arg " + arg.getClass()); 1052 return null; 1053 } 1054 } 1055 1056 /** 1057 * Get the opcode associated with this method reference 1058 */ 1059 private int referenceKind(Symbol refSym) { 1060 if (refSym.isConstructor()) { 1061 return ClassFile.REF_newInvokeSpecial; 1062 } else { 1063 if (refSym.isStatic()) { 1064 return ClassFile.REF_invokeStatic; 1065 } else if ((refSym.flags() & PRIVATE) != 0) { 1066 return ClassFile.REF_invokeSpecial; 1067 } else if (refSym.enclClass().isInterface()) { 1068 return ClassFile.REF_invokeInterface; 1069 } else { 1070 return ClassFile.REF_invokeVirtual; 1071 } 1072 } 1073 } 1074 1075 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer"> 1076 /** 1077 * This visitor collects information about translation of a lambda expression. 1078 * More specifically, it keeps track of the enclosing contexts and captured locals 1079 * accessed by the lambda being translated (as well as other useful info). 1080 * It also translates away problems for LambdaToMethod. 1081 */ 1082 class LambdaAnalyzerPreprocessor extends TreeTranslator { 1083 1084 /** the frame stack - used to reconstruct translation info about enclosing scopes */ 1085 private List<Frame> frameStack; 1086 1087 /** 1088 * keep the count of lambda expression (used to generate unambiguous 1089 * names) 1090 */ 1091 private int lambdaCount = 0; 1092 1093 /** 1094 * keep the count of lambda expression defined in given context (used to 1095 * generate unambiguous names for serializable lambdas) 1096 */ 1097 private class SyntheticMethodNameCounter { 1098 private Map<String, Integer> map = new HashMap<>(); 1099 int getIndex(StringBuilder buf) { 1100 String temp = buf.toString(); 1101 Integer count = map.get(temp); 1102 if (count == null) { 1103 count = 0; 1104 } 1105 ++count; 1106 map.put(temp, count); 1107 return count; 1108 } 1109 } 1110 private SyntheticMethodNameCounter syntheticMethodNameCounts = 1111 new SyntheticMethodNameCounter(); 1112 1113 private Map<Symbol, JCClassDecl> localClassDefs; 1114 1115 /** 1116 * maps for fake clinit symbols to be used as owners of lambda occurring in 1117 * a static var init context 1118 */ 1119 private Map<ClassSymbol, Symbol> clinits = new HashMap<>(); 1120 1121 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { 1122 frameStack = List.nil(); 1123 localClassDefs = new HashMap<>(); 1124 return translate(tree); 1125 } 1126 1127 @Override 1128 public void visitBlock(JCBlock tree) { 1129 List<Frame> prevStack = frameStack; 1130 try { 1131 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { 1132 frameStack = frameStack.prepend(new Frame(tree)); 1133 } 1134 super.visitBlock(tree); 1135 } 1136 finally { 1137 frameStack = prevStack; 1138 } 1139 } 1140 1141 @Override 1142 public void visitClassDef(JCClassDecl tree) { 1143 List<Frame> prevStack = frameStack; 1144 SyntheticMethodNameCounter prevSyntheticMethodNameCounts = 1145 syntheticMethodNameCounts; 1146 Map<ClassSymbol, Symbol> prevClinits = clinits; 1147 DiagnosticSource prevSource = log.currentSource(); 1148 try { 1149 log.useSource(tree.sym.sourcefile); 1150 syntheticMethodNameCounts = new SyntheticMethodNameCounter(); 1151 prevClinits = new HashMap<>(); 1152 if (tree.sym.owner.kind == MTH) { 1153 localClassDefs.put(tree.sym, tree); 1154 } 1155 if (directlyEnclosingLambda() != null) { 1156 tree.sym.owner = owner(); 1157 if (tree.sym.hasOuterInstance()) { 1158 //if a class is defined within a lambda, the lambda must capture 1159 //its enclosing instance (if any) 1160 TranslationContext<?> localContext = context(); 1161 while (localContext != null) { 1162 if (localContext.tree.getTag() == LAMBDA) { 1163 ((LambdaTranslationContext)localContext) 1164 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); 1165 } 1166 localContext = localContext.prev; 1167 } 1168 } 1169 } 1170 frameStack = frameStack.prepend(new Frame(tree)); 1171 super.visitClassDef(tree); 1172 } 1173 finally { 1174 log.useSource(prevSource.getFile()); 1175 frameStack = prevStack; 1176 syntheticMethodNameCounts = prevSyntheticMethodNameCounts; 1177 clinits = prevClinits; 1178 } 1179 } 1180 1181 @Override 1182 public void visitIdent(JCIdent tree) { 1183 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) { 1184 if (tree.sym.kind == VAR && 1185 tree.sym.owner.kind == MTH && 1186 tree.type.constValue() == null) { 1187 TranslationContext<?> localContext = context(); 1188 while (localContext != null) { 1189 if (localContext.tree.getTag() == LAMBDA) { 1190 JCTree block = capturedDecl(localContext.depth, tree.sym); 1191 if (block == null) break; 1192 ((LambdaTranslationContext)localContext) 1193 .addSymbol(tree.sym, CAPTURED_VAR); 1194 } 1195 localContext = localContext.prev; 1196 } 1197 } else if (tree.sym.owner.kind == TYP) { 1198 TranslationContext<?> localContext = context(); 1199 while (localContext != null) { 1200 if (localContext.tree.hasTag(LAMBDA)) { 1201 JCTree block = capturedDecl(localContext.depth, tree.sym); 1202 if (block == null) break; 1203 switch (block.getTag()) { 1204 case CLASSDEF: 1205 JCClassDecl cdecl = (JCClassDecl)block; 1206 ((LambdaTranslationContext)localContext) 1207 .addSymbol(cdecl.sym, CAPTURED_THIS); 1208 break; 1209 default: 1210 Assert.error("bad block kind"); 1211 } 1212 } 1213 localContext = localContext.prev; 1214 } 1215 } 1216 } 1217 super.visitIdent(tree); 1218 } 1219 1220 @Override 1221 public void visitLambda(JCLambda tree) { 1222 analyzeLambda(tree, "lambda.stat"); 1223 } 1224 1225 private void analyzeLambda(JCLambda tree, JCExpression methodReferenceReceiver) { 1226 // Translation of the receiver expression must occur first 1227 JCExpression rcvr = translate(methodReferenceReceiver); 1228 LambdaTranslationContext context = analyzeLambda(tree, "mref.stat.1"); 1229 if (rcvr != null) { 1230 context.methodReferenceReceiver = rcvr; 1231 } 1232 } 1233 1234 private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) { 1235 List<Frame> prevStack = frameStack; 1236 try { 1237 LambdaTranslationContext context = new LambdaTranslationContext(tree); 1238 if (dumpLambdaToMethodStats) { 1239 log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym); 1240 } 1241 frameStack = frameStack.prepend(new Frame(tree)); 1242 for (JCVariableDecl param : tree.params) { 1243 context.addSymbol(param.sym, PARAM); 1244 frameStack.head.addLocal(param.sym); 1245 } 1246 contextMap.put(tree, context); 1247 super.visitLambda(tree); 1248 context.complete(); 1249 return context; 1250 } 1251 finally { 1252 frameStack = prevStack; 1253 } 1254 } 1255 1256 @Override 1257 public void visitMethodDef(JCMethodDecl tree) { 1258 List<Frame> prevStack = frameStack; 1259 try { 1260 frameStack = frameStack.prepend(new Frame(tree)); 1261 super.visitMethodDef(tree); 1262 } 1263 finally { 1264 frameStack = prevStack; 1265 } 1266 } 1267 1268 @Override 1269 public void visitNewClass(JCNewClass tree) { 1270 TypeSymbol def = tree.type.tsym; 1271 boolean inReferencedClass = currentlyInClass(def); 1272 boolean isLocal = def.isLocal(); 1273 if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) { 1274 TranslationContext<?> localContext = context(); 1275 while (localContext != null) { 1276 if (localContext.tree.getTag() == LAMBDA) { 1277 ((LambdaTranslationContext)localContext) 1278 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); 1279 } 1280 localContext = localContext.prev; 1281 } 1282 } 1283 if (context() != null && !inReferencedClass && isLocal) { 1284 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context(); 1285 captureLocalClassDefs(def, lambdaContext); 1286 } 1287 super.visitNewClass(tree); 1288 } 1289 //where 1290 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) { 1291 JCClassDecl localCDef = localClassDefs.get(csym); 1292 if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) { 1293 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() { 1294 @Override 1295 void addFreeVars(ClassSymbol c) { 1296 captureLocalClassDefs(c, lambdaContext); 1297 } 1298 @Override 1299 void visitSymbol(Symbol sym) { 1300 if (sym.kind == VAR && 1301 sym.owner.kind == MTH && 1302 ((VarSymbol)sym).getConstValue() == null) { 1303 TranslationContext<?> localContext = context(); 1304 while (localContext != null) { 1305 if (localContext.tree.getTag() == LAMBDA) { 1306 JCTree block = capturedDecl(localContext.depth, sym); 1307 if (block == null) break; 1308 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR); 1309 } 1310 localContext = localContext.prev; 1311 } 1312 } 1313 } 1314 }; 1315 fvc.scan(localCDef); 1316 } 1317 } 1318 //where 1319 boolean currentlyInClass(Symbol csym) { 1320 for (Frame frame : frameStack) { 1321 if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1322 JCClassDecl cdef = (JCClassDecl) frame.tree; 1323 if (cdef.sym == csym) { 1324 return true; 1325 } 1326 } 1327 } 1328 return false; 1329 } 1330 1331 /** 1332 * Method references to local class constructors, may, if the local 1333 * class references local variables, have implicit constructor 1334 * parameters added in Lower; As a result, the invokedynamic bootstrap 1335 * information added in the LambdaToMethod pass will have the wrong 1336 * signature. Hooks between Lower and LambdaToMethod have been added to 1337 * handle normal "new" in this case. This visitor converts potentially 1338 * affected method references into a lambda containing a normal 1339 * expression. 1340 * 1341 * @param tree 1342 */ 1343 @Override 1344 public void visitReference(JCMemberReference tree) { 1345 ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree); 1346 contextMap.put(tree, rcontext); 1347 if (rcontext.needsConversionToLambda()) { 1348 // Convert to a lambda, and process as such 1349 MemberReferenceToLambda conv = new MemberReferenceToLambda(tree, rcontext, owner()); 1350 analyzeLambda(conv.lambda(), conv.getReceiverExpression()); 1351 } else { 1352 super.visitReference(tree); 1353 if (dumpLambdaToMethodStats) { 1354 log.note(tree, "mref.stat", rcontext.needsAltMetafactory(), null); 1355 } 1356 } 1357 } 1358 1359 @Override 1360 public void visitSelect(JCFieldAccess tree) { 1361 if (context() != null && tree.sym.kind == VAR && 1362 (tree.sym.name == names._this || 1363 tree.sym.name == names._super)) { 1364 // A select of this or super means, if we are in a lambda, 1365 // we much have an instance context 1366 TranslationContext<?> localContext = context(); 1367 while (localContext != null) { 1368 if (localContext.tree.hasTag(LAMBDA)) { 1369 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); 1370 if (clazz == null) break; 1371 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); 1372 } 1373 localContext = localContext.prev; 1374 } 1375 } 1376 super.visitSelect(tree); 1377 } 1378 1379 @Override 1380 public void visitVarDef(JCVariableDecl tree) { 1381 TranslationContext<?> context = context(); 1382 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)? 1383 (LambdaTranslationContext)context : 1384 null; 1385 if (ltc != null) { 1386 if (frameStack.head.tree.hasTag(LAMBDA)) { 1387 ltc.addSymbol(tree.sym, LOCAL_VAR); 1388 } 1389 // Check for type variables (including as type arguments). 1390 // If they occur within class nested in a lambda, mark for erasure 1391 Type type = tree.sym.asType(); 1392 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) { 1393 ltc.addSymbol(tree.sym, TYPE_VAR); 1394 } 1395 } 1396 1397 List<Frame> prevStack = frameStack; 1398 try { 1399 if (tree.sym.owner.kind == MTH) { 1400 frameStack.head.addLocal(tree.sym); 1401 } 1402 frameStack = frameStack.prepend(new Frame(tree)); 1403 super.visitVarDef(tree); 1404 } 1405 finally { 1406 frameStack = prevStack; 1407 } 1408 } 1409 1410 /** 1411 * Return a valid owner given the current declaration stack 1412 * (required to skip synthetic lambda symbols) 1413 */ 1414 private Symbol owner() { 1415 return owner(false); 1416 } 1417 1418 @SuppressWarnings("fallthrough") 1419 private Symbol owner(boolean skipLambda) { 1420 List<Frame> frameStack2 = frameStack; 1421 while (frameStack2.nonEmpty()) { 1422 switch (frameStack2.head.tree.getTag()) { 1423 case VARDEF: 1424 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) { 1425 frameStack2 = frameStack2.tail; 1426 break; 1427 } 1428 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; 1429 return initSym(cdecl.sym, 1430 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC); 1431 case BLOCK: 1432 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; 1433 return initSym(cdecl2.sym, 1434 ((JCBlock)frameStack2.head.tree).flags & STATIC); 1435 case CLASSDEF: 1436 return ((JCClassDecl)frameStack2.head.tree).sym; 1437 case METHODDEF: 1438 return ((JCMethodDecl)frameStack2.head.tree).sym; 1439 case LAMBDA: 1440 if (!skipLambda) 1441 return ((LambdaTranslationContext)contextMap 1442 .get(frameStack2.head.tree)).translatedSym; 1443 default: 1444 frameStack2 = frameStack2.tail; 1445 } 1446 } 1447 Assert.error(); 1448 return null; 1449 } 1450 1451 private Symbol initSym(ClassSymbol csym, long flags) { 1452 boolean isStatic = (flags & STATIC) != 0; 1453 if (isStatic) { 1454 /* static clinits are generated in Gen, so we need to use a fake 1455 * one. Attr creates a fake clinit method while attributing 1456 * lambda expressions used as initializers of static fields, so 1457 * let's use that one. 1458 */ 1459 MethodSymbol clinit = attr.removeClinit(csym); 1460 if (clinit != null) { 1461 clinits.put(csym, clinit); 1462 return clinit; 1463 } 1464 1465 /* if no clinit is found at Attr, then let's try at clinits. 1466 */ 1467 clinit = (MethodSymbol)clinits.get(csym); 1468 if (clinit == null) { 1469 /* no luck, let's create a new one 1470 */ 1471 clinit = makePrivateSyntheticMethod(STATIC, 1472 names.clinit, 1473 new MethodType(List.<Type>nil(), syms.voidType, 1474 List.<Type>nil(), syms.methodClass), 1475 csym); 1476 clinits.put(csym, clinit); 1477 } 1478 return clinit; 1479 } else { 1480 //get the first constructor and treat it as the instance init sym 1481 for (Symbol s : csym.members_field.getSymbolsByName(names.init)) { 1482 return s; 1483 } 1484 } 1485 Assert.error("init not found"); 1486 return null; 1487 } 1488 1489 private JCTree directlyEnclosingLambda() { 1490 if (frameStack.isEmpty()) { 1491 return null; 1492 } 1493 List<Frame> frameStack2 = frameStack; 1494 while (frameStack2.nonEmpty()) { 1495 switch (frameStack2.head.tree.getTag()) { 1496 case CLASSDEF: 1497 case METHODDEF: 1498 return null; 1499 case LAMBDA: 1500 return frameStack2.head.tree; 1501 default: 1502 frameStack2 = frameStack2.tail; 1503 } 1504 } 1505 Assert.error(); 1506 return null; 1507 } 1508 1509 private boolean inClassWithinLambda() { 1510 if (frameStack.isEmpty()) { 1511 return false; 1512 } 1513 List<Frame> frameStack2 = frameStack; 1514 boolean classFound = false; 1515 while (frameStack2.nonEmpty()) { 1516 switch (frameStack2.head.tree.getTag()) { 1517 case LAMBDA: 1518 return classFound; 1519 case CLASSDEF: 1520 classFound = true; 1521 frameStack2 = frameStack2.tail; 1522 break; 1523 default: 1524 frameStack2 = frameStack2.tail; 1525 } 1526 } 1527 // No lambda 1528 return false; 1529 } 1530 1531 /** 1532 * Return the declaration corresponding to a symbol in the enclosing 1533 * scope; the depth parameter is used to filter out symbols defined 1534 * in nested scopes (which do not need to undergo capture). 1535 */ 1536 private JCTree capturedDecl(int depth, Symbol sym) { 1537 int currentDepth = frameStack.size() - 1; 1538 for (Frame block : frameStack) { 1539 switch (block.tree.getTag()) { 1540 case CLASSDEF: 1541 ClassSymbol clazz = ((JCClassDecl)block.tree).sym; 1542 if (sym.isMemberOf(clazz, types)) { 1543 return currentDepth > depth ? null : block.tree; 1544 } 1545 break; 1546 case VARDEF: 1547 if (((JCVariableDecl)block.tree).sym == sym && 1548 sym.owner.kind == MTH) { //only locals are captured 1549 return currentDepth > depth ? null : block.tree; 1550 } 1551 break; 1552 case BLOCK: 1553 case METHODDEF: 1554 case LAMBDA: 1555 if (block.locals != null && block.locals.contains(sym)) { 1556 return currentDepth > depth ? null : block.tree; 1557 } 1558 break; 1559 default: 1560 Assert.error("bad decl kind " + block.tree.getTag()); 1561 } 1562 currentDepth--; 1563 } 1564 return null; 1565 } 1566 1567 private TranslationContext<?> context() { 1568 for (Frame frame : frameStack) { 1569 TranslationContext<?> context = contextMap.get(frame.tree); 1570 if (context != null) { 1571 return context; 1572 } 1573 } 1574 return null; 1575 } 1576 1577 /** 1578 * This is used to filter out those identifiers that needs to be adjusted 1579 * when translating away lambda expressions 1580 */ 1581 private boolean lambdaIdentSymbolFilter(Symbol sym) { 1582 return (sym.kind == VAR || sym.kind == MTH) 1583 && !sym.isStatic() 1584 && sym.name != names.init; 1585 } 1586 1587 /** 1588 * This is used to filter out those new class expressions that need to 1589 * be qualified with an enclosing tree 1590 */ 1591 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) { 1592 if (context != null 1593 && tree.encl == null 1594 && tree.def == null 1595 && !tree.type.getEnclosingType().hasTag(NONE)) { 1596 Type encl = tree.type.getEnclosingType(); 1597 Type current = context.owner.enclClass().type; 1598 while (!current.hasTag(NONE)) { 1599 if (current.tsym.isSubClass(encl.tsym, types)) { 1600 return true; 1601 } 1602 current = current.getEnclosingType(); 1603 } 1604 return false; 1605 } else { 1606 return false; 1607 } 1608 } 1609 1610 private class Frame { 1611 final JCTree tree; 1612 List<Symbol> locals; 1613 1614 public Frame(JCTree tree) { 1615 this.tree = tree; 1616 } 1617 1618 void addLocal(Symbol sym) { 1619 if (locals == null) { 1620 locals = List.nil(); 1621 } 1622 locals = locals.prepend(sym); 1623 } 1624 } 1625 1626 /** 1627 * This class is used to store important information regarding translation of 1628 * lambda expression/method references (see subclasses). 1629 */ 1630 private abstract class TranslationContext<T extends JCFunctionalExpression> { 1631 1632 /** the underlying (untranslated) tree */ 1633 final T tree; 1634 1635 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ 1636 final Symbol owner; 1637 1638 /** the depth of this lambda expression in the frame stack */ 1639 final int depth; 1640 1641 /** the enclosing translation context (set for nested lambdas/mref) */ 1642 final TranslationContext<?> prev; 1643 1644 /** list of methods to be bridged by the meta-factory */ 1645 final List<Symbol> bridges; 1646 1647 TranslationContext(T tree) { 1648 this.tree = tree; 1649 this.owner = owner(); 1650 this.depth = frameStack.size() - 1; 1651 this.prev = context(); 1652 ClassSymbol csym = 1653 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE); 1654 this.bridges = types.functionalInterfaceBridges(csym); 1655 } 1656 1657 /** does this functional expression need to be created using alternate metafactory? */ 1658 boolean needsAltMetafactory() { 1659 return tree.targets.length() > 1 || 1660 isSerializable() || 1661 bridges.length() > 1; 1662 } 1663 1664 /** does this functional expression require serialization support? */ 1665 boolean isSerializable() { 1666 if (forceSerializable) { 1667 return true; 1668 } 1669 for (Type target : tree.targets) { 1670 if (types.asSuper(target, syms.serializableType.tsym) != null) { 1671 return true; 1672 } 1673 } 1674 return false; 1675 } 1676 1677 /** 1678 * @return Name of the enclosing method to be folded into synthetic 1679 * method name 1680 */ 1681 String enclosingMethodName() { 1682 return syntheticMethodNameComponent(owner.name); 1683 } 1684 1685 /** 1686 * @return Method name in a form that can be folded into a 1687 * component of a synthetic method name 1688 */ 1689 String syntheticMethodNameComponent(Name name) { 1690 if (name == null) { 1691 return "null"; 1692 } 1693 String methodName = name.toString(); 1694 if (methodName.equals("<clinit>")) { 1695 methodName = "static"; 1696 } else if (methodName.equals("<init>")) { 1697 methodName = "new"; 1698 } 1699 return methodName; 1700 } 1701 } 1702 1703 /** 1704 * This class retains all the useful information about a lambda expression; 1705 * the contents of this class are filled by the LambdaAnalyzer visitor, 1706 * and the used by the main translation routines in order to adjust references 1707 * to captured locals/members, etc. 1708 */ 1709 private class LambdaTranslationContext extends TranslationContext<JCLambda> { 1710 1711 /** variable in the enclosing context to which this lambda is assigned */ 1712 final Symbol self; 1713 1714 /** variable in the enclosing context to which this lambda is assigned */ 1715 final Symbol assignedTo; 1716 1717 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols; 1718 1719 /** the synthetic symbol for the method hoisting the translated lambda */ 1720 Symbol translatedSym; 1721 1722 List<JCVariableDecl> syntheticParams; 1723 1724 /** 1725 * to prevent recursion, track local classes processed 1726 */ 1727 final Set<Symbol> freeVarProcessedLocalClasses; 1728 1729 /** 1730 * For method references converted to lambdas. The method 1731 * reference receiver expression. Must be treated like a captured 1732 * variable. 1733 */ 1734 JCExpression methodReferenceReceiver; 1735 1736 LambdaTranslationContext(JCLambda tree) { 1737 super(tree); 1738 Frame frame = frameStack.head; 1739 switch (frame.tree.getTag()) { 1740 case VARDEF: 1741 assignedTo = self = ((JCVariableDecl) frame.tree).sym; 1742 break; 1743 case ASSIGN: 1744 self = null; 1745 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable()); 1746 break; 1747 default: 1748 assignedTo = self = null; 1749 break; 1750 } 1751 1752 // This symbol will be filled-in in complete 1753 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass()); 1754 1755 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class); 1756 1757 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>()); 1758 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>()); 1759 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>()); 1760 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>()); 1761 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>()); 1762 1763 freeVarProcessedLocalClasses = new HashSet<>(); 1764 } 1765 1766 /** 1767 * For a serializable lambda, generate a disambiguating string 1768 * which maximizes stability across deserialization. 1769 * 1770 * @return String to differentiate synthetic lambda method names 1771 */ 1772 private String serializedLambdaDisambiguation() { 1773 StringBuilder buf = new StringBuilder(); 1774 // Append the enclosing method signature to differentiate 1775 // overloaded enclosing methods. For lambdas enclosed in 1776 // lambdas, the generated lambda method will not have type yet, 1777 // but the enclosing method's name will have been generated 1778 // with this same method, so it will be unique and never be 1779 // overloaded. 1780 Assert.check( 1781 owner.type != null || 1782 directlyEnclosingLambda() != null); 1783 if (owner.type != null) { 1784 buf.append(typeSig(owner.type)); 1785 buf.append(":"); 1786 } 1787 1788 // Add target type info 1789 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName()); 1790 buf.append(" "); 1791 1792 // Add variable assigned to 1793 if (assignedTo != null) { 1794 buf.append(assignedTo.flatName()); 1795 buf.append("="); 1796 } 1797 //add captured locals info: type, name, order 1798 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) { 1799 if (fv != self) { 1800 buf.append(typeSig(fv.type)); 1801 buf.append(" "); 1802 buf.append(fv.flatName()); 1803 buf.append(","); 1804 } 1805 } 1806 1807 return buf.toString(); 1808 } 1809 1810 /** 1811 * For a non-serializable lambda, generate a simple method. 1812 * 1813 * @return Name to use for the synthetic lambda method name 1814 */ 1815 private Name lambdaName() { 1816 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++)); 1817 } 1818 1819 /** 1820 * For a serializable lambda, generate a method name which maximizes 1821 * name stability across deserialization. 1822 * 1823 * @return Name to use for the synthetic lambda method name 1824 */ 1825 private Name serializedLambdaName() { 1826 StringBuilder buf = new StringBuilder(); 1827 buf.append(names.lambda); 1828 // Append the name of the method enclosing the lambda. 1829 buf.append(enclosingMethodName()); 1830 buf.append('$'); 1831 // Append a hash of the disambiguating string : enclosing method 1832 // signature, etc. 1833 String disam = serializedLambdaDisambiguation(); 1834 buf.append(Integer.toHexString(disam.hashCode())); 1835 buf.append('$'); 1836 // The above appended name components may not be unique, append 1837 // a count based on the above name components. 1838 buf.append(syntheticMethodNameCounts.getIndex(buf)); 1839 String result = buf.toString(); 1840 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam); 1841 return names.fromString(result); 1842 } 1843 1844 /** 1845 * Translate a symbol of a given kind into something suitable for the 1846 * synthetic lambda body 1847 */ 1848 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) { 1849 Symbol ret; 1850 switch (skind) { 1851 case CAPTURED_THIS: 1852 ret = sym; // self represented 1853 break; 1854 case TYPE_VAR: 1855 // Just erase the type var 1856 ret = new VarSymbol(sym.flags(), name, 1857 types.erasure(sym.type), sym.owner); 1858 1859 /* this information should also be kept for LVT generation at Gen 1860 * a Symbol with pos < startPos won't be tracked. 1861 */ 1862 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos; 1863 break; 1864 case CAPTURED_VAR: 1865 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) { 1866 @Override 1867 public Symbol baseSymbol() { 1868 //keep mapping with original captured symbol 1869 return sym; 1870 } 1871 }; 1872 break; 1873 case LOCAL_VAR: 1874 ret = new VarSymbol(sym.flags() & FINAL, name, sym.type, translatedSym); 1875 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 1876 break; 1877 case PARAM: 1878 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym); 1879 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 1880 break; 1881 default: 1882 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym); 1883 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; 1884 } 1885 if (ret != sym) { 1886 ret.setDeclarationAttributes(sym.getRawAttributes()); 1887 ret.setTypeAttributes(sym.getRawTypeAttributes()); 1888 } 1889 return ret; 1890 } 1891 1892 void addSymbol(Symbol sym, LambdaSymbolKind skind) { 1893 Map<Symbol, Symbol> transMap = getSymbolMap(skind); 1894 Name preferredName; 1895 switch (skind) { 1896 case CAPTURED_THIS: 1897 preferredName = names.fromString("encl$" + transMap.size()); 1898 break; 1899 case CAPTURED_VAR: 1900 preferredName = names.fromString("cap$" + transMap.size()); 1901 break; 1902 case LOCAL_VAR: 1903 preferredName = sym.name; 1904 break; 1905 case PARAM: 1906 preferredName = sym.name; 1907 break; 1908 case TYPE_VAR: 1909 preferredName = sym.name; 1910 break; 1911 default: throw new AssertionError(); 1912 } 1913 if (!transMap.containsKey(sym)) { 1914 transMap.put(sym, translate(preferredName, sym, skind)); 1915 } 1916 } 1917 1918 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) { 1919 Map<Symbol, Symbol> m = translatedSymbols.get(skind); 1920 Assert.checkNonNull(m); 1921 return m; 1922 } 1923 1924 JCTree translate(JCIdent lambdaIdent) { 1925 for (Map<Symbol, Symbol> m : translatedSymbols.values()) { 1926 if (m.containsKey(lambdaIdent.sym)) { 1927 Symbol tSym = m.get(lambdaIdent.sym); 1928 JCTree t = make.Ident(tSym).setType(lambdaIdent.type); 1929 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); 1930 return t; 1931 } 1932 } 1933 return null; 1934 } 1935 1936 /** 1937 * The translatedSym is not complete/accurate until the analysis is 1938 * finished. Once the analysis is finished, the translatedSym is 1939 * "completed" -- updated with type information, access modifiers, 1940 * and full parameter list. 1941 */ 1942 void complete() { 1943 if (syntheticParams != null) { 1944 return; 1945 } 1946 boolean inInterface = translatedSym.owner.isInterface(); 1947 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); 1948 1949 // If instance access isn't needed, make it static. 1950 // Interface instance methods must be default methods. 1951 // Lambda methods are private synthetic. 1952 // Inherit ACC_STRICT from the enclosing method, or, for clinit, 1953 // from the class. 1954 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD | 1955 owner.flags_field & STRICTFP | 1956 owner.owner.flags_field & STRICTFP | 1957 PRIVATE | 1958 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC); 1959 1960 //compute synthetic params 1961 ListBuffer<JCVariableDecl> params = new ListBuffer<>(); 1962 1963 // The signature of the method is augmented with the following 1964 // synthetic parameters: 1965 // 1966 // 1) reference to enclosing contexts captured by the lambda expression 1967 // 2) enclosing locals captured by the lambda expression 1968 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) { 1969 params.append(make.VarDef((VarSymbol) thisSym, null)); 1970 } 1971 if (methodReferenceReceiver != null) { 1972 params.append(make.VarDef( 1973 make.Modifiers(PARAMETER|FINAL), 1974 names.fromString("$rcvr$"), 1975 make.Type(methodReferenceReceiver.type), 1976 null)); 1977 } 1978 for (Symbol thisSym : getSymbolMap(PARAM).values()) { 1979 params.append(make.VarDef((VarSymbol) thisSym, null)); 1980 } 1981 syntheticParams = params.toList(); 1982 1983 // Compute and set the lambda name 1984 translatedSym.name = isSerializable() 1985 ? serializedLambdaName() 1986 : lambdaName(); 1987 1988 //prepend synthetic args to translated lambda method signature 1989 translatedSym.type = types.createMethodTypeWithParameters( 1990 generatedLambdaSig(), 1991 TreeInfo.types(syntheticParams)); 1992 } 1993 1994 Type generatedLambdaSig() { 1995 return types.erasure(tree.getDescriptorType(types)); 1996 } 1997 } 1998 1999 /** 2000 * This class retains all the useful information about a method reference; 2001 * the contents of this class are filled by the LambdaAnalyzer visitor, 2002 * and the used by the main translation routines in order to adjust method 2003 * references (i.e. in case a bridge is needed) 2004 */ 2005 private final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> { 2006 2007 final boolean isSuper; 2008 final Symbol sigPolySym; 2009 2010 ReferenceTranslationContext(JCMemberReference tree) { 2011 super(tree); 2012 this.isSuper = tree.hasKind(ReferenceKind.SUPER); 2013 this.sigPolySym = isSignaturePolymorphic() 2014 ? makePrivateSyntheticMethod(tree.sym.flags(), 2015 tree.sym.name, 2016 bridgedRefSig(), 2017 tree.sym.enclClass()) 2018 : null; 2019 } 2020 2021 /** 2022 * Get the opcode associated with this method reference 2023 */ 2024 int referenceKind() { 2025 return LambdaToMethod.this.referenceKind(tree.sym); 2026 } 2027 2028 boolean needsVarArgsConversion() { 2029 return tree.varargsElement != null; 2030 } 2031 2032 /** 2033 * @return Is this an array operation like clone() 2034 */ 2035 boolean isArrayOp() { 2036 return tree.sym.owner == syms.arrayClass; 2037 } 2038 2039 boolean receiverAccessible() { 2040 //hack needed to workaround 292 bug (7087658) 2041 //when 292 issue is fixed we should remove this and change the backend 2042 //code to always generate a method handle to an accessible method 2043 return tree.ownerAccessible; 2044 } 2045 2046 /** 2047 * The VM does not support access across nested classes (8010319). 2048 * Were that ever to change, this should be removed. 2049 */ 2050 boolean isPrivateInOtherClass() { 2051 return (tree.sym.flags() & PRIVATE) != 0 && 2052 !types.isSameType( 2053 types.erasure(tree.sym.enclClass().asType()), 2054 types.erasure(owner.enclClass().asType())); 2055 } 2056 2057 /** 2058 * Signature polymorphic methods need special handling. 2059 * e.g. MethodHandle.invoke() MethodHandle.invokeExact() 2060 */ 2061 final boolean isSignaturePolymorphic() { 2062 return tree.sym.kind == MTH && 2063 types.isSignaturePolymorphic((MethodSymbol)tree.sym); 2064 } 2065 2066 /** 2067 * Does this reference need to be converted to a lambda 2068 * (i.e. var args need to be expanded or "super" is used) 2069 */ 2070 final boolean needsConversionToLambda() { 2071 return isSuper || needsVarArgsConversion() || isArrayOp() || 2072 isPrivateInOtherClass() || 2073 !receiverAccessible() || 2074 (tree.getMode() == ReferenceMode.NEW && 2075 tree.kind != ReferenceKind.ARRAY_CTOR && 2076 (tree.sym.owner.isLocal() || tree.sym.owner.isInner())); 2077 } 2078 2079 Type generatedRefSig() { 2080 return types.erasure(tree.sym.type); 2081 } 2082 2083 Type bridgedRefSig() { 2084 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type); 2085 } 2086 } 2087 } 2088 // </editor-fold> 2089 2090 /* 2091 * These keys provide mappings for various translated lambda symbols 2092 * and the prevailing order must be maintained. 2093 */ 2094 enum LambdaSymbolKind { 2095 PARAM, // original to translated lambda parameters 2096 LOCAL_VAR, // original to translated lambda locals 2097 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters 2098 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access) 2099 TYPE_VAR // original to translated lambda type variables 2100 } 2101 2102 /** 2103 * **************************************************************** 2104 * Signature Generation 2105 * **************************************************************** 2106 */ 2107 2108 private String typeSig(Type type) { 2109 L2MSignatureGenerator sg = new L2MSignatureGenerator(); 2110 sg.assembleSig(type); 2111 return sg.toString(); 2112 } 2113 2114 private String classSig(Type type) { 2115 L2MSignatureGenerator sg = new L2MSignatureGenerator(); 2116 sg.assembleClassSig(type); 2117 return sg.toString(); 2118 } 2119 2120 /** 2121 * Signature Generation 2122 */ 2123 private class L2MSignatureGenerator extends Types.SignatureGenerator { 2124 2125 /** 2126 * An output buffer for type signatures. 2127 */ 2128 StringBuilder sb = new StringBuilder(); 2129 2130 L2MSignatureGenerator() { 2131 super(types); 2132 } 2133 2134 @Override 2135 protected void append(char ch) { 2136 sb.append(ch); 2137 } 2138 2139 @Override 2140 protected void append(byte[] ba) { 2141 sb.append(new String(ba)); 2142 } 2143 2144 @Override 2145 protected void append(Name name) { 2146 sb.append(name.toString()); 2147 } 2148 2149 @Override 2150 public String toString() { 2151 return sb.toString(); 2152 } 2153 } 2154} 2155