Mon, 18 Mar 2013 14:40:32 -0700
8008425: Remove interim new javax.lang.model API for type-annotations
Reviewed-by: darcy
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.tools.javac.comp;
27 import com.sun.tools.javac.tree.*;
28 import com.sun.tools.javac.tree.JCTree;
29 import com.sun.tools.javac.tree.JCTree.*;
30 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
31 import com.sun.tools.javac.tree.TreeMaker;
32 import com.sun.tools.javac.tree.TreeScanner;
33 import com.sun.tools.javac.tree.TreeTranslator;
34 import com.sun.tools.javac.code.Kinds;
35 import com.sun.tools.javac.code.Scope;
36 import com.sun.tools.javac.code.Symbol;
37 import com.sun.tools.javac.code.Symbol.ClassSymbol;
38 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
39 import com.sun.tools.javac.code.Symbol.MethodSymbol;
40 import com.sun.tools.javac.code.Symbol.VarSymbol;
41 import com.sun.tools.javac.code.Symtab;
42 import com.sun.tools.javac.code.Type;
43 import com.sun.tools.javac.code.Type.ClassType;
44 import com.sun.tools.javac.code.Type.MethodType;
45 import com.sun.tools.javac.code.Types;
46 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*;
47 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
48 import com.sun.tools.javac.jvm.*;
49 import com.sun.tools.javac.util.*;
50 import com.sun.tools.javac.util.List;
51 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
52 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
54 import java.util.HashMap;
55 import java.util.LinkedHashMap;
56 import java.util.Map;
58 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
59 import static com.sun.tools.javac.code.Flags.*;
60 import static com.sun.tools.javac.code.Kinds.*;
61 import static com.sun.tools.javac.code.TypeTag.*;
62 import static com.sun.tools.javac.tree.JCTree.Tag.*;
64 /**
65 * This pass desugars lambda expressions into static methods
66 *
67 * <p><b>This is NOT part of any supported API.
68 * If you write code that depends on this, you do so at your own risk.
69 * This code and its internal interfaces are subject to change or
70 * deletion without notice.</b>
71 */
72 public class LambdaToMethod extends TreeTranslator {
74 private Lower lower;
75 private Names names;
76 private Symtab syms;
77 private Resolve rs;
78 private TreeMaker make;
79 private Types types;
80 private TransTypes transTypes;
81 private Env<AttrContext> attrEnv;
83 /** the analyzer scanner */
84 private LambdaAnalyzer analyzer;
86 /** map from lambda trees to translation contexts */
87 private Map<JCTree, TranslationContext<?>> contextMap;
89 /** current translation context (visitor argument) */
90 private TranslationContext<?> context;
92 /** info about the current class being processed */
93 private KlassInfo kInfo;
95 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
96 public static final int FLAG_SERIALIZABLE = 1 << 0;
98 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
99 public static final int FLAG_MARKERS = 1 << 1;
101 private class KlassInfo {
103 /**
104 * list of methods to append
105 */
106 private ListBuffer<JCTree> appendedMethodList;
108 /**
109 * list of deserialization cases
110 */
111 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
113 /**
114 * deserialize method symbol
115 */
116 private final MethodSymbol deserMethodSym;
118 /**
119 * deserialize method parameter symbol
120 */
121 private final VarSymbol deserParamSym;
123 private KlassInfo(Symbol kSym) {
124 appendedMethodList = ListBuffer.lb();
125 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
126 long flags = PRIVATE | STATIC | SYNTHETIC;
127 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
128 List.<Type>nil(), syms.methodClass);
129 deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
130 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
131 syms.serializedLambdaType, deserMethodSym);
132 }
134 private void addMethod(JCTree decl) {
135 appendedMethodList = appendedMethodList.prepend(decl);
136 }
137 }
139 // <editor-fold defaultstate="collapsed" desc="Instantiating">
140 private static final Context.Key<LambdaToMethod> unlambdaKey =
141 new Context.Key<LambdaToMethod>();
143 public static LambdaToMethod instance(Context context) {
144 LambdaToMethod instance = context.get(unlambdaKey);
145 if (instance == null) {
146 instance = new LambdaToMethod(context);
147 }
148 return instance;
149 }
151 private LambdaToMethod(Context context) {
152 lower = Lower.instance(context);
153 names = Names.instance(context);
154 syms = Symtab.instance(context);
155 rs = Resolve.instance(context);
156 make = TreeMaker.instance(context);
157 types = Types.instance(context);
158 transTypes = TransTypes.instance(context);
159 analyzer = new LambdaAnalyzer();
160 }
161 // </editor-fold>
163 // <editor-fold defaultstate="collapsed" desc="translate methods">
164 @Override
165 public <T extends JCTree> T translate(T tree) {
166 TranslationContext<?> newContext = contextMap.get(tree);
167 return translate(tree, newContext != null ? newContext : context);
168 }
170 public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
171 TranslationContext<?> prevContext = context;
172 try {
173 context = newContext;
174 return super.translate(tree);
175 }
176 finally {
177 context = prevContext;
178 }
179 }
181 public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
182 ListBuffer<T> buf = ListBuffer.lb();
183 for (T tree : trees) {
184 buf.append(translate(tree, newContext));
185 }
186 return buf.toList();
187 }
189 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
190 this.make = make;
191 this.attrEnv = env;
192 this.context = null;
193 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
194 return translate(cdef);
195 }
196 // </editor-fold>
198 // <editor-fold defaultstate="collapsed" desc="visitor methods">
199 /**
200 * Visit a class.
201 * Maintain the translatedMethodList across nested classes.
202 * Append the translatedMethodList to the class after it is translated.
203 * @param tree
204 */
205 @Override
206 public void visitClassDef(JCClassDecl tree) {
207 if (tree.sym.owner.kind == PCK) {
208 //analyze class
209 analyzer.analyzeClass(tree);
210 }
211 KlassInfo prevKlassInfo = kInfo;
212 try {
213 kInfo = new KlassInfo(tree.sym);
214 super.visitClassDef(tree);
215 if (!kInfo.deserializeCases.isEmpty()) {
216 kInfo.addMethod(makeDeserializeMethod(tree.sym));
217 }
218 //add all translated instance methods here
219 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
220 tree.defs = tree.defs.appendList(newMethods);
221 for (JCTree lambda : newMethods) {
222 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
223 }
224 result = tree;
225 } finally {
226 kInfo = prevKlassInfo;
227 }
228 }
230 /**
231 * Translate a lambda into a method to be inserted into the class.
232 * Then replace the lambda site with an invokedynamic call of to lambda
233 * meta-factory, which will use the lambda method.
234 * @param tree
235 */
236 @Override
237 public void visitLambda(JCLambda tree) {
238 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
239 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
240 MethodType lambdaType = (MethodType) sym.type;
242 //create the method declaration hoisting the lambda body
243 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
244 sym.name,
245 make.QualIdent(lambdaType.getReturnType().tsym),
246 List.<JCTypeParameter>nil(),
247 localContext.syntheticParams,
248 lambdaType.getThrownTypes() == null ?
249 List.<JCExpression>nil() :
250 make.Types(lambdaType.getThrownTypes()),
251 null,
252 null);
253 lambdaDecl.sym = sym;
254 lambdaDecl.type = lambdaType;
256 //translate lambda body
257 //As the lambda body is translated, all references to lambda locals,
258 //captured variables, enclosing members are adjusted accordingly
259 //to refer to the static method parameters (rather than i.e. acessing to
260 //captured members directly).
261 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
263 //Add the method to the list of methods to be added to this class.
264 kInfo.addMethod(lambdaDecl);
266 //now that we have generated a method for the lambda expression,
267 //we can translate the lambda into a method reference pointing to the newly
268 //created method.
269 //
270 //Note that we need to adjust the method handle so that it will match the
271 //signature of the SAM descriptor - this means that the method reference
272 //should be added the following synthetic arguments:
273 //
274 // * the "this" argument if it is an instance method
275 // * enclosing locals captured by the lambda expression
277 ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
279 if (!sym.isStatic()) {
280 syntheticInits.append(makeThis(
281 sym.owner.enclClass().asType(),
282 localContext.owner.enclClass()));
283 }
285 //add captured locals
286 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
287 if (fv != localContext.self) {
288 JCTree captured_local = make.Ident(fv).setType(fv.type);
289 syntheticInits.append((JCExpression) captured_local);
290 }
291 }
293 //then, determine the arguments to the indy call
294 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
296 //build a sam instance using an indy call to the meta-factory
297 int refKind = referenceKind(sym);
299 //convert to an invokedynamic call
300 result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
301 }
303 private JCIdent makeThis(Type type, Symbol owner) {
304 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
305 names._this,
306 type,
307 owner);
308 return make.Ident(_this);
309 }
311 /**
312 * Translate a method reference into an invokedynamic call to the
313 * meta-factory.
314 * @param tree
315 */
316 @Override
317 public void visitReference(JCMemberReference tree) {
318 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
320 //first determine the method symbol to be used to generate the sam instance
321 //this is either the method reference symbol, or the bridged reference symbol
322 Symbol refSym = localContext.needsBridge() ?
323 localContext.bridgeSym :
324 tree.sym;
326 //build the bridge method, if needed
327 if (localContext.needsBridge()) {
328 bridgeMemberReference(tree, localContext);
329 }
331 //the qualifying expression is treated as a special captured arg
332 JCExpression init;
333 switch(tree.kind) {
335 case IMPLICIT_INNER: /** Inner :: new */
336 case SUPER: /** super :: instMethod */
337 init = makeThis(
338 localContext.owner.enclClass().asType(),
339 localContext.owner.enclClass());
340 break;
342 case BOUND: /** Expr :: instMethod */
343 init = tree.getQualifierExpression();
344 break;
346 case UNBOUND: /** Type :: instMethod */
347 case STATIC: /** Type :: staticMethod */
348 case TOPLEVEL: /** Top level :: new */
349 case ARRAY_CTOR: /** ArrayType :: new */
350 init = null;
351 break;
353 default:
354 throw new InternalError("Should not have an invalid kind");
355 }
357 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
360 //build a sam instance using an indy call to the meta-factory
361 result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
362 }
364 /**
365 * Translate identifiers within a lambda to the mapped identifier
366 * @param tree
367 */
368 @Override
369 public void visitIdent(JCIdent tree) {
370 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
371 super.visitIdent(tree);
372 } else {
373 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
374 if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
375 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
376 result = make.Ident(translatedSym).setType(tree.type);
377 } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
378 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
379 result = make.Ident(translatedSym).setType(tree.type);
380 } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
381 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
382 result = make.Ident(translatedSym).setType(translatedSym.type);
383 } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
384 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
385 result = make.Ident(translatedSym).setType(tree.type);
386 } else {
387 if (tree.sym.owner.kind == Kinds.TYP) {
388 for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
389 if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
390 JCExpression enclRef = make.Ident(encl_entry.getValue());
391 result = tree.sym.name == names._this
392 ? enclRef.setType(tree.type)
393 : make.Select(enclRef, tree.sym).setType(tree.type);
394 result = tree;
395 return;
396 }
397 }
398 }
399 //access to untranslated symbols (i.e. compile-time constants,
400 //members defined inside the lambda body, etc.) )
401 super.visitIdent(tree);
402 }
403 }
404 }
406 @Override
407 public void visitVarDef(JCVariableDecl tree) {
408 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
409 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
410 JCExpression init = translate(tree.init);
411 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
412 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
413 JCExpression init = translate(tree.init);
414 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
415 result = make.VarDef(xsym, init);
416 // Replace the entered symbol for this variable
417 Scope sc = tree.sym.owner.members();
418 if (sc != null) {
419 sc.remove(tree.sym);
420 sc.enter(xsym);
421 }
422 } else {
423 super.visitVarDef(tree);
424 }
425 }
427 // </editor-fold>
429 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
431 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
432 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
433 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
434 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
435 }
437 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
438 Type restype = lambdaMethodDecl.type.getReturnType();
439 boolean isLambda_void = expr.type.hasTag(VOID);
440 boolean isTarget_void = restype.hasTag(VOID);
441 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
442 if (isTarget_void) {
443 //target is void:
444 // BODY;
445 JCStatement stat = make.Exec(expr);
446 return make.Block(0, List.<JCStatement>of(stat));
447 } else if (isLambda_void && isTarget_Void) {
448 //void to Void conversion:
449 // BODY; return null;
450 ListBuffer<JCStatement> stats = ListBuffer.lb();
451 stats.append(make.Exec(expr));
452 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
453 return make.Block(0, stats.toList());
454 } else {
455 //non-void to non-void conversion:
456 // return (TYPE)BODY;
457 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
458 return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
459 }
460 }
462 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
463 final Type restype = lambdaMethodDecl.type.getReturnType();
464 final boolean isTarget_void = restype.hasTag(VOID);
465 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
467 class LambdaBodyTranslator extends TreeTranslator {
469 @Override
470 public void visitClassDef(JCClassDecl tree) {
471 //do NOT recurse on any inner classes
472 result = tree;
473 }
475 @Override
476 public void visitLambda(JCLambda tree) {
477 //do NOT recurse on any nested lambdas
478 result = tree;
479 }
481 @Override
482 public void visitReturn(JCReturn tree) {
483 boolean isLambda_void = tree.expr == null;
484 if (isTarget_void && !isLambda_void) {
485 //Void to void conversion:
486 // { TYPE $loc = RET-EXPR; return; }
487 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
488 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
489 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
490 } else if (!isTarget_void || !isLambda_void) {
491 //non-void to non-void conversion:
492 // return (TYPE)RET-EXPR;
493 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
494 result = tree;
495 } else {
496 result = tree;
497 }
499 }
500 }
502 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
503 if (completeNormally && isTarget_Void) {
504 //there's no return statement and the lambda (possibly inferred)
505 //return type is java.lang.Void; emit a synthetic return statement
506 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
507 }
508 return trans_block;
509 }
511 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
512 ListBuffer<JCCase> cases = ListBuffer.lb();
513 ListBuffer<JCBreak> breaks = ListBuffer.lb();
514 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
515 JCBreak br = make.Break(null);
516 breaks.add(br);
517 List<JCStatement> stmts = entry.getValue().append(br).toList();
518 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
519 }
520 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
521 for (JCBreak br : breaks) {
522 br.target = sw;
523 }
524 JCBlock body = make.Block(0L, List.<JCStatement>of(
525 sw,
526 make.Throw(makeNewClass(
527 syms.illegalArgumentExceptionType,
528 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
529 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
530 names.deserializeLambda,
531 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
532 List.<JCTypeParameter>nil(),
533 List.of(make.VarDef(kInfo.deserParamSym, null)),
534 List.<JCExpression>nil(),
535 body,
536 null);
537 deser.sym = kInfo.deserMethodSym;
538 deser.type = kInfo.deserMethodSym.type;
539 //System.err.printf("DESER: '%s'\n", deser);
540 return deser;
541 }
543 /** Make an attributed class instance creation expression.
544 * @param ctype The class type.
545 * @param args The constructor arguments.
546 */
547 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
548 JCNewClass tree = make.NewClass(null,
549 null, make.QualIdent(ctype.tsym), args, null);
550 tree.constructor = rs.resolveConstructor(
551 null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
552 tree.type = ctype;
553 return tree;
554 }
556 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
557 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
558 String functionalInterfaceClass = classSig(targetType);
559 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
560 String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
561 String implClass = classSig(types.erasure(refSym.owner.type));
562 String implMethodName = refSym.getQualifiedName().toString();
563 String implMethodSignature = methodSig(types.erasure(refSym.type));
565 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
566 ListBuffer<JCExpression> serArgs = ListBuffer.lb();
567 int i = 0;
568 for (Type t : indyType.getParameterTypes()) {
569 List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
570 List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
571 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
572 ++i;
573 }
574 JCStatement stmt = make.If(
575 deserTest(deserTest(deserTest(deserTest(deserTest(
576 kindTest,
577 "getFunctionalInterfaceClass", functionalInterfaceClass),
578 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
579 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
580 "getImplClass", implClass),
581 "getImplMethodSignature", implMethodSignature),
582 make.Return(makeIndyCall(
583 pos,
584 syms.lambdaMetafactory,
585 names.altMetaFactory,
586 staticArgs, indyType, serArgs.toList())),
587 null);
588 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
589 if (stmts == null) {
590 stmts = ListBuffer.lb();
591 kInfo.deserializeCases.put(implMethodName, stmts);
592 }
593 /****
594 System.err.printf("+++++++++++++++++\n");
595 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
596 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
597 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
598 System.err.printf("*implMethodKind: %d\n", implMethodKind);
599 System.err.printf("*implClass: '%s'\n", implClass);
600 System.err.printf("*implMethodName: '%s'\n", implMethodName);
601 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
602 ****/
603 stmts.append(stmt);
604 }
606 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
607 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
608 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
609 testExpr.setType(syms.booleanType);
610 return testExpr;
611 }
613 private JCExpression deserTest(JCExpression prev, String func, String lit) {
614 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
615 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
616 JCMethodInvocation eqtest = make.Apply(
617 List.<JCExpression>nil(),
618 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
619 List.<JCExpression>of(make.Literal(lit)));
620 eqtest.setType(syms.booleanType);
621 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
622 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
623 compound.setType(syms.booleanType);
624 return compound;
625 }
627 private JCExpression deserGetter(String func, Type type) {
628 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
629 }
631 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
632 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
633 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
634 return make.Apply(
635 List.<JCExpression>nil(),
636 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
637 args).setType(type);
638 }
640 /**
641 * Create new synthetic method with given flags, name, type, owner
642 */
643 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
644 return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
645 }
647 /**
648 * Create new synthetic variable with given flags, name, type, owner
649 */
650 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
651 return makeSyntheticVar(flags, names.fromString(name), type, owner);
652 }
654 /**
655 * Create new synthetic variable with given flags, name, type, owner
656 */
657 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
658 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
659 }
661 /**
662 * Set varargsElement field on a given tree (must be either a new class tree
663 * or a method call tree)
664 */
665 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
666 if (varargsElement != null) {
667 switch (tree.getTag()) {
668 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
669 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
670 default: throw new AssertionError();
671 }
672 }
673 }
675 /**
676 * Convert method/constructor arguments by inserting appropriate cast
677 * as required by type-erasure - this is needed when bridging a lambda/method
678 * reference, as the bridged signature might require downcast to be compatible
679 * with the generated signature.
680 */
681 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
682 Assert.check(meth.kind == Kinds.MTH);
683 List<Type> formals = types.erasure(meth.type).getParameterTypes();
684 if (varargsElement != null) {
685 Assert.check((meth.flags() & VARARGS) != 0);
686 }
687 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
688 }
690 // </editor-fold>
692 /**
693 * Generate an adapter method "bridge" for a method reference which cannot
694 * be used directly.
695 */
696 private class MemberReferenceBridger {
698 private final JCMemberReference tree;
699 private final ReferenceTranslationContext localContext;
700 private final ListBuffer<JCExpression> args = ListBuffer.lb();
701 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
703 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
704 this.tree = tree;
705 this.localContext = localContext;
706 }
708 /**
709 * Generate the bridge
710 */
711 JCMethodDecl bridge() {
712 int prevPos = make.pos;
713 try {
714 make.at(tree);
715 Type samDesc = localContext.bridgedRefSig();
716 List<Type> samPTypes = samDesc.getParameterTypes();
718 //an extra argument is prepended to the signature of the bridge in case
719 //the member reference is an instance method reference (in which case
720 //the receiver expression is passed to the bridge itself).
721 Type recType = null;
722 switch (tree.kind) {
723 case IMPLICIT_INNER:
724 recType = tree.sym.owner.type.getEnclosingType();
725 break;
726 case BOUND:
727 recType = tree.getQualifierExpression().type;
728 break;
729 case UNBOUND:
730 recType = samPTypes.head;
731 samPTypes = samPTypes.tail;
732 break;
733 }
735 //generate the parameter list for the bridged member reference - the
736 //bridge signature will match the signature of the target sam descriptor
738 VarSymbol rcvr = (recType == null)
739 ? null
740 : addParameter("rec$", recType, false);
742 List<Type> refPTypes = tree.sym.type.getParameterTypes();
743 int refSize = refPTypes.size();
744 int samSize = samPTypes.size();
745 // Last parameter to copy from referenced method
746 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
748 List<Type> l = refPTypes;
749 // Use parameter types of the referenced method, excluding final var args
750 for (int i = 0; l.nonEmpty() && i < last; ++i) {
751 addParameter("x$" + i, l.head, true);
752 l = l.tail;
753 }
754 // Flatten out the var args
755 for (int i = last; i < samSize; ++i) {
756 addParameter("xva$" + i, tree.varargsElement, true);
757 }
759 //generate the bridge method declaration
760 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
761 localContext.bridgeSym.name,
762 make.QualIdent(samDesc.getReturnType().tsym),
763 List.<JCTypeParameter>nil(),
764 params.toList(),
765 tree.sym.type.getThrownTypes() == null
766 ? List.<JCExpression>nil()
767 : make.Types(tree.sym.type.getThrownTypes()),
768 null,
769 null);
770 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
771 bridgeDecl.type = localContext.bridgeSym.type =
772 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
774 //bridge method body generation - this can be either a method call or a
775 //new instance creation expression, depending on the member reference kind
776 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
777 ? bridgeExpressionInvoke(makeReceiver(rcvr))
778 : bridgeExpressionNew();
780 //the body is either a return expression containing a method call,
781 //or the method call itself, depending on whether the return type of
782 //the bridge is non-void/void.
783 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
785 return bridgeDecl;
786 } finally {
787 make.at(prevPos);
788 }
789 }
790 //where
791 private JCExpression makeReceiver(VarSymbol rcvr) {
792 if (rcvr == null) return null;
793 JCExpression rcvrExpr = make.Ident(rcvr);
794 Type rcvrType = tree.sym.enclClass().type;
795 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
796 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
797 }
798 return rcvrExpr;
799 }
801 /**
802 * determine the receiver of the bridged method call - the receiver can
803 * be either the synthetic receiver parameter or a type qualifier; the
804 * original qualifier expression is never used here, as it might refer
805 * to symbols not available in the static context of the bridge
806 */
807 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
808 JCExpression qualifier =
809 tree.sym.isStatic() ?
810 make.Type(tree.sym.owner.type) :
811 (rcvr != null) ?
812 rcvr :
813 tree.getQualifierExpression();
815 //create the qualifier expression
816 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
817 select.sym = tree.sym;
818 select.type = tree.sym.erasure(types);
820 //create the method call expression
821 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
822 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
823 setType(tree.sym.erasure(types).getReturnType());
825 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
826 setVarargsIfNeeded(apply, tree.varargsElement);
827 return apply;
828 }
830 /**
831 * the enclosing expression is either 'null' (no enclosing type) or set
832 * to the first bridge synthetic parameter
833 */
834 private JCExpression bridgeExpressionNew() {
835 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
836 //create the array creation expression
837 JCNewArray newArr = make.NewArray(
838 make.Type(types.elemtype(tree.getQualifierExpression().type)),
839 List.of(make.Ident(params.first())),
840 null);
841 newArr.type = tree.getQualifierExpression().type;
842 return newArr;
843 } else {
844 JCExpression encl = null;
845 switch (tree.kind) {
846 case UNBOUND:
847 case IMPLICIT_INNER:
848 encl = make.Ident(params.first());
849 }
851 //create the instance creation expression
852 JCNewClass newClass = make.NewClass(encl,
853 List.<JCExpression>nil(),
854 make.Type(tree.getQualifierExpression().type),
855 convertArgs(tree.sym, args.toList(), tree.varargsElement),
856 null);
857 newClass.constructor = tree.sym;
858 newClass.constructorType = tree.sym.erasure(types);
859 newClass.type = tree.getQualifierExpression().type;
860 setVarargsIfNeeded(newClass, tree.varargsElement);
861 return newClass;
862 }
863 }
865 private VarSymbol addParameter(String name, Type p, boolean genArg) {
866 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
867 params.append(make.VarDef(vsym, null));
868 if (genArg) {
869 args.append(make.Ident(vsym));
870 }
871 return vsym;
872 }
873 }
875 /**
876 * Bridges a member reference - this is needed when:
877 * * Var args in the referenced method need to be flattened away
878 * * super is used
879 */
880 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
881 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
882 }
884 /**
885 * Generate an indy method call to the meta factory
886 */
887 private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
888 boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
889 //determine the static bsm args
890 Type mtype = types.erasure(tree.descriptorType);
891 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
892 List<Object> staticArgs = List.<Object>of(
893 new Pool.MethodHandle(ClassFile.REF_invokeInterface,
894 types.findDescriptorSymbol(tree.type.tsym), types),
895 new Pool.MethodHandle(refKind, refSym, types),
896 new MethodType(mtype.getParameterTypes(),
897 mtype.getReturnType(),
898 mtype.getThrownTypes(),
899 syms.methodClass));
901 //computed indy arg types
902 ListBuffer<Type> indy_args_types = ListBuffer.lb();
903 for (JCExpression arg : indy_args) {
904 indy_args_types.append(arg.type);
905 }
907 //finally, compute the type of the indy call
908 MethodType indyType = new MethodType(indy_args_types.toList(),
909 tree.type,
910 List.<Type>nil(),
911 syms.methodClass);
913 Name metafactoryName = needsAltMetafactory ?
914 names.altMetaFactory : names.metaFactory;
916 if (needsAltMetafactory) {
917 ListBuffer<Object> markers = ListBuffer.lb();
918 for (Symbol t : tree.targets.tail) {
919 if (t != syms.serializableType.tsym) {
920 markers.append(t);
921 }
922 }
923 int flags = isSerializable? FLAG_SERIALIZABLE : 0;
924 boolean hasMarkers = markers.nonEmpty();
925 flags |= hasMarkers ? FLAG_MARKERS : 0;
926 staticArgs = staticArgs.append(flags);
927 if (hasMarkers) {
928 staticArgs = staticArgs.append(markers.length());
929 staticArgs = staticArgs.appendList(markers.toList());
930 }
931 if (isSerializable) {
932 addDeserializationCase(refKind, refSym, tree.type, samSym,
933 tree, staticArgs, indyType);
934 }
935 }
937 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
938 }
940 /**
941 * Generate an indy method call with given name, type and static bootstrap
942 * arguments types
943 */
944 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
945 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
946 int prevPos = make.pos;
947 try {
948 make.at(pos);
949 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
950 syms.stringType,
951 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
953 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
954 bsmName, bsm_staticArgs, List.<Type>nil());
956 DynamicMethodSymbol dynSym =
957 new DynamicMethodSymbol(names.lambda,
958 syms.noSymbol,
959 bsm.isStatic() ?
960 ClassFile.REF_invokeStatic :
961 ClassFile.REF_invokeVirtual,
962 (MethodSymbol)bsm,
963 indyType,
964 staticArgs.toArray());
966 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
967 qualifier.sym = dynSym;
968 qualifier.type = indyType.getReturnType();
970 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
971 proxyCall.type = indyType.getReturnType();
972 return proxyCall;
973 } finally {
974 make.at(prevPos);
975 }
976 }
977 //where
978 private List<Type> bsmStaticArgToTypes(List<Object> args) {
979 ListBuffer<Type> argtypes = ListBuffer.lb();
980 for (Object arg : args) {
981 argtypes.append(bsmStaticArgToType(arg));
982 }
983 return argtypes.toList();
984 }
986 private Type bsmStaticArgToType(Object arg) {
987 Assert.checkNonNull(arg);
988 if (arg instanceof ClassSymbol) {
989 return syms.classType;
990 } else if (arg instanceof Integer) {
991 return syms.intType;
992 } else if (arg instanceof Long) {
993 return syms.longType;
994 } else if (arg instanceof Float) {
995 return syms.floatType;
996 } else if (arg instanceof Double) {
997 return syms.doubleType;
998 } else if (arg instanceof String) {
999 return syms.stringType;
1000 } else if (arg instanceof Pool.MethodHandle) {
1001 return syms.methodHandleType;
1002 } else if (arg instanceof MethodType) {
1003 return syms.methodTypeType;
1004 } else {
1005 Assert.error("bad static arg " + arg.getClass());
1006 return null;
1007 }
1008 }
1010 /**
1011 * Get the opcode associated with this method reference
1012 */
1013 private int referenceKind(Symbol refSym) {
1014 if (refSym.isConstructor()) {
1015 return ClassFile.REF_newInvokeSpecial;
1016 } else {
1017 if (refSym.isStatic()) {
1018 return ClassFile.REF_invokeStatic;
1019 } else if (refSym.enclClass().isInterface()) {
1020 return ClassFile.REF_invokeInterface;
1021 } else {
1022 return ClassFile.REF_invokeVirtual;
1023 }
1024 }
1025 }
1027 // </editor-fold>
1029 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
1030 /**
1031 * This visitor collects information about translation of a lambda expression.
1032 * More specifically, it keeps track of the enclosing contexts and captured locals
1033 * accessed by the lambda being translated (as well as other useful info).
1034 */
1035 class LambdaAnalyzer extends TreeScanner {
1037 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1038 private List<Frame> frameStack;
1040 /**
1041 * keep the count of lambda expression (used to generate unambiguous
1042 * names)
1043 */
1044 private int lambdaCount = 0;
1046 /**
1047 * keep the count of lambda expression defined in given context (used to
1048 * generate unambiguous names for serializable lambdas)
1049 */
1050 private Map<String, Integer> serializableLambdaCounts =
1051 new HashMap<String, Integer>();
1053 private Map<Symbol, JCClassDecl> localClassDefs;
1055 /**
1056 * maps for fake clinit symbols to be used as owners of lambda occurring in
1057 * a static var init context
1058 */
1059 private Map<ClassSymbol, Symbol> clinits =
1060 new HashMap<ClassSymbol, Symbol>();
1062 private void analyzeClass(JCClassDecl tree) {
1063 frameStack = List.nil();
1064 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1065 scan(tree);
1066 }
1068 @Override
1069 public void visitBlock(JCBlock tree) {
1070 List<Frame> prevStack = frameStack;
1071 try {
1072 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1073 frameStack = frameStack.prepend(new Frame(tree));
1074 }
1075 super.visitBlock(tree);
1076 }
1077 finally {
1078 frameStack = prevStack;
1079 }
1080 }
1082 @Override
1083 public void visitClassDef(JCClassDecl tree) {
1084 List<Frame> prevStack = frameStack;
1085 Map<String, Integer> prevSerializableLambdaCount =
1086 serializableLambdaCounts;
1087 Map<ClassSymbol, Symbol> prevClinits = clinits;
1088 try {
1089 serializableLambdaCounts = new HashMap<String, Integer>();
1090 prevClinits = new HashMap<ClassSymbol, Symbol>();
1091 if (tree.sym.owner.kind == MTH) {
1092 localClassDefs.put(tree.sym, tree);
1093 }
1094 if (directlyEnclosingLambda() != null) {
1095 tree.sym.owner = owner();
1096 if (tree.sym.hasOuterInstance()) {
1097 //if a class is defined within a lambda, the lambda must capture
1098 //its enclosing instance (if any)
1099 TranslationContext<?> localContext = context();
1100 while (localContext != null) {
1101 if (localContext.tree.getTag() == LAMBDA) {
1102 ((LambdaTranslationContext)localContext)
1103 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1104 }
1105 localContext = localContext.prev;
1106 }
1107 }
1108 }
1109 frameStack = frameStack.prepend(new Frame(tree));
1110 super.visitClassDef(tree);
1111 }
1112 finally {
1113 frameStack = prevStack;
1114 serializableLambdaCounts = prevSerializableLambdaCount;
1115 clinits = prevClinits;
1116 }
1117 }
1119 @Override
1120 public void visitIdent(JCIdent tree) {
1121 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1122 if (tree.sym.kind == VAR &&
1123 tree.sym.owner.kind == MTH &&
1124 tree.type.constValue() == null) {
1125 TranslationContext<?> localContext = context();
1126 while (localContext != null) {
1127 if (localContext.tree.getTag() == LAMBDA) {
1128 JCTree block = capturedDecl(localContext.depth, tree.sym);
1129 if (block == null) break;
1130 ((LambdaTranslationContext)localContext)
1131 .addSymbol(tree.sym, CAPTURED_VAR);
1132 }
1133 localContext = localContext.prev;
1134 }
1135 } else if (tree.sym.owner.kind == TYP) {
1136 TranslationContext<?> localContext = context();
1137 while (localContext != null) {
1138 if (localContext.tree.hasTag(LAMBDA)) {
1139 JCTree block = capturedDecl(localContext.depth, tree.sym);
1140 if (block == null) break;
1141 switch (block.getTag()) {
1142 case CLASSDEF:
1143 JCClassDecl cdecl = (JCClassDecl)block;
1144 ((LambdaTranslationContext)localContext)
1145 .addSymbol(cdecl.sym, CAPTURED_THIS);
1146 break;
1147 default:
1148 Assert.error("bad block kind");
1149 }
1150 }
1151 localContext = localContext.prev;
1152 }
1153 }
1154 }
1155 super.visitIdent(tree);
1156 }
1158 @Override
1159 public void visitLambda(JCLambda tree) {
1160 List<Frame> prevStack = frameStack;
1161 try {
1162 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1163 frameStack = frameStack.prepend(new Frame(tree));
1164 for (JCVariableDecl param : tree.params) {
1165 context.addSymbol(param.sym, PARAM);
1166 frameStack.head.addLocal(param.sym);
1167 }
1168 contextMap.put(tree, context);
1169 scan(tree.body);
1170 context.complete();
1171 }
1172 finally {
1173 frameStack = prevStack;
1174 }
1175 }
1177 @Override
1178 public void visitMethodDef(JCMethodDecl tree) {
1179 List<Frame> prevStack = frameStack;
1180 try {
1181 frameStack = frameStack.prepend(new Frame(tree));
1182 super.visitMethodDef(tree);
1183 }
1184 finally {
1185 frameStack = prevStack;
1186 }
1187 }
1189 @Override
1190 public void visitNewClass(JCNewClass tree) {
1191 if (lambdaNewClassFilter(context(), tree)) {
1192 TranslationContext<?> localContext = context();
1193 while (localContext != null) {
1194 if (localContext.tree.getTag() == LAMBDA) {
1195 ((LambdaTranslationContext)localContext)
1196 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1197 }
1198 localContext = localContext.prev;
1199 }
1200 }
1201 if (context() != null && tree.type.tsym.owner.kind == MTH) {
1202 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1203 captureLocalClassDefs(tree.type.tsym, lambdaContext);
1204 }
1205 super.visitNewClass(tree);
1206 }
1207 //where
1208 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1209 JCClassDecl localCDef = localClassDefs.get(csym);
1210 if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) {
1211 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1212 @Override
1213 void addFreeVars(ClassSymbol c) {
1214 captureLocalClassDefs(c, lambdaContext);
1215 }
1216 @Override
1217 void visitSymbol(Symbol sym) {
1218 if (sym.kind == VAR &&
1219 sym.owner.kind == MTH &&
1220 ((VarSymbol)sym).getConstValue() == null) {
1221 TranslationContext<?> localContext = context();
1222 while (localContext != null) {
1223 if (localContext.tree.getTag() == LAMBDA) {
1224 JCTree block = capturedDecl(localContext.depth, sym);
1225 if (block == null) break;
1226 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1227 }
1228 localContext = localContext.prev;
1229 }
1230 }
1231 }
1232 };
1233 fvc.scan(localCDef);
1234 }
1235 }
1237 @Override
1238 public void visitReference(JCMemberReference tree) {
1239 scan(tree.getQualifierExpression());
1240 contextMap.put(tree, makeReferenceContext(tree));
1241 }
1243 @Override
1244 public void visitSelect(JCFieldAccess tree) {
1245 if (context() != null && lambdaSelectSymbolFilter(tree.sym)) {
1246 TranslationContext<?> localContext = context();
1247 while (localContext != null) {
1248 if (localContext.tree.hasTag(LAMBDA)) {
1249 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1250 if (clazz == null) break;
1251 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1252 }
1253 localContext = localContext.prev;
1254 }
1255 scan(tree.selected);
1256 } else {
1257 super.visitSelect(tree);
1258 }
1259 }
1261 @Override
1262 public void visitVarDef(JCVariableDecl tree) {
1263 TranslationContext<?> context = context();
1264 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1265 (LambdaTranslationContext)context :
1266 null;
1267 if (ltc != null) {
1268 if (frameStack.head.tree.hasTag(LAMBDA)) {
1269 ltc.addSymbol(tree.sym, LOCAL_VAR);
1270 }
1271 // Check for type variables (including as type arguments).
1272 // If they occur within class nested in a lambda, mark for erasure
1273 Type type = tree.sym.asType();
1274 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1275 ltc.addSymbol(tree.sym, TYPE_VAR);
1276 }
1277 }
1279 List<Frame> prevStack = frameStack;
1280 try {
1281 if (tree.sym.owner.kind == MTH) {
1282 frameStack.head.addLocal(tree.sym);
1283 }
1284 frameStack = frameStack.prepend(new Frame(tree));
1285 super.visitVarDef(tree);
1286 }
1287 finally {
1288 frameStack = prevStack;
1289 }
1290 }
1292 private Name lambdaName() {
1293 return names.lambda.append(names.fromString("" + lambdaCount++));
1294 }
1296 /**
1297 * For a serializable lambda, generate a name which maximizes name
1298 * stability across deserialization.
1299 * @param owner
1300 * @return Name to use for the synthetic lambda method name
1301 */
1302 private Name serializedLambdaName(Symbol owner) {
1303 StringBuilder buf = new StringBuilder();
1304 buf.append(names.lambda);
1305 // Append the name of the method enclosing the lambda.
1306 String methodName = owner.name.toString();
1307 if (methodName.equals("<clinit>"))
1308 methodName = "static";
1309 else if (methodName.equals("<init>"))
1310 methodName = "new";
1311 buf.append(methodName);
1312 buf.append('$');
1313 // Append a hash of the enclosing method signature to differentiate
1314 // overloaded enclosing methods. For lambdas enclosed in lambdas,
1315 // the generated lambda method will not have type yet, but the
1316 // enclosing method's name will have been generated with this same
1317 // method, so it will be unique and never be overloaded.
1318 if (owner.type != null) {
1319 int methTypeHash = methodSig(owner.type).hashCode();
1320 buf.append(Integer.toHexString(methTypeHash));
1321 }
1322 buf.append('$');
1323 // The above appended name components may not be unique, append a
1324 // count based on the above name components.
1325 String temp = buf.toString();
1326 Integer count = serializableLambdaCounts.get(temp);
1327 if (count == null) {
1328 count = 0;
1329 }
1330 buf.append(count++);
1331 serializableLambdaCounts.put(temp, count);
1332 return names.fromString(buf.toString());
1333 }
1335 /**
1336 * Return a valid owner given the current declaration stack
1337 * (required to skip synthetic lambda symbols)
1338 */
1339 private Symbol owner() {
1340 return owner(false);
1341 }
1343 @SuppressWarnings("fallthrough")
1344 private Symbol owner(boolean skipLambda) {
1345 List<Frame> frameStack2 = frameStack;
1346 while (frameStack2.nonEmpty()) {
1347 switch (frameStack2.head.tree.getTag()) {
1348 case VARDEF:
1349 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1350 frameStack2 = frameStack2.tail;
1351 break;
1352 }
1353 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1354 return initSym(cdecl.sym,
1355 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1356 case BLOCK:
1357 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1358 return initSym(cdecl2.sym,
1359 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1360 case CLASSDEF:
1361 return ((JCClassDecl)frameStack2.head.tree).sym;
1362 case METHODDEF:
1363 return ((JCMethodDecl)frameStack2.head.tree).sym;
1364 case LAMBDA:
1365 if (!skipLambda)
1366 return ((LambdaTranslationContext)contextMap
1367 .get(frameStack2.head.tree)).translatedSym;
1368 default:
1369 frameStack2 = frameStack2.tail;
1370 }
1371 }
1372 Assert.error();
1373 return null;
1374 }
1376 private Symbol initSym(ClassSymbol csym, long flags) {
1377 boolean isStatic = (flags & STATIC) != 0;
1378 if (isStatic) {
1379 //static clinits are generated in Gen - so we need to fake them
1380 Symbol clinit = clinits.get(csym);
1381 if (clinit == null) {
1382 clinit = makeSyntheticMethod(STATIC,
1383 names.clinit,
1384 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1385 csym);
1386 clinits.put(csym, clinit);
1387 }
1388 return clinit;
1389 } else {
1390 //get the first constructor and treat it as the instance init sym
1391 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1392 return s;
1393 }
1394 }
1395 Assert.error("init not found");
1396 return null;
1397 }
1399 private JCTree directlyEnclosingLambda() {
1400 if (frameStack.isEmpty()) {
1401 return null;
1402 }
1403 List<Frame> frameStack2 = frameStack;
1404 while (frameStack2.nonEmpty()) {
1405 switch (frameStack2.head.tree.getTag()) {
1406 case CLASSDEF:
1407 case METHODDEF:
1408 return null;
1409 case LAMBDA:
1410 return frameStack2.head.tree;
1411 default:
1412 frameStack2 = frameStack2.tail;
1413 }
1414 }
1415 Assert.error();
1416 return null;
1417 }
1419 private boolean inClassWithinLambda() {
1420 if (frameStack.isEmpty()) {
1421 return false;
1422 }
1423 List<Frame> frameStack2 = frameStack;
1424 boolean classFound = false;
1425 while (frameStack2.nonEmpty()) {
1426 switch (frameStack2.head.tree.getTag()) {
1427 case LAMBDA:
1428 return classFound;
1429 case CLASSDEF:
1430 classFound = true;
1431 frameStack2 = frameStack2.tail;
1432 break;
1433 default:
1434 frameStack2 = frameStack2.tail;
1435 }
1436 }
1437 // No lambda
1438 return false;
1439 }
1441 /**
1442 * Return the declaration corresponding to a symbol in the enclosing
1443 * scope; the depth parameter is used to filter out symbols defined
1444 * in nested scopes (which do not need to undergo capture).
1445 */
1446 private JCTree capturedDecl(int depth, Symbol sym) {
1447 int currentDepth = frameStack.size() - 1;
1448 for (Frame block : frameStack) {
1449 switch (block.tree.getTag()) {
1450 case CLASSDEF:
1451 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1452 if (sym.isMemberOf(clazz, types)) {
1453 return currentDepth > depth ? null : block.tree;
1454 }
1455 break;
1456 case VARDEF:
1457 if (((JCVariableDecl)block.tree).sym == sym &&
1458 sym.owner.kind == MTH) { //only locals are captured
1459 return currentDepth > depth ? null : block.tree;
1460 }
1461 break;
1462 case BLOCK:
1463 case METHODDEF:
1464 case LAMBDA:
1465 if (block.locals != null && block.locals.contains(sym)) {
1466 return currentDepth > depth ? null : block.tree;
1467 }
1468 break;
1469 default:
1470 Assert.error("bad decl kind " + block.tree.getTag());
1471 }
1472 currentDepth--;
1473 }
1474 return null;
1475 }
1477 private TranslationContext<?> context() {
1478 for (Frame frame : frameStack) {
1479 TranslationContext<?> context = contextMap.get(frame.tree);
1480 if (context != null) {
1481 return context;
1482 }
1483 }
1484 return null;
1485 }
1487 /**
1488 * This is used to filter out those identifiers that needs to be adjusted
1489 * when translating away lambda expressions
1490 */
1491 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1492 return (sym.kind == VAR || sym.kind == MTH)
1493 && !sym.isStatic()
1494 && sym.name != names.init;
1495 }
1497 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1498 return (sym.kind == VAR || sym.kind == MTH) &&
1499 !sym.isStatic() &&
1500 (sym.name == names._this ||
1501 sym.name == names._super);
1502 }
1504 /**
1505 * This is used to filter out those new class expressions that need to
1506 * be qualified with an enclosing tree
1507 */
1508 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1509 if (context != null
1510 && tree.encl == null
1511 && tree.def == null
1512 && !tree.type.getEnclosingType().hasTag(NONE)) {
1513 Type encl = tree.type.getEnclosingType();
1514 Type current = context.owner.enclClass().type;
1515 while (!current.hasTag(NONE)) {
1516 if (current.tsym.isSubClass(encl.tsym, types)) {
1517 return true;
1518 }
1519 current = current.getEnclosingType();
1520 }
1521 return false;
1522 } else {
1523 return false;
1524 }
1525 }
1527 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1528 return new LambdaTranslationContext(tree);
1529 }
1531 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1532 return new ReferenceTranslationContext(tree);
1533 }
1535 private class Frame {
1536 final JCTree tree;
1537 List<Symbol> locals;
1539 public Frame(JCTree tree) {
1540 this.tree = tree;
1541 }
1543 void addLocal(Symbol sym) {
1544 if (locals == null) {
1545 locals = List.nil();
1546 }
1547 locals = locals.prepend(sym);
1548 }
1549 }
1551 /**
1552 * This class is used to store important information regarding translation of
1553 * lambda expression/method references (see subclasses).
1554 */
1555 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1557 /** the underlying (untranslated) tree */
1558 T tree;
1560 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1561 Symbol owner;
1563 /** the depth of this lambda expression in the frame stack */
1564 int depth;
1566 /** the enclosing translation context (set for nested lambdas/mref) */
1567 TranslationContext<?> prev;
1569 TranslationContext(T tree) {
1570 this.tree = tree;
1571 this.owner = owner();
1572 this.depth = frameStack.size() - 1;
1573 this.prev = context();
1574 }
1576 /** does this functional expression need to be created using alternate metafactory? */
1577 boolean needsAltMetafactory() {
1578 return (tree.targets.length() > 1 ||
1579 isSerializable());
1580 }
1582 /** does this functional expression require serialization support? */
1583 boolean isSerializable() {
1584 for (Symbol target : tree.targets) {
1585 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1586 return true;
1587 }
1588 }
1589 return false;
1590 }
1591 }
1593 /**
1594 * This class retains all the useful information about a lambda expression;
1595 * the contents of this class are filled by the LambdaAnalyzer visitor,
1596 * and the used by the main translation routines in order to adjust references
1597 * to captured locals/members, etc.
1598 */
1599 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1601 /** variable in the enclosing context to which this lambda is assigned */
1602 Symbol self;
1604 /** map from original to translated lambda parameters */
1605 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1607 /** map from original to translated lambda locals */
1608 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1610 /** map from variables in enclosing scope to translated synthetic parameters */
1611 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1613 /** map from class symbols to translated synthetic parameters (for captured member access) */
1614 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1616 /** map from original to translated lambda locals */
1617 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1619 /** the synthetic symbol for the method hoisting the translated lambda */
1620 Symbol translatedSym;
1622 List<JCVariableDecl> syntheticParams;
1624 LambdaTranslationContext(JCLambda tree) {
1625 super(tree);
1626 Frame frame = frameStack.head;
1627 if (frame.tree.hasTag(VARDEF)) {
1628 self = ((JCVariableDecl)frame.tree).sym;
1629 }
1630 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1631 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1632 }
1634 /**
1635 * Translate a symbol of a given kind into something suitable for the
1636 * synthetic lambda body
1637 */
1638 Symbol translate(String name, final Symbol sym, LambdaSymbolKind skind) {
1639 switch (skind) {
1640 case CAPTURED_THIS:
1641 return sym; // self represented
1642 case TYPE_VAR:
1643 // Just erase the type var
1644 return new VarSymbol(sym.flags(), names.fromString(name),
1645 types.erasure(sym.type), sym.owner);
1646 case CAPTURED_VAR:
1647 return new VarSymbol(SYNTHETIC | FINAL, names.fromString(name), types.erasure(sym.type), translatedSym) {
1648 @Override
1649 public Symbol baseSymbol() {
1650 //keep mapping with original captured symbol
1651 return sym;
1652 }
1653 };
1654 default:
1655 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1656 }
1657 }
1659 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1660 Map<Symbol, Symbol> transMap = null;
1661 String preferredName;
1662 switch (skind) {
1663 case CAPTURED_THIS:
1664 transMap = capturedThis;
1665 preferredName = "encl$" + capturedThis.size();
1666 break;
1667 case CAPTURED_VAR:
1668 transMap = capturedLocals;
1669 preferredName = "cap$" + capturedLocals.size();
1670 break;
1671 case LOCAL_VAR:
1672 transMap = lambdaLocals;
1673 preferredName = sym.name.toString();
1674 break;
1675 case PARAM:
1676 transMap = lambdaParams;
1677 preferredName = sym.name.toString();
1678 break;
1679 case TYPE_VAR:
1680 transMap = typeVars;
1681 preferredName = sym.name.toString();
1682 break;
1683 default: throw new AssertionError();
1684 }
1685 if (!transMap.containsKey(sym)) {
1686 transMap.put(sym, translate(preferredName, sym, skind));
1687 }
1688 }
1690 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1691 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1692 for (LambdaSymbolKind skind : skinds) {
1693 switch (skind) {
1694 case CAPTURED_THIS:
1695 translationMap.putAll(capturedThis);
1696 break;
1697 case CAPTURED_VAR:
1698 translationMap.putAll(capturedLocals);
1699 break;
1700 case LOCAL_VAR:
1701 translationMap.putAll(lambdaLocals);
1702 break;
1703 case PARAM:
1704 translationMap.putAll(lambdaParams);
1705 break;
1706 case TYPE_VAR:
1707 translationMap.putAll(typeVars);
1708 break;
1709 default: throw new AssertionError();
1710 }
1711 }
1712 return translationMap;
1713 }
1715 /**
1716 * The translatedSym is not complete/accurate until the analysis is
1717 * finished. Once the analysis is finished, the translatedSym is
1718 * "completed" -- updated with type information, access modifiers,
1719 * and full parameter list.
1720 */
1721 void complete() {
1722 if (syntheticParams != null) {
1723 return;
1724 }
1725 boolean inInterface = translatedSym.owner.isInterface();
1726 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1727 boolean needInstance = thisReferenced || inInterface;
1729 // If instance access isn't needed, make it static
1730 // Interface methods much be public default methods, otherwise make it private
1731 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
1732 (inInterface? PUBLIC | DEFAULT : PRIVATE);
1734 //compute synthetic params
1735 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1737 // The signature of the method is augmented with the following
1738 // synthetic parameters:
1739 //
1740 // 1) reference to enclosing contexts captured by the lambda expression
1741 // 2) enclosing locals captured by the lambda expression
1742 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1743 params.append(make.VarDef((VarSymbol) thisSym, null));
1744 }
1746 syntheticParams = params.toList();
1748 //prepend synthetic args to translated lambda method signature
1749 translatedSym.type = types.createMethodTypeWithParameters(
1750 generatedLambdaSig(),
1751 TreeInfo.types(syntheticParams));
1752 }
1754 Type generatedLambdaSig() {
1755 return types.erasure(tree.descriptorType);
1756 }
1757 }
1759 /**
1760 * This class retains all the useful information about a method reference;
1761 * the contents of this class are filled by the LambdaAnalyzer visitor,
1762 * and the used by the main translation routines in order to adjust method
1763 * references (i.e. in case a bridge is needed)
1764 */
1765 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1767 final boolean isSuper;
1768 final Symbol bridgeSym;
1770 ReferenceTranslationContext(JCMemberReference tree) {
1771 super(tree);
1772 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1773 this.bridgeSym = needsBridge()
1774 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1775 lambdaName().append(names.fromString("$bridge")), null,
1776 owner.enclClass())
1777 : null;
1778 }
1780 /**
1781 * Get the opcode associated with this method reference
1782 */
1783 int referenceKind() {
1784 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1785 }
1787 boolean needsVarArgsConversion() {
1788 return tree.varargsElement != null;
1789 }
1791 /**
1792 * @return Is this an array operation like clone()
1793 */
1794 boolean isArrayOp() {
1795 return tree.sym.owner == syms.arrayClass;
1796 }
1798 boolean isPrivateConstructor() {
1799 //hack needed to workaround 292 bug (8005122)
1800 //when 292 issue is fixed we should simply remove this
1801 return tree.sym.name == names.init &&
1802 (tree.sym.flags() & PRIVATE) != 0;
1803 }
1805 boolean receiverAccessible() {
1806 //hack needed to workaround 292 bug (7087658)
1807 //when 292 issue is fixed we should remove this and change the backend
1808 //code to always generate a method handle to an accessible method
1809 return tree.ownerAccessible;
1810 }
1812 /**
1813 * Does this reference needs a bridge (i.e. var args need to be
1814 * expanded or "super" is used)
1815 */
1816 final boolean needsBridge() {
1817 return isSuper || needsVarArgsConversion() || isArrayOp() ||
1818 isPrivateConstructor() || !receiverAccessible();
1819 }
1821 Type generatedRefSig() {
1822 return types.erasure(tree.sym.type);
1823 }
1825 Type bridgedRefSig() {
1826 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1827 }
1828 }
1829 }
1830 // </editor-fold>
1832 enum LambdaSymbolKind {
1833 CAPTURED_VAR,
1834 CAPTURED_THIS,
1835 LOCAL_VAR,
1836 PARAM,
1837 TYPE_VAR;
1838 }
1840 /**
1841 * ****************************************************************
1842 * Signature Generation
1843 * ****************************************************************
1844 */
1846 private String methodSig(Type type) {
1847 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1848 sg.assembleSig(type);
1849 return sg.toString();
1850 }
1852 private String classSig(Type type) {
1853 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1854 sg.assembleClassSig(type);
1855 return sg.toString();
1856 }
1858 /**
1859 * Signature Generation
1860 */
1861 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1863 /**
1864 * An output buffer for type signatures.
1865 */
1866 StringBuilder sb = new StringBuilder();
1868 L2MSignatureGenerator() {
1869 super(types);
1870 }
1872 @Override
1873 protected void append(char ch) {
1874 sb.append(ch);
1875 }
1877 @Override
1878 protected void append(byte[] ba) {
1879 sb.append(new String(ba));
1880 }
1882 @Override
1883 protected void append(Name name) {
1884 sb.append(name.toString());
1885 }
1887 @Override
1888 public String toString() {
1889 return sb.toString();
1890 }
1891 }
1892 }