1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Mon Oct 29 10:39:49 2012 -0700 1.3 @@ -0,0 +1,1398 @@ 1.4 +/* 1.5 + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 +package com.sun.tools.javac.comp; 1.29 + 1.30 +import com.sun.tools.javac.tree.*; 1.31 +import com.sun.tools.javac.tree.JCTree; 1.32 +import com.sun.tools.javac.tree.JCTree.*; 1.33 +import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; 1.34 +import com.sun.tools.javac.tree.TreeMaker; 1.35 +import com.sun.tools.javac.tree.TreeScanner; 1.36 +import com.sun.tools.javac.tree.TreeTranslator; 1.37 +import com.sun.tools.javac.code.Flags; 1.38 +import com.sun.tools.javac.code.Kinds; 1.39 +import com.sun.tools.javac.code.Symbol; 1.40 +import com.sun.tools.javac.code.Symbol.ClassSymbol; 1.41 +import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; 1.42 +import com.sun.tools.javac.code.Symbol.MethodSymbol; 1.43 +import com.sun.tools.javac.code.Symbol.VarSymbol; 1.44 +import com.sun.tools.javac.code.Symtab; 1.45 +import com.sun.tools.javac.code.Type; 1.46 +import com.sun.tools.javac.code.Type.ClassType; 1.47 +import com.sun.tools.javac.code.Type.MethodType; 1.48 +import com.sun.tools.javac.code.Types; 1.49 +import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*; 1.50 +import com.sun.tools.javac.jvm.*; 1.51 +import com.sun.tools.javac.util.*; 1.52 +import com.sun.tools.javac.util.List; 1.53 +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 1.54 +import com.sun.source.tree.MemberReferenceTree.ReferenceMode; 1.55 + 1.56 +import java.util.HashMap; 1.57 +import java.util.LinkedHashMap; 1.58 +import java.util.Map; 1.59 + 1.60 +import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; 1.61 +import static com.sun.tools.javac.code.Flags.*; 1.62 +import static com.sun.tools.javac.code.Kinds.*; 1.63 +import static com.sun.tools.javac.code.TypeTag.BOT; 1.64 +import static com.sun.tools.javac.code.TypeTag.NONE; 1.65 +import static com.sun.tools.javac.code.TypeTag.VOID; 1.66 +import static com.sun.tools.javac.tree.JCTree.Tag.*; 1.67 + 1.68 +/** 1.69 + * This pass desugars lambda expressions into static methods 1.70 + * 1.71 + * <p><b>This is NOT part of any supported API. 1.72 + * If you write code that depends on this, you do so at your own risk. 1.73 + * This code and its internal interfaces are subject to change or 1.74 + * deletion without notice.</b> 1.75 + */ 1.76 +public class LambdaToMethod extends TreeTranslator { 1.77 + 1.78 + private Names names; 1.79 + private Symtab syms; 1.80 + private Resolve rs; 1.81 + private TreeMaker make; 1.82 + private Types types; 1.83 + private TransTypes transTypes; 1.84 + private Env<AttrContext> attrEnv; 1.85 + 1.86 + /** the analyzer scanner */ 1.87 + private LambdaAnalyzer analyzer; 1.88 + 1.89 + /** map from lambda trees to translation contexts */ 1.90 + private Map<JCTree, TranslationContext<?>> contextMap; 1.91 + 1.92 + /** current translation context (visitor argument) */ 1.93 + private TranslationContext<?> context; 1.94 + 1.95 + /** list of translated methods 1.96 + **/ 1.97 + private ListBuffer<JCTree> translatedMethodList; 1.98 + 1.99 + // <editor-fold defaultstate="collapsed" desc="Instantiating"> 1.100 + private static final Context.Key<LambdaToMethod> unlambdaKey = 1.101 + new Context.Key<LambdaToMethod>(); 1.102 + 1.103 + public static LambdaToMethod instance(Context context) { 1.104 + LambdaToMethod instance = context.get(unlambdaKey); 1.105 + if (instance == null) { 1.106 + instance = new LambdaToMethod(context); 1.107 + } 1.108 + return instance; 1.109 + } 1.110 + 1.111 + private LambdaToMethod(Context context) { 1.112 + names = Names.instance(context); 1.113 + syms = Symtab.instance(context); 1.114 + rs = Resolve.instance(context); 1.115 + make = TreeMaker.instance(context); 1.116 + types = Types.instance(context); 1.117 + transTypes = TransTypes.instance(context); 1.118 + this.analyzer = makeAnalyzer(); 1.119 + } 1.120 + 1.121 + private LambdaAnalyzer makeAnalyzer() { 1.122 + return new LambdaAnalyzer(); 1.123 + } 1.124 + // </editor-fold> 1.125 + 1.126 + // <editor-fold defaultstate="collapsed" desc="translate methods"> 1.127 + @Override 1.128 + public <T extends JCTree> T translate(T tree) { 1.129 + TranslationContext<?> newContext = contextMap.get(tree); 1.130 + return translate(tree, newContext != null ? newContext : context); 1.131 + } 1.132 + 1.133 + public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) { 1.134 + TranslationContext<?> prevContext = context; 1.135 + try { 1.136 + context = newContext; 1.137 + return super.translate(tree); 1.138 + } 1.139 + finally { 1.140 + context = prevContext; 1.141 + } 1.142 + } 1.143 + 1.144 + public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) { 1.145 + ListBuffer<T> buf = ListBuffer.lb(); 1.146 + for (T tree : trees) { 1.147 + buf.append(translate(tree, newContext)); 1.148 + } 1.149 + return buf.toList(); 1.150 + } 1.151 + 1.152 + public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 1.153 + this.make = make; 1.154 + this.attrEnv = env; 1.155 + this.context = null; 1.156 + this.contextMap = new HashMap<JCTree, TranslationContext<?>>(); 1.157 + return translate(cdef); 1.158 + } 1.159 + // </editor-fold> 1.160 + 1.161 + // <editor-fold defaultstate="collapsed" desc="visitor methods"> 1.162 + /** 1.163 + * Visit a class. 1.164 + * Maintain the translatedMethodList across nested classes. 1.165 + * Append the translatedMethodList to the class after it is translated. 1.166 + * @param tree 1.167 + */ 1.168 + @Override 1.169 + public void visitClassDef(JCClassDecl tree) { 1.170 + if (tree.sym.owner.kind == PCK) { 1.171 + //analyze class 1.172 + analyzer.analyzeClass(tree); 1.173 + } 1.174 + ListBuffer<JCTree> prevTranslated = translatedMethodList; 1.175 + try { 1.176 + translatedMethodList = ListBuffer.lb(); 1.177 + super.visitClassDef(tree); 1.178 + //add all translated instance methods here 1.179 + tree.defs = tree.defs.appendList(translatedMethodList.toList()); 1.180 + for (JCTree lambda : translatedMethodList) { 1.181 + tree.sym.members().enter(((JCMethodDecl)lambda).sym); 1.182 + } 1.183 + result = tree; 1.184 + } finally { 1.185 + translatedMethodList = prevTranslated; 1.186 + } 1.187 + } 1.188 + 1.189 + /** 1.190 + * Translate a lambda into a method to be inserted into the class. 1.191 + * Then replace the lambda site with an invokedynamic call of to lambda 1.192 + * meta-factory, which will use the lambda method. 1.193 + * @param tree 1.194 + */ 1.195 + @Override 1.196 + public void visitLambda(JCLambda tree) { 1.197 + LambdaTranslationContext localContext = (LambdaTranslationContext)context; 1.198 + MethodSymbol sym = (MethodSymbol)localContext.translatedSym; 1.199 + MethodType lambdaType = (MethodType) sym.type; 1.200 + 1.201 + //create the method declaration hoisting the lambda body 1.202 + JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field), 1.203 + sym.name, 1.204 + make.QualIdent(lambdaType.getReturnType().tsym), 1.205 + List.<JCTypeParameter>nil(), 1.206 + localContext.syntheticParams, 1.207 + lambdaType.getThrownTypes() == null ? 1.208 + List.<JCExpression>nil() : 1.209 + make.Types(lambdaType.getThrownTypes()), 1.210 + null, 1.211 + null); 1.212 + lambdaDecl.sym = sym; 1.213 + lambdaDecl.type = lambdaType; 1.214 + 1.215 + //translate lambda body 1.216 + //As the lambda body is translated, all references to lambda locals, 1.217 + //captured variables, enclosing members are adjusted accordingly 1.218 + //to refer to the static method parameters (rather than i.e. acessing to 1.219 + //captured members directly). 1.220 + lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); 1.221 + 1.222 + //Add the method to the list of methods to be added to this class. 1.223 + translatedMethodList = translatedMethodList.prepend(lambdaDecl); 1.224 + 1.225 + //now that we have generated a method for the lambda expression, 1.226 + //we can translate the lambda into a method reference pointing to the newly 1.227 + //created method. 1.228 + // 1.229 + //Note that we need to adjust the method handle so that it will match the 1.230 + //signature of the SAM descriptor - this means that the method reference 1.231 + //should be added the following synthetic arguments: 1.232 + // 1.233 + // * the "this" argument if it is an instance method 1.234 + // * enclosing locals captured by the lambda expression 1.235 + 1.236 + ListBuffer<JCExpression> syntheticInits = ListBuffer.lb(); 1.237 + 1.238 + if (!sym.isStatic()) { 1.239 + syntheticInits.append(makeThis( 1.240 + sym.owner.asType(), 1.241 + localContext.owner.enclClass())); 1.242 + } 1.243 + 1.244 + //add captured locals 1.245 + for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { 1.246 + if (fv != localContext.self) { 1.247 + JCTree captured_local = make.Ident(fv).setType(fv.type); 1.248 + syntheticInits.append((JCExpression) captured_local); 1.249 + } 1.250 + } 1.251 + 1.252 + //then, determine the arguments to the indy call 1.253 + List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev); 1.254 + 1.255 + //build a sam instance using an indy call to the meta-factory 1.256 + int refKind = referenceKind(sym); 1.257 + 1.258 + //convert to an invokedynamic call 1.259 + result = makeMetaFactoryIndyCall(tree, tree.targetType, refKind, sym, indy_args); 1.260 + } 1.261 + 1.262 + private JCIdent makeThis(Type type, Symbol owner) { 1.263 + VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, 1.264 + names._this, 1.265 + type, 1.266 + owner); 1.267 + return make.Ident(_this); 1.268 + } 1.269 + 1.270 + /** 1.271 + * Translate a method reference into an invokedynamic call to the 1.272 + * meta-factory. 1.273 + * @param tree 1.274 + */ 1.275 + @Override 1.276 + public void visitReference(JCMemberReference tree) { 1.277 + ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; 1.278 + 1.279 + //first determine the method symbol to be used to generate the sam instance 1.280 + //this is either the method reference symbol, or the bridged reference symbol 1.281 + Symbol refSym = localContext.needsBridge() ? 1.282 + localContext.bridgeSym : 1.283 + tree.sym; 1.284 + 1.285 + //build the bridge method, if needed 1.286 + if (localContext.needsBridge()) { 1.287 + bridgeMemberReference(tree, localContext); 1.288 + } 1.289 + 1.290 + //the qualifying expression is treated as a special captured arg 1.291 + JCExpression init; 1.292 + switch(tree.kind) { 1.293 + 1.294 + case IMPLICIT_INNER: /** Inner # new */ 1.295 + case SUPER: /** super # instMethod */ 1.296 + init = makeThis( 1.297 + localContext.owner.owner.asType(), 1.298 + localContext.owner); 1.299 + break; 1.300 + 1.301 + case BOUND: /** Expr # instMethod */ 1.302 + init = tree.getQualifierExpression(); 1.303 + break; 1.304 + 1.305 + case STATIC_EVAL: /** Expr # staticMethod */ 1.306 + case UNBOUND: /** Type # instMethod */ 1.307 + case STATIC: /** Type # staticMethod */ 1.308 + case TOPLEVEL: /** Top level # new */ 1.309 + init = null; 1.310 + break; 1.311 + 1.312 + default: 1.313 + throw new InternalError("Should not have an invalid kind"); 1.314 + } 1.315 + 1.316 + List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev); 1.317 + 1.318 + 1.319 + //build a sam instance using an indy call to the meta-factory 1.320 + result = makeMetaFactoryIndyCall(tree, tree.targetType, localContext.referenceKind(), refSym, indy_args); 1.321 + 1.322 + //if we had a static reference with non-static qualifier, add a let 1.323 + //expression to force the evaluation of the qualifier expr 1.324 + if (tree.hasKind(ReferenceKind.STATIC_EVAL)) { 1.325 + VarSymbol rec = new VarSymbol(0, names.fromString("rec$"), tree.getQualifierExpression().type, localContext.owner); 1.326 + JCVariableDecl recDef = make.VarDef(rec, tree.getQualifierExpression()); 1.327 + result = make.LetExpr(recDef, result).setType(tree.type); 1.328 + } 1.329 + } 1.330 + 1.331 + /** 1.332 + * Translate identifiers within a lambda to the mapped identifier 1.333 + * @param tree 1.334 + */ 1.335 + @Override 1.336 + public void visitIdent(JCIdent tree) { 1.337 + if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { 1.338 + super.visitIdent(tree); 1.339 + } else { 1.340 + LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 1.341 + if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) { 1.342 + Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym); 1.343 + result = make.Ident(translatedSym).setType(tree.type); 1.344 + } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { 1.345 + Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); 1.346 + result = make.Ident(translatedSym).setType(tree.type); 1.347 + } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) { 1.348 + Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym); 1.349 + result = make.Ident(translatedSym).setType(tree.type); 1.350 + } else { 1.351 + if (tree.sym.owner.kind == Kinds.TYP) { 1.352 + for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) { 1.353 + if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) { 1.354 + JCExpression enclRef = make.Ident(encl_entry.getValue()); 1.355 + result = tree.sym.name == names._this 1.356 + ? enclRef.setType(tree.type) 1.357 + : make.Select(enclRef, tree.sym).setType(tree.type); 1.358 + result = tree; 1.359 + return; 1.360 + } 1.361 + } 1.362 + } 1.363 + //access to untranslated symbols (i.e. compile-time constants, 1.364 + //members defined inside the lambda body, etc.) ) 1.365 + super.visitIdent(tree); 1.366 + } 1.367 + } 1.368 + } 1.369 + 1.370 + @Override 1.371 + public void visitVarDef(JCVariableDecl tree) { 1.372 + LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; 1.373 + if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { 1.374 + JCExpression init = translate(tree.init); 1.375 + result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init); 1.376 + } else { 1.377 + super.visitVarDef(tree); 1.378 + } 1.379 + } 1.380 + 1.381 + // </editor-fold> 1.382 + 1.383 + // <editor-fold defaultstate="collapsed" desc="Translation helper methods"> 1.384 + 1.385 + private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) { 1.386 + return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? 1.387 + makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) : 1.388 + makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally); 1.389 + } 1.390 + 1.391 + private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) { 1.392 + Type restype = lambdaMethodDecl.type.getReturnType(); 1.393 + boolean isLambda_void = expr.type.hasTag(VOID); 1.394 + boolean isTarget_void = restype.hasTag(VOID); 1.395 + boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 1.396 + if (isTarget_void) { 1.397 + //target is void: 1.398 + // BODY; 1.399 + JCStatement stat = make.Exec(expr); 1.400 + return make.Block(0, List.<JCStatement>of(stat)); 1.401 + } else if (isLambda_void && isTarget_Void) { 1.402 + //void to Void conversion: 1.403 + // BODY; return null; 1.404 + ListBuffer<JCStatement> stats = ListBuffer.lb(); 1.405 + stats.append(make.Exec(expr)); 1.406 + stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 1.407 + return make.Block(0, stats.toList()); 1.408 + } else { 1.409 + //non-void to non-void conversion: 1.410 + // return (TYPE)BODY; 1.411 + JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); 1.412 + return make.Block(0, List.<JCStatement>of(make.Return(retExpr))); 1.413 + } 1.414 + } 1.415 + 1.416 + private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) { 1.417 + final Type restype = lambdaMethodDecl.type.getReturnType(); 1.418 + final boolean isTarget_void = restype.hasTag(VOID); 1.419 + boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 1.420 + 1.421 + class LambdaBodyTranslator extends TreeTranslator { 1.422 + 1.423 + @Override 1.424 + public void visitClassDef(JCClassDecl tree) { 1.425 + //do NOT recurse on any inner classes 1.426 + result = tree; 1.427 + } 1.428 + 1.429 + @Override 1.430 + public void visitLambda(JCLambda tree) { 1.431 + //do NOT recurse on any nested lambdas 1.432 + result = tree; 1.433 + } 1.434 + 1.435 + @Override 1.436 + public void visitReturn(JCReturn tree) { 1.437 + boolean isLambda_void = tree.expr == null; 1.438 + if (isTarget_void && !isLambda_void) { 1.439 + //Void to void conversion: 1.440 + // { TYPE $loc = RET-EXPR; return; } 1.441 + VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); 1.442 + JCVariableDecl varDef = make.VarDef(loc, tree.expr); 1.443 + result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null))); 1.444 + } else if (!isTarget_void || !isLambda_void) { 1.445 + //non-void to non-void conversion: 1.446 + // return (TYPE)RET-EXPR; 1.447 + tree.expr = transTypes.coerce(attrEnv, tree.expr, restype); 1.448 + result = tree; 1.449 + } else { 1.450 + result = tree; 1.451 + } 1.452 + 1.453 + } 1.454 + } 1.455 + 1.456 + JCBlock trans_block = new LambdaBodyTranslator().translate(block); 1.457 + if (completeNormally && isTarget_Void) { 1.458 + //there's no return statement and the lambda (possibly inferred) 1.459 + //return type is java.lang.Void; emit a synthetic return statement 1.460 + trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 1.461 + } 1.462 + return trans_block; 1.463 + } 1.464 + 1.465 + /** 1.466 + * Create new synthetic method with given flags, name, type, owner 1.467 + */ 1.468 + private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) { 1.469 + return new MethodSymbol(flags | SYNTHETIC, name, type, owner); 1.470 + } 1.471 + 1.472 + /** 1.473 + * Create new synthetic variable with given flags, name, type, owner 1.474 + */ 1.475 + private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) { 1.476 + return makeSyntheticVar(flags, names.fromString(name), type, owner); 1.477 + } 1.478 + 1.479 + /** 1.480 + * Create new synthetic variable with given flags, name, type, owner 1.481 + */ 1.482 + private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { 1.483 + return new VarSymbol(flags | SYNTHETIC, name, type, owner); 1.484 + } 1.485 + 1.486 + /** 1.487 + * Set varargsElement field on a given tree (must be either a new class tree 1.488 + * or a method call tree) 1.489 + */ 1.490 + private void setVarargsIfNeeded(JCTree tree, Type varargsElement) { 1.491 + if (varargsElement != null) { 1.492 + switch (tree.getTag()) { 1.493 + case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break; 1.494 + case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break; 1.495 + default: throw new AssertionError(); 1.496 + } 1.497 + } 1.498 + } 1.499 + 1.500 + /** 1.501 + * Convert method/constructor arguments by inserting appropriate cast 1.502 + * as required by type-erasure - this is needed when bridging a lambda/method 1.503 + * reference, as the bridged signature might require downcast to be compatible 1.504 + * with the generated signature. 1.505 + */ 1.506 + private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) { 1.507 + Assert.check(meth.kind == Kinds.MTH); 1.508 + List<Type> formals = types.erasure(meth.type).getParameterTypes(); 1.509 + if (varargsElement != null) { 1.510 + Assert.check((meth.flags() & VARARGS) != 0); 1.511 + } 1.512 + return transTypes.translateArgs(args, formals, varargsElement, attrEnv); 1.513 + } 1.514 + 1.515 + // </editor-fold> 1.516 + 1.517 + private MethodSymbol makeSamDescriptor(Type targetType) { 1.518 + return (MethodSymbol)types.findDescriptorSymbol(targetType.tsym); 1.519 + } 1.520 + 1.521 + private Type makeFunctionalDescriptorType(Type targetType, MethodSymbol samDescriptor, boolean erased) { 1.522 + Type descType = types.memberType(targetType, samDescriptor); 1.523 + return erased ? types.erasure(descType) : descType; 1.524 + } 1.525 + 1.526 + private Type makeFunctionalDescriptorType(Type targetType, boolean erased) { 1.527 + return makeFunctionalDescriptorType(targetType, makeSamDescriptor(targetType), erased); 1.528 + } 1.529 + 1.530 + /** 1.531 + * Generate an adapter method "bridge" for a method reference which cannot 1.532 + * be used directly. 1.533 + */ 1.534 + private class MemberReferenceBridger { 1.535 + 1.536 + private final JCMemberReference tree; 1.537 + private final ReferenceTranslationContext localContext; 1.538 + private final ListBuffer<JCExpression> args = ListBuffer.lb(); 1.539 + private final ListBuffer<JCVariableDecl> params = ListBuffer.lb(); 1.540 + 1.541 + MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) { 1.542 + this.tree = tree; 1.543 + this.localContext = localContext; 1.544 + } 1.545 + 1.546 + /** 1.547 + * Generate the bridge 1.548 + */ 1.549 + JCMethodDecl bridge() { 1.550 + int prevPos = make.pos; 1.551 + try { 1.552 + make.at(tree); 1.553 + Type samDesc = localContext.bridgedRefSig(); 1.554 + List<Type> samPTypes = samDesc.getParameterTypes(); 1.555 + 1.556 + //an extra argument is prepended to the signature of the bridge in case 1.557 + //the member reference is an instance method reference (in which case 1.558 + //the receiver expression is passed to the bridge itself). 1.559 + Type recType = null; 1.560 + switch (tree.kind) { 1.561 + case IMPLICIT_INNER: 1.562 + recType = tree.sym.owner.type.getEnclosingType(); 1.563 + break; 1.564 + case BOUND: 1.565 + recType = tree.getQualifierExpression().type; 1.566 + break; 1.567 + case UNBOUND: 1.568 + recType = samPTypes.head; 1.569 + samPTypes = samPTypes.tail; 1.570 + break; 1.571 + } 1.572 + 1.573 + //generate the parameter list for the bridged member reference - the 1.574 + //bridge signature will match the signature of the target sam descriptor 1.575 + 1.576 + VarSymbol rcvr = (recType == null) 1.577 + ? null 1.578 + : addParameter("rec$", recType, false); 1.579 + 1.580 + List<Type> refPTypes = tree.sym.type.getParameterTypes(); 1.581 + int refSize = refPTypes.size(); 1.582 + int samSize = samPTypes.size(); 1.583 + int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; // Last parameter to copy from referenced method 1.584 + 1.585 + List<Type> l = refPTypes; 1.586 + // Use parameter types of the referenced method, excluding final var args 1.587 + for (int i = 0; l.nonEmpty() && i < last; ++i) { 1.588 + addParameter("x$" + i, l.head, true); 1.589 + l = l.tail; 1.590 + } 1.591 + // Flatten out the var args 1.592 + for (int i = last; i < samSize; ++i) { 1.593 + addParameter("xva$" + i, tree.varargsElement, true); 1.594 + } 1.595 + 1.596 + //generate the bridge method declaration 1.597 + JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()), 1.598 + localContext.bridgeSym.name, 1.599 + make.QualIdent(samDesc.getReturnType().tsym), 1.600 + List.<JCTypeParameter>nil(), 1.601 + params.toList(), 1.602 + tree.sym.type.getThrownTypes() == null 1.603 + ? List.<JCExpression>nil() 1.604 + : make.Types(tree.sym.type.getThrownTypes()), 1.605 + null, 1.606 + null); 1.607 + bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym; 1.608 + bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList())); 1.609 + 1.610 + //bridge method body generation - this can be either a method call or a 1.611 + //new instance creation expression, depending on the member reference kind 1.612 + JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE) 1.613 + ? bridgeExpressionInvoke(rcvr) 1.614 + : bridgeExpressionNew(); 1.615 + 1.616 + //the body is either a return expression containing a method call, 1.617 + //or the method call itself, depending on whether the return type of 1.618 + //the bridge is non-void/void. 1.619 + bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl); 1.620 + 1.621 + return bridgeDecl; 1.622 + } finally { 1.623 + make.at(prevPos); 1.624 + } 1.625 + } 1.626 + 1.627 + /** 1.628 + * determine the receiver of the bridged method call - the receiver can 1.629 + * be either the synthetic receiver parameter or a type qualifier; the 1.630 + * original qualifier expression is never used here, as it might refer 1.631 + * to symbols not available in the static context of the bridge 1.632 + */ 1.633 + private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) { 1.634 + JCExpression qualifier = 1.635 + tree.sym.isStatic() ? 1.636 + make.Type(tree.sym.owner.type) : 1.637 + (rcvr != null) ? 1.638 + make.Ident(rcvr) : 1.639 + tree.getQualifierExpression(); 1.640 + 1.641 + //create the qualifier expression 1.642 + JCFieldAccess select = make.Select(qualifier, tree.sym.name); 1.643 + select.sym = tree.sym; 1.644 + select.type = tree.sym.erasure(types); 1.645 + 1.646 + //create the method call expression 1.647 + JCExpression apply = make.Apply(List.<JCExpression>nil(), select, 1.648 + convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType()); 1.649 + 1.650 + apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType()); 1.651 + setVarargsIfNeeded(apply, tree.varargsElement); 1.652 + return apply; 1.653 + } 1.654 + 1.655 + /** 1.656 + * the enclosing expression is either 'null' (no enclosing type) or set 1.657 + * to the first bridge synthetic parameter 1.658 + */ 1.659 + private JCExpression bridgeExpressionNew() { 1.660 + JCExpression encl = null; 1.661 + switch (tree.kind) { 1.662 + case UNBOUND: 1.663 + case IMPLICIT_INNER: 1.664 + encl = make.Ident(params.first()); 1.665 + } 1.666 + 1.667 + //create the instance creation expression 1.668 + JCNewClass newClass = make.NewClass(encl, 1.669 + List.<JCExpression>nil(), 1.670 + make.Type(tree.getQualifierExpression().type), 1.671 + convertArgs(tree.sym, args.toList(), tree.varargsElement), 1.672 + null); 1.673 + newClass.constructor = tree.sym; 1.674 + newClass.constructorType = tree.sym.erasure(types); 1.675 + newClass.type = tree.getQualifierExpression().type; 1.676 + setVarargsIfNeeded(newClass, tree.varargsElement); 1.677 + return newClass; 1.678 + } 1.679 + 1.680 + private VarSymbol addParameter(String name, Type p, boolean genArg) { 1.681 + VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym); 1.682 + params.append(make.VarDef(vsym, null)); 1.683 + if (genArg) { 1.684 + args.append(make.Ident(vsym)); 1.685 + } 1.686 + return vsym; 1.687 + } 1.688 + } 1.689 + 1.690 + /** 1.691 + * Bridges a member reference - this is needed when: 1.692 + * * Var args in the referenced method need to be flattened away 1.693 + * * super is used 1.694 + */ 1.695 + private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) { 1.696 + JCMethodDecl bridgeDecl = (new MemberReferenceBridger(tree, localContext).bridge()); 1.697 + translatedMethodList = translatedMethodList.prepend(bridgeDecl); 1.698 + } 1.699 + 1.700 + /** 1.701 + * Generate an indy method call to the meta factory 1.702 + */ 1.703 + private JCExpression makeMetaFactoryIndyCall(JCExpression tree, Type targetType, int refKind, Symbol refSym, List<JCExpression> indy_args) { 1.704 + //determine the static bsm args 1.705 + Type mtype = makeFunctionalDescriptorType(targetType, true); 1.706 + List<Object> staticArgs = List.<Object>of( 1.707 + new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(targetType.tsym)), 1.708 + new Pool.MethodHandle(refKind, refSym), 1.709 + new MethodType(mtype.getParameterTypes(), 1.710 + mtype.getReturnType(), 1.711 + mtype.getThrownTypes(), 1.712 + syms.methodClass)); 1.713 + 1.714 + //computed indy arg types 1.715 + ListBuffer<Type> indy_args_types = ListBuffer.lb(); 1.716 + for (JCExpression arg : indy_args) { 1.717 + indy_args_types.append(arg.type); 1.718 + } 1.719 + 1.720 + //finally, compute the type of the indy call 1.721 + MethodType indyType = new MethodType(indy_args_types.toList(), 1.722 + tree.type, 1.723 + List.<Type>nil(), 1.724 + syms.methodClass); 1.725 + 1.726 + return makeIndyCall(tree, syms.lambdaMetafactory, names.metaFactory, staticArgs, indyType, indy_args); 1.727 + } 1.728 + 1.729 + /** 1.730 + * Generate an indy method call with given name, type and static bootstrap 1.731 + * arguments types 1.732 + */ 1.733 + private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) { 1.734 + int prevPos = make.pos; 1.735 + try { 1.736 + make.at(pos); 1.737 + List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType, 1.738 + syms.stringType, 1.739 + syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs)); 1.740 + 1.741 + Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, 1.742 + bsmName, bsm_staticArgs, List.<Type>nil()); 1.743 + 1.744 + DynamicMethodSymbol dynSym = 1.745 + new DynamicMethodSymbol(names.lambda, 1.746 + syms.noSymbol, 1.747 + bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual, 1.748 + (MethodSymbol)bsm, 1.749 + indyType, 1.750 + staticArgs.toArray()); 1.751 + 1.752 + JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); 1.753 + qualifier.sym = dynSym; 1.754 + qualifier.type = indyType.getReturnType(); 1.755 + 1.756 + JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs); 1.757 + proxyCall.type = indyType.getReturnType(); 1.758 + return proxyCall; 1.759 + } finally { 1.760 + make.at(prevPos); 1.761 + } 1.762 + } 1.763 + //where 1.764 + private List<Type> bsmStaticArgToTypes(List<Object> args) { 1.765 + ListBuffer<Type> argtypes = ListBuffer.lb(); 1.766 + for (Object arg : args) { 1.767 + argtypes.append(bsmStaticArgToType(arg)); 1.768 + } 1.769 + return argtypes.toList(); 1.770 + } 1.771 + 1.772 + private Type bsmStaticArgToType(Object arg) { 1.773 + Assert.checkNonNull(arg); 1.774 + if (arg instanceof ClassSymbol) { 1.775 + return syms.classType; 1.776 + } else if (arg instanceof Integer) { 1.777 + return syms.intType; 1.778 + } else if (arg instanceof Long) { 1.779 + return syms.longType; 1.780 + } else if (arg instanceof Float) { 1.781 + return syms.floatType; 1.782 + } else if (arg instanceof Double) { 1.783 + return syms.doubleType; 1.784 + } else if (arg instanceof String) { 1.785 + return syms.stringType; 1.786 + } else if (arg instanceof Pool.MethodHandle) { 1.787 + return syms.methodHandleType; 1.788 + } else if (arg instanceof MethodType) { 1.789 + return syms.methodTypeType; 1.790 + } else { 1.791 + Assert.error("bad static arg " + arg.getClass()); 1.792 + return null; 1.793 + } 1.794 + } 1.795 + 1.796 + /** 1.797 + * Get the opcode associated with this method reference 1.798 + */ 1.799 + private int referenceKind(Symbol refSym) { 1.800 + if (refSym.isConstructor()) { 1.801 + return ClassFile.REF_newInvokeSpecial; 1.802 + } else { 1.803 + if (refSym.isStatic()) { 1.804 + return ClassFile.REF_invokeStatic; 1.805 + } else if (refSym.enclClass().isInterface()) { 1.806 + return ClassFile.REF_invokeInterface; 1.807 + } else { 1.808 + return ClassFile.REF_invokeVirtual; 1.809 + } 1.810 + } 1.811 + } 1.812 + // </editor-fold> 1.813 + 1.814 + // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\ 1.815 + /** 1.816 + * This visitor collects information about translation of a lambda expression. 1.817 + * More specifically, it keeps track of the enclosing contexts and captured locals 1.818 + * accessed by the lambda being translated (as well as other useful info). 1.819 + */ 1.820 + class LambdaAnalyzer extends TreeScanner { 1.821 + 1.822 + /** the frame stack - used to reconstruct translation info about enclosing scopes */ 1.823 + private List<Frame> frameStack; 1.824 + 1.825 + /** 1.826 + * keep the count of lambda expression (used to generate unambiguous 1.827 + * names) 1.828 + */ 1.829 + private int lambdaCount = 0; 1.830 + 1.831 + private void analyzeClass(JCClassDecl tree) { 1.832 + frameStack = List.nil(); 1.833 + scan(tree); 1.834 + } 1.835 + 1.836 + @Override 1.837 + public void visitBlock(JCBlock tree) { 1.838 + List<Frame> prevStack = frameStack; 1.839 + try { 1.840 + if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { 1.841 + frameStack = frameStack.prepend(new Frame(tree)); 1.842 + } 1.843 + super.visitBlock(tree); 1.844 + } 1.845 + finally { 1.846 + frameStack = prevStack; 1.847 + } 1.848 + } 1.849 + 1.850 + @Override 1.851 + public void visitClassDef(JCClassDecl tree) { 1.852 + List<Frame> prevStack = frameStack; 1.853 + try { 1.854 + if (frameStack.nonEmpty() && enclosingLambda() != null) { 1.855 + tree.sym.owner = owner(); 1.856 + LambdaTranslationContext lambdaContext = (LambdaTranslationContext)contextMap.get(enclosingLambda()); 1.857 + Type encl = lambdaContext.enclosingType(); 1.858 + if (encl.hasTag(NONE)) { 1.859 + //if the translated lambda body occurs in a static context, 1.860 + //any class declaration within it must be made static 1.861 + tree.sym.flags_field |= STATIC; 1.862 + ((ClassType)tree.sym.type).setEnclosingType(Type.noType); 1.863 + } else { 1.864 + //if the translated lambda body is in an instance context 1.865 + //the enclosing type of any class declaration within it 1.866 + //must be updated to point to the new enclosing type (if any) 1.867 + ((ClassType)tree.sym.type).setEnclosingType(encl); 1.868 + } 1.869 + } 1.870 + frameStack = frameStack.prepend(new Frame(tree)); 1.871 + super.visitClassDef(tree); 1.872 + } 1.873 + finally { 1.874 + frameStack = prevStack; 1.875 + } 1.876 + if (frameStack.nonEmpty() && enclosingLambda() != null) { 1.877 + // Any class defined within a lambda is an implicit 'this' reference 1.878 + // because its constructor will reference the enclosing class 1.879 + ((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); 1.880 + } 1.881 + } 1.882 + 1.883 + @Override 1.884 + public void visitIdent(JCIdent tree) { 1.885 + if (context() == null || !lambdaIdentSymbolFilter(tree.sym)) { 1.886 + super.visitIdent(tree); 1.887 + } else { 1.888 + if (tree.sym.kind == VAR && 1.889 + tree.sym.owner.kind == MTH && 1.890 + tree.type.constValue() == null) { 1.891 + TranslationContext<?> localContext = context(); 1.892 + while (localContext != null) { 1.893 + if (localContext.tree.getTag() == LAMBDA) { 1.894 + JCTree block = capturedDecl(localContext.depth, tree.sym); 1.895 + if (block == null) break; 1.896 + ((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR); 1.897 + } 1.898 + localContext = localContext.prev; 1.899 + } 1.900 + } else if (tree.sym.owner.kind == TYP) { 1.901 + TranslationContext<?> localContext = context(); 1.902 + while (localContext != null) { 1.903 + if (localContext.tree.hasTag(LAMBDA)) { 1.904 + JCTree block = capturedDecl(localContext.depth, tree.sym); 1.905 + if (block == null) break; 1.906 + switch (block.getTag()) { 1.907 + case CLASSDEF: 1.908 + JCClassDecl cdecl = (JCClassDecl)block; 1.909 + ((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS); 1.910 + break; 1.911 + default: 1.912 + Assert.error("bad block kind"); 1.913 + } 1.914 + } 1.915 + localContext = localContext.prev; 1.916 + } 1.917 + } 1.918 + } 1.919 + } 1.920 + 1.921 + @Override 1.922 + public void visitLambda(JCLambda tree) { 1.923 + List<Frame> prevStack = frameStack; 1.924 + try { 1.925 + LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree); 1.926 + frameStack = frameStack.prepend(new Frame(tree)); 1.927 + for (JCVariableDecl param : tree.params) { 1.928 + context.addSymbol(param.sym, PARAM); 1.929 + frameStack.head.addLocal(param.sym); 1.930 + } 1.931 + contextMap.put(tree, context); 1.932 + scan(tree.body); 1.933 + context.complete(); 1.934 + } 1.935 + finally { 1.936 + frameStack = prevStack; 1.937 + } 1.938 + } 1.939 + 1.940 + @Override 1.941 + public void visitMethodDef(JCMethodDecl tree) { 1.942 + List<Frame> prevStack = frameStack; 1.943 + try { 1.944 + frameStack = frameStack.prepend(new Frame(tree)); 1.945 + super.visitMethodDef(tree); 1.946 + } 1.947 + finally { 1.948 + frameStack = prevStack; 1.949 + } 1.950 + } 1.951 + 1.952 + @Override 1.953 + public void visitNewClass(JCNewClass tree) { 1.954 + if (lambdaNewClassFilter(context(), tree)) { 1.955 + ((LambdaTranslationContext) context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); 1.956 + } 1.957 + super.visitNewClass(tree); 1.958 + } 1.959 + 1.960 + @Override 1.961 + public void visitReference(JCMemberReference tree) { 1.962 + scan(tree.getQualifierExpression()); 1.963 + contextMap.put(tree, makeReferenceContext(tree)); 1.964 + } 1.965 + 1.966 + @Override 1.967 + public void visitSelect(JCFieldAccess tree) { 1.968 + if (context() != null && lambdaSelectSymbolFilter(tree.sym)) { 1.969 + TranslationContext<?> localContext = context(); 1.970 + while (localContext != null) { 1.971 + if (localContext.tree.hasTag(LAMBDA)) { 1.972 + JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); 1.973 + if (clazz == null) break; 1.974 + ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); 1.975 + } 1.976 + localContext = localContext.prev; 1.977 + } 1.978 + scan(tree.selected); 1.979 + } else { 1.980 + super.visitSelect(tree); 1.981 + } 1.982 + } 1.983 + 1.984 + @Override 1.985 + public void visitVarDef(JCVariableDecl tree) { 1.986 + if (frameStack.head.tree.hasTag(LAMBDA)) { 1.987 + ((LambdaTranslationContext)context()).addSymbol(tree.sym, LOCAL_VAR); 1.988 + } 1.989 + List<Frame> prevStack = frameStack; 1.990 + try { 1.991 + if (tree.sym.owner.kind == MTH) { 1.992 + frameStack.head.addLocal(tree.sym); 1.993 + } 1.994 + frameStack = frameStack.prepend(new Frame(tree)); 1.995 + super.visitVarDef(tree); 1.996 + } 1.997 + finally { 1.998 + frameStack = prevStack; 1.999 + } 1.1000 + } 1.1001 + 1.1002 + private Name lambdaName() { 1.1003 + return names.lambda.append(names.fromString("$" + lambdaCount++)); 1.1004 + } 1.1005 + 1.1006 + /** 1.1007 + * Return a valid owner given the current declaration stack 1.1008 + * (required to skip synthetic lambda symbols) 1.1009 + */ 1.1010 + private Symbol owner() { 1.1011 + List<Frame> frameStack2 = frameStack; 1.1012 + while (frameStack2.nonEmpty()) { 1.1013 + switch (frameStack2.head.tree.getTag()) { 1.1014 + case VARDEF: 1.1015 + if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) { 1.1016 + frameStack2 = frameStack2.tail; 1.1017 + break; 1.1018 + } 1.1019 + JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; 1.1020 + return makeSyntheticMethod(((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC, names.empty, null, cdecl.sym); 1.1021 + case BLOCK: 1.1022 + JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; 1.1023 + return makeSyntheticMethod(((JCBlock)frameStack2.head.tree).flags & STATIC | Flags.BLOCK, names.empty, null, cdecl2.sym); 1.1024 + case CLASSDEF: 1.1025 + return ((JCClassDecl)frameStack2.head.tree).sym; 1.1026 + case METHODDEF: 1.1027 + return ((JCMethodDecl)frameStack2.head.tree).sym; 1.1028 + case LAMBDA: 1.1029 + return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym; 1.1030 + default: 1.1031 + frameStack2 = frameStack2.tail; 1.1032 + } 1.1033 + } 1.1034 + Assert.error(); 1.1035 + return null; 1.1036 + } 1.1037 + 1.1038 + private JCTree enclosingLambda() { 1.1039 + List<Frame> frameStack2 = frameStack; 1.1040 + while (frameStack2.nonEmpty()) { 1.1041 + switch (frameStack2.head.tree.getTag()) { 1.1042 + case CLASSDEF: 1.1043 + case METHODDEF: 1.1044 + return null; 1.1045 + case LAMBDA: 1.1046 + return frameStack2.head.tree; 1.1047 + default: 1.1048 + frameStack2 = frameStack2.tail; 1.1049 + } 1.1050 + } 1.1051 + Assert.error(); 1.1052 + return null; 1.1053 + } 1.1054 + 1.1055 + /** 1.1056 + * Return the declaration corresponding to a symbol in the enclosing 1.1057 + * scope; the depth parameter is used to filter out symbols defined 1.1058 + * in nested scopes (which do not need to undergo capture). 1.1059 + */ 1.1060 + private JCTree capturedDecl(int depth, Symbol sym) { 1.1061 + int currentDepth = frameStack.size() - 1; 1.1062 + for (Frame block : frameStack) { 1.1063 + switch (block.tree.getTag()) { 1.1064 + case CLASSDEF: 1.1065 + ClassSymbol clazz = ((JCClassDecl)block.tree).sym; 1.1066 + if (sym.isMemberOf(clazz, types)) { 1.1067 + return currentDepth > depth ? null : block.tree; 1.1068 + } 1.1069 + break; 1.1070 + case VARDEF: 1.1071 + if (((JCVariableDecl)block.tree).sym == sym && 1.1072 + sym.owner.kind == MTH) { //only locals are captured 1.1073 + return currentDepth > depth ? null : block.tree; 1.1074 + } 1.1075 + break; 1.1076 + case BLOCK: 1.1077 + case METHODDEF: 1.1078 + case LAMBDA: 1.1079 + if (block.locals != null && block.locals.contains(sym)) { 1.1080 + return currentDepth > depth ? null : block.tree; 1.1081 + } 1.1082 + break; 1.1083 + default: 1.1084 + Assert.error("bad decl kind " + block.tree.getTag()); 1.1085 + } 1.1086 + currentDepth--; 1.1087 + } 1.1088 + return null; 1.1089 + } 1.1090 + 1.1091 + private TranslationContext<?> context() { 1.1092 + for (Frame frame : frameStack) { 1.1093 + TranslationContext<?> context = contextMap.get(frame.tree); 1.1094 + if (context != null) { 1.1095 + return context; 1.1096 + } 1.1097 + } 1.1098 + return null; 1.1099 + } 1.1100 + 1.1101 + /** 1.1102 + * This is used to filter out those identifiers that needs to be adjusted 1.1103 + * when translating away lambda expressions 1.1104 + */ 1.1105 + private boolean lambdaIdentSymbolFilter(Symbol sym) { 1.1106 + return (sym.kind == VAR || sym.kind == MTH) 1.1107 + && !sym.isStatic() 1.1108 + && sym.name != names.init; 1.1109 + } 1.1110 + 1.1111 + private boolean lambdaSelectSymbolFilter(Symbol sym) { 1.1112 + return (sym.kind == VAR || sym.kind == MTH) && 1.1113 + !sym.isStatic() && 1.1114 + (sym.name == names._this || 1.1115 + sym.name == names._super); 1.1116 + } 1.1117 + 1.1118 + /** 1.1119 + * This is used to filter out those new class expressions that need to 1.1120 + * be qualified with an enclosing tree 1.1121 + */ 1.1122 + private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) { 1.1123 + if (context != null 1.1124 + && tree.encl == null 1.1125 + && tree.def == null 1.1126 + && tree.type.getEnclosingType().hasTag(NONE)) { 1.1127 + Type encl = tree.type.getEnclosingType(); 1.1128 + Type current = context.owner.enclClass().type; 1.1129 + while (current.hasTag(NONE)) { 1.1130 + if (current.tsym.isSubClass(encl.tsym, types)) { 1.1131 + return true; 1.1132 + } 1.1133 + current = current.getEnclosingType(); 1.1134 + } 1.1135 + return false; 1.1136 + } else { 1.1137 + return false; 1.1138 + } 1.1139 + } 1.1140 + 1.1141 + private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) { 1.1142 + return new LambdaTranslationContext(tree); 1.1143 + } 1.1144 + 1.1145 + private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) { 1.1146 + return new ReferenceTranslationContext(tree); 1.1147 + } 1.1148 + 1.1149 + private class Frame { 1.1150 + final JCTree tree; 1.1151 + List<Symbol> locals; 1.1152 + 1.1153 + public Frame(JCTree tree) { 1.1154 + this.tree = tree; 1.1155 + } 1.1156 + 1.1157 + void addLocal(Symbol sym) { 1.1158 + if (locals == null) { 1.1159 + locals = List.nil(); 1.1160 + } 1.1161 + locals = locals.prepend(sym); 1.1162 + } 1.1163 + } 1.1164 + 1.1165 + /** 1.1166 + * This class is used to store important information regarding translation of 1.1167 + * lambda expression/method references (see subclasses). 1.1168 + */ 1.1169 + private abstract class TranslationContext<T extends JCTree> { 1.1170 + 1.1171 + /** the underlying (untranslated) tree */ 1.1172 + T tree; 1.1173 + 1.1174 + /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ 1.1175 + Symbol owner; 1.1176 + 1.1177 + /** the depth of this lambda expression in the frame stack */ 1.1178 + int depth; 1.1179 + 1.1180 + /** the enclosing translation context (set for nested lambdas/mref) */ 1.1181 + TranslationContext<?> prev; 1.1182 + 1.1183 + TranslationContext(T tree) { 1.1184 + this.tree = tree; 1.1185 + this.owner = owner(); 1.1186 + this.depth = frameStack.size() - 1; 1.1187 + this.prev = context(); 1.1188 + } 1.1189 + } 1.1190 + 1.1191 + /** 1.1192 + * This class retains all the useful information about a lambda expression; 1.1193 + * the contents of this class are filled by the LambdaAnalyzer visitor, 1.1194 + * and the used by the main translation routines in order to adjust references 1.1195 + * to captured locals/members, etc. 1.1196 + */ 1.1197 + private class LambdaTranslationContext extends TranslationContext<JCLambda> { 1.1198 + 1.1199 + /** variable in the enclosing context to which this lambda is assigned */ 1.1200 + Symbol self; 1.1201 + 1.1202 + /** map from original to translated lambda parameters */ 1.1203 + Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>(); 1.1204 + 1.1205 + /** map from original to translated lambda locals */ 1.1206 + Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>(); 1.1207 + 1.1208 + /** map from variables in enclosing scope to translated synthetic parameters */ 1.1209 + Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>(); 1.1210 + 1.1211 + /** map from class symbols to translated synthetic parameters (for captured member access) */ 1.1212 + Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>(); 1.1213 + 1.1214 + /** the synthetic symbol for the method hoisting the translated lambda */ 1.1215 + Symbol translatedSym; 1.1216 + 1.1217 + List<JCVariableDecl> syntheticParams; 1.1218 + 1.1219 + LambdaTranslationContext(JCLambda tree) { 1.1220 + super(tree); 1.1221 + Frame frame = frameStack.head; 1.1222 + if (frame.tree.hasTag(VARDEF)) { 1.1223 + self = ((JCVariableDecl)frame.tree).sym; 1.1224 + } 1.1225 + this.translatedSym = makeSyntheticMethod(0, lambdaName(), null, owner.enclClass()); 1.1226 + } 1.1227 + 1.1228 + /** 1.1229 + * Translate a symbol of a given kind into something suitable for the 1.1230 + * synthetic lambda body 1.1231 + */ 1.1232 + Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) { 1.1233 + if (skind == CAPTURED_THIS) { 1.1234 + return sym; // self represented 1.1235 + } else { 1.1236 + return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym); 1.1237 + } 1.1238 + } 1.1239 + 1.1240 + void addSymbol(Symbol sym, LambdaSymbolKind skind) { 1.1241 + Map<Symbol, Symbol> transMap = null; 1.1242 + String preferredName; 1.1243 + switch (skind) { 1.1244 + case CAPTURED_THIS: 1.1245 + transMap = capturedThis; 1.1246 + preferredName = "encl$" + capturedThis.size(); 1.1247 + break; 1.1248 + case CAPTURED_VAR: 1.1249 + transMap = capturedLocals; 1.1250 + preferredName = "cap$" + capturedLocals.size(); 1.1251 + break; 1.1252 + case LOCAL_VAR: 1.1253 + transMap = lambdaLocals; 1.1254 + preferredName = sym.name.toString(); 1.1255 + break; 1.1256 + case PARAM: 1.1257 + transMap = lambdaParams; 1.1258 + preferredName = sym.name.toString(); 1.1259 + break; 1.1260 + default: throw new AssertionError(); 1.1261 + } 1.1262 + if (!transMap.containsKey(sym)) { 1.1263 + transMap.put(sym, translate(preferredName, sym, skind)); 1.1264 + } 1.1265 + } 1.1266 + 1.1267 + Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) { 1.1268 + LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>(); 1.1269 + for (LambdaSymbolKind skind : skinds) { 1.1270 + switch (skind) { 1.1271 + case CAPTURED_THIS: 1.1272 + translationMap.putAll(capturedThis); 1.1273 + break; 1.1274 + case CAPTURED_VAR: 1.1275 + translationMap.putAll(capturedLocals); 1.1276 + break; 1.1277 + case LOCAL_VAR: 1.1278 + translationMap.putAll(lambdaLocals); 1.1279 + break; 1.1280 + case PARAM: 1.1281 + translationMap.putAll(lambdaParams); 1.1282 + break; 1.1283 + default: throw new AssertionError(); 1.1284 + } 1.1285 + } 1.1286 + return translationMap; 1.1287 + } 1.1288 + 1.1289 + /** 1.1290 + * The translatedSym is not complete/accurate until the analysis is 1.1291 + * finished. Once the analysis is finished, the translatedSym is 1.1292 + * "completed" -- updated with type information, access modifiers, 1.1293 + * and full parameter list. 1.1294 + */ 1.1295 + void complete() { 1.1296 + if (syntheticParams != null) { 1.1297 + return; 1.1298 + } 1.1299 + boolean inInterface = translatedSym.owner.isInterface(); 1.1300 + boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); 1.1301 + boolean needInstance = thisReferenced || inInterface; 1.1302 + 1.1303 + // If instance access isn't needed, make it static 1.1304 + // Interface methods much be public default methods, otherwise make it private 1.1305 + translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE); 1.1306 + 1.1307 + //compute synthetic params 1.1308 + ListBuffer<JCVariableDecl> params = ListBuffer.lb(); 1.1309 + 1.1310 + // The signature of the method is augmented with the following 1.1311 + // synthetic parameters: 1.1312 + // 1.1313 + // 1) reference to enclosing contexts captured by the lambda expression 1.1314 + // 2) enclosing locals captured by the lambda expression 1.1315 + for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) { 1.1316 + params.append(make.VarDef((VarSymbol) thisSym, null)); 1.1317 + } 1.1318 + 1.1319 + syntheticParams = params.toList(); 1.1320 + 1.1321 + //prepend synthetic args to translated lambda method signature 1.1322 + translatedSym.type = (MethodType) types.createMethodTypeWithParameters( 1.1323 + (MethodType) generatedLambdaSig(), 1.1324 + TreeInfo.types(syntheticParams)); 1.1325 + } 1.1326 + 1.1327 + Type enclosingType() { 1.1328 + //local inner classes defined inside a lambda are always non-static 1.1329 + return owner.enclClass().type; 1.1330 + } 1.1331 + 1.1332 + Type generatedLambdaSig() { 1.1333 + return types.erasure(types.findDescriptorType(tree.targetType)); 1.1334 + } 1.1335 + } 1.1336 + 1.1337 + /** 1.1338 + * This class retains all the useful information about a method reference; 1.1339 + * the contents of this class are filled by the LambdaAnalyzer visitor, 1.1340 + * and the used by the main translation routines in order to adjust method 1.1341 + * references (i.e. in case a bridge is needed) 1.1342 + */ 1.1343 + private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> { 1.1344 + 1.1345 + final boolean isSuper; 1.1346 + final Symbol bridgeSym; 1.1347 + 1.1348 + ReferenceTranslationContext(JCMemberReference tree) { 1.1349 + super(tree); 1.1350 + this.isSuper = tree.hasKind(ReferenceKind.SUPER); 1.1351 + this.bridgeSym = needsBridge() 1.1352 + ? makeSyntheticMethod(isSuper ? 0 : STATIC, 1.1353 + lambdaName().append(names.fromString("$bridge")), null, 1.1354 + owner.enclClass()) 1.1355 + : null; 1.1356 + } 1.1357 + 1.1358 + /** 1.1359 + * Get the opcode associated with this method reference 1.1360 + */ 1.1361 + int referenceKind() { 1.1362 + return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym); 1.1363 + } 1.1364 + 1.1365 + boolean needsVarArgsConversion() { 1.1366 + return tree.varargsElement != null; 1.1367 + } 1.1368 + 1.1369 + /** 1.1370 + * @return Is this an array operation like clone() 1.1371 + */ 1.1372 + boolean isArrayOp() { 1.1373 + return tree.sym.owner == syms.arrayClass; 1.1374 + } 1.1375 + 1.1376 + /** 1.1377 + * Does this reference needs a bridge (i.e. var args need to be 1.1378 + * expanded or "super" is used) 1.1379 + */ 1.1380 + final boolean needsBridge() { 1.1381 + return isSuper || needsVarArgsConversion() || isArrayOp(); 1.1382 + } 1.1383 + 1.1384 + Type generatedRefSig() { 1.1385 + return types.erasure(tree.sym.type); 1.1386 + } 1.1387 + 1.1388 + Type bridgedRefSig() { 1.1389 + return types.erasure(types.findDescriptorSymbol(tree.targetType.tsym).type); 1.1390 + } 1.1391 + } 1.1392 + } 1.1393 + // </editor-fold> 1.1394 + 1.1395 + enum LambdaSymbolKind { 1.1396 + CAPTURED_VAR, 1.1397 + CAPTURED_THIS, 1.1398 + LOCAL_VAR, 1.1399 + PARAM; 1.1400 + } 1.1401 +}