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