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