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