Mon, 18 Feb 2013 14:33:25 +0000
6563143: javac should issue a warning for overriding equals without hashCode
Reviewed-by: jjg, mcimadamore
1 /*
2 * Copyright (c) 2010, 2012, 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.jvm.*;
48 import com.sun.tools.javac.util.*;
49 import com.sun.tools.javac.util.List;
50 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
51 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
53 import java.util.HashMap;
54 import java.util.LinkedHashMap;
55 import java.util.Map;
57 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
58 import static com.sun.tools.javac.code.Flags.*;
59 import static com.sun.tools.javac.code.Kinds.*;
60 import static com.sun.tools.javac.code.TypeTag.*;
61 import static com.sun.tools.javac.tree.JCTree.Tag.*;
63 /**
64 * This pass desugars lambda expressions into static methods
65 *
66 * <p><b>This is NOT part of any supported API.
67 * If you write code that depends on this, you do so at your own risk.
68 * This code and its internal interfaces are subject to change or
69 * deletion without notice.</b>
70 */
71 public class LambdaToMethod extends TreeTranslator {
73 private Names names;
74 private Symtab syms;
75 private Resolve rs;
76 private TreeMaker make;
77 private Types types;
78 private TransTypes transTypes;
79 private Env<AttrContext> attrEnv;
81 /** the analyzer scanner */
82 private LambdaAnalyzer analyzer;
84 /** map from lambda trees to translation contexts */
85 private Map<JCTree, TranslationContext<?>> contextMap;
87 /** current translation context (visitor argument) */
88 private TranslationContext<?> context;
90 /** info about the current class being processed */
91 private KlassInfo kInfo;
93 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
94 public static final int FLAG_SERIALIZABLE = 1 << 0;
96 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
97 public static final int FLAG_MARKERS = 1 << 1;
99 private class KlassInfo {
101 /**
102 * list of methods to append
103 */
104 private ListBuffer<JCTree> appendedMethodList;
106 /**
107 * list of deserialization cases
108 */
109 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
111 /**
112 * deserialize method symbol
113 */
114 private final MethodSymbol deserMethodSym;
116 /**
117 * deserialize method parameter symbol
118 */
119 private final VarSymbol deserParamSym;
121 private KlassInfo(Symbol kSym) {
122 appendedMethodList = ListBuffer.lb();
123 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
124 long flags = PRIVATE | STATIC | SYNTHETIC;
125 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
126 List.<Type>nil(), syms.methodClass);
127 deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
128 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), syms.serializedLambdaType, deserMethodSym);
129 }
131 private void addMethod(JCTree decl) {
132 appendedMethodList = appendedMethodList.prepend(decl);
133 }
134 }
136 // <editor-fold defaultstate="collapsed" desc="Instantiating">
137 private static final Context.Key<LambdaToMethod> unlambdaKey =
138 new Context.Key<LambdaToMethod>();
140 public static LambdaToMethod instance(Context context) {
141 LambdaToMethod instance = context.get(unlambdaKey);
142 if (instance == null) {
143 instance = new LambdaToMethod(context);
144 }
145 return instance;
146 }
148 private LambdaToMethod(Context context) {
149 names = Names.instance(context);
150 syms = Symtab.instance(context);
151 rs = Resolve.instance(context);
152 make = TreeMaker.instance(context);
153 types = Types.instance(context);
154 transTypes = TransTypes.instance(context);
155 analyzer = new LambdaAnalyzer();
156 }
157 // </editor-fold>
159 // <editor-fold defaultstate="collapsed" desc="translate methods">
160 @Override
161 public <T extends JCTree> T translate(T tree) {
162 TranslationContext<?> newContext = contextMap.get(tree);
163 return translate(tree, newContext != null ? newContext : context);
164 }
166 public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
167 TranslationContext<?> prevContext = context;
168 try {
169 context = newContext;
170 return super.translate(tree);
171 }
172 finally {
173 context = prevContext;
174 }
175 }
177 public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
178 ListBuffer<T> buf = ListBuffer.lb();
179 for (T tree : trees) {
180 buf.append(translate(tree, newContext));
181 }
182 return buf.toList();
183 }
185 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
186 this.make = make;
187 this.attrEnv = env;
188 this.context = null;
189 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
190 return translate(cdef);
191 }
192 // </editor-fold>
194 // <editor-fold defaultstate="collapsed" desc="visitor methods">
195 /**
196 * Visit a class.
197 * Maintain the translatedMethodList across nested classes.
198 * Append the translatedMethodList to the class after it is translated.
199 * @param tree
200 */
201 @Override
202 public void visitClassDef(JCClassDecl tree) {
203 if (tree.sym.owner.kind == PCK) {
204 //analyze class
205 analyzer.analyzeClass(tree);
206 }
207 KlassInfo prevKlassInfo = kInfo;
208 try {
209 kInfo = new KlassInfo(tree.sym);
210 super.visitClassDef(tree);
211 if (!kInfo.deserializeCases.isEmpty()) {
212 kInfo.addMethod(makeDeserializeMethod(tree.sym));
213 }
214 //add all translated instance methods here
215 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
216 tree.defs = tree.defs.appendList(newMethods);
217 for (JCTree lambda : newMethods) {
218 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
219 }
220 result = tree;
221 } finally {
222 kInfo = prevKlassInfo;
223 }
224 }
226 /**
227 * Translate a lambda into a method to be inserted into the class.
228 * Then replace the lambda site with an invokedynamic call of to lambda
229 * meta-factory, which will use the lambda method.
230 * @param tree
231 */
232 @Override
233 public void visitLambda(JCLambda tree) {
234 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
235 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
236 MethodType lambdaType = (MethodType) sym.type;
238 //create the method declaration hoisting the lambda body
239 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
240 sym.name,
241 make.QualIdent(lambdaType.getReturnType().tsym),
242 List.<JCTypeParameter>nil(),
243 localContext.syntheticParams,
244 lambdaType.getThrownTypes() == null ?
245 List.<JCExpression>nil() :
246 make.Types(lambdaType.getThrownTypes()),
247 null,
248 null);
249 lambdaDecl.sym = sym;
250 lambdaDecl.type = lambdaType;
252 //translate lambda body
253 //As the lambda body is translated, all references to lambda locals,
254 //captured variables, enclosing members are adjusted accordingly
255 //to refer to the static method parameters (rather than i.e. acessing to
256 //captured members directly).
257 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
259 //Add the method to the list of methods to be added to this class.
260 kInfo.addMethod(lambdaDecl);
262 //now that we have generated a method for the lambda expression,
263 //we can translate the lambda into a method reference pointing to the newly
264 //created method.
265 //
266 //Note that we need to adjust the method handle so that it will match the
267 //signature of the SAM descriptor - this means that the method reference
268 //should be added the following synthetic arguments:
269 //
270 // * the "this" argument if it is an instance method
271 // * enclosing locals captured by the lambda expression
273 ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
275 if (!sym.isStatic()) {
276 syntheticInits.append(makeThis(
277 sym.owner.enclClass().asType(),
278 localContext.owner.enclClass()));
279 }
281 //add captured locals
282 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
283 if (fv != localContext.self) {
284 JCTree captured_local = make.Ident(fv).setType(fv.type);
285 syntheticInits.append((JCExpression) captured_local);
286 }
287 }
289 //then, determine the arguments to the indy call
290 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
292 //build a sam instance using an indy call to the meta-factory
293 int refKind = referenceKind(sym);
295 //convert to an invokedynamic call
296 result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
297 }
299 private JCIdent makeThis(Type type, Symbol owner) {
300 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
301 names._this,
302 type,
303 owner);
304 return make.Ident(_this);
305 }
307 /**
308 * Translate a method reference into an invokedynamic call to the
309 * meta-factory.
310 * @param tree
311 */
312 @Override
313 public void visitReference(JCMemberReference tree) {
314 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
316 //first determine the method symbol to be used to generate the sam instance
317 //this is either the method reference symbol, or the bridged reference symbol
318 Symbol refSym = localContext.needsBridge() ?
319 localContext.bridgeSym :
320 tree.sym;
322 //build the bridge method, if needed
323 if (localContext.needsBridge()) {
324 bridgeMemberReference(tree, localContext);
325 }
327 //the qualifying expression is treated as a special captured arg
328 JCExpression init;
329 switch(tree.kind) {
331 case IMPLICIT_INNER: /** Inner :: new */
332 case SUPER: /** super :: instMethod */
333 init = makeThis(
334 localContext.owner.enclClass().asType(),
335 localContext.owner.enclClass());
336 break;
338 case BOUND: /** Expr :: instMethod */
339 init = tree.getQualifierExpression();
340 break;
342 case UNBOUND: /** Type :: instMethod */
343 case STATIC: /** Type :: staticMethod */
344 case TOPLEVEL: /** Top level :: new */
345 case ARRAY_CTOR: /** ArrayType :: new */
346 init = null;
347 break;
349 default:
350 throw new InternalError("Should not have an invalid kind");
351 }
353 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
356 //build a sam instance using an indy call to the meta-factory
357 result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
358 }
360 /**
361 * Translate identifiers within a lambda to the mapped identifier
362 * @param tree
363 */
364 @Override
365 public void visitIdent(JCIdent tree) {
366 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
367 super.visitIdent(tree);
368 } else {
369 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
370 if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
371 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
372 result = make.Ident(translatedSym).setType(tree.type);
373 } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
374 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
375 result = make.Ident(translatedSym).setType(tree.type);
376 } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
377 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
378 result = make.Ident(translatedSym).setType(translatedSym.type);
379 } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
380 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
381 result = make.Ident(translatedSym).setType(tree.type);
382 } else {
383 if (tree.sym.owner.kind == Kinds.TYP) {
384 for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
385 if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
386 JCExpression enclRef = make.Ident(encl_entry.getValue());
387 result = tree.sym.name == names._this
388 ? enclRef.setType(tree.type)
389 : make.Select(enclRef, tree.sym).setType(tree.type);
390 result = tree;
391 return;
392 }
393 }
394 }
395 //access to untranslated symbols (i.e. compile-time constants,
396 //members defined inside the lambda body, etc.) )
397 super.visitIdent(tree);
398 }
399 }
400 }
402 @Override
403 public void visitVarDef(JCVariableDecl tree) {
404 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
405 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
406 JCExpression init = translate(tree.init);
407 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
408 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
409 JCExpression init = translate(tree.init);
410 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
411 result = make.VarDef(xsym, init);
412 // Replace the entered symbol for this variable
413 Scope sc = tree.sym.owner.members();
414 if (sc != null) {
415 sc.remove(tree.sym);
416 sc.enter(xsym);
417 }
418 } else {
419 super.visitVarDef(tree);
420 }
421 }
423 // </editor-fold>
425 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
427 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
428 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
429 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
430 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
431 }
433 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
434 Type restype = lambdaMethodDecl.type.getReturnType();
435 boolean isLambda_void = expr.type.hasTag(VOID);
436 boolean isTarget_void = restype.hasTag(VOID);
437 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
438 if (isTarget_void) {
439 //target is void:
440 // BODY;
441 JCStatement stat = make.Exec(expr);
442 return make.Block(0, List.<JCStatement>of(stat));
443 } else if (isLambda_void && isTarget_Void) {
444 //void to Void conversion:
445 // BODY; return null;
446 ListBuffer<JCStatement> stats = ListBuffer.lb();
447 stats.append(make.Exec(expr));
448 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
449 return make.Block(0, stats.toList());
450 } else {
451 //non-void to non-void conversion:
452 // return (TYPE)BODY;
453 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
454 return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
455 }
456 }
458 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
459 final Type restype = lambdaMethodDecl.type.getReturnType();
460 final boolean isTarget_void = restype.hasTag(VOID);
461 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
463 class LambdaBodyTranslator extends TreeTranslator {
465 @Override
466 public void visitClassDef(JCClassDecl tree) {
467 //do NOT recurse on any inner classes
468 result = tree;
469 }
471 @Override
472 public void visitLambda(JCLambda tree) {
473 //do NOT recurse on any nested lambdas
474 result = tree;
475 }
477 @Override
478 public void visitReturn(JCReturn tree) {
479 boolean isLambda_void = tree.expr == null;
480 if (isTarget_void && !isLambda_void) {
481 //Void to void conversion:
482 // { TYPE $loc = RET-EXPR; return; }
483 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
484 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
485 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
486 } else if (!isTarget_void || !isLambda_void) {
487 //non-void to non-void conversion:
488 // return (TYPE)RET-EXPR;
489 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
490 result = tree;
491 } else {
492 result = tree;
493 }
495 }
496 }
498 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
499 if (completeNormally && isTarget_Void) {
500 //there's no return statement and the lambda (possibly inferred)
501 //return type is java.lang.Void; emit a synthetic return statement
502 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
503 }
504 return trans_block;
505 }
507 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
508 ListBuffer<JCCase> cases = ListBuffer.lb();
509 ListBuffer<JCBreak> breaks = ListBuffer.lb();
510 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
511 JCBreak br = make.Break(null);
512 breaks.add(br);
513 List<JCStatement> stmts = entry.getValue().append(br).toList();
514 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
515 }
516 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
517 for (JCBreak br : breaks) {
518 br.target = sw;
519 }
520 JCBlock body = make.Block(0L, List.<JCStatement>of(
521 sw,
522 make.Throw(makeNewClass(
523 syms.illegalArgumentExceptionType,
524 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
525 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
526 names.deserializeLambda,
527 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
528 List.<JCTypeParameter>nil(),
529 List.of(make.VarDef(kInfo.deserParamSym, null)),
530 List.<JCExpression>nil(),
531 body,
532 null);
533 deser.sym = kInfo.deserMethodSym;
534 deser.type = kInfo.deserMethodSym.type;
535 //System.err.printf("DESER: '%s'\n", deser);
536 return deser;
537 }
539 /** Make an attributed class instance creation expression.
540 * @param ctype The class type.
541 * @param args The constructor arguments.
542 */
543 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
544 JCNewClass tree = make.NewClass(null,
545 null, make.QualIdent(ctype.tsym), args, null);
546 tree.constructor = rs.resolveConstructor(
547 null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
548 tree.type = ctype;
549 return tree;
550 }
552 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
553 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
554 String functionalInterfaceClass = classSig(targetType);
555 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
556 String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
557 String implClass = classSig(refSym.owner.type);
558 String implMethodName = refSym.getQualifiedName().toString();
559 String implMethodSignature = methodSig(types.erasure(refSym.type));
561 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
562 ListBuffer<JCExpression> serArgs = ListBuffer.lb();
563 int i = 0;
564 for (Type t : indyType.getParameterTypes()) {
565 List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
566 List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
567 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
568 ++i;
569 }
570 JCStatement stmt = make.If(
571 deserTest(deserTest(deserTest(deserTest(deserTest(
572 kindTest,
573 "getFunctionalInterfaceClass", functionalInterfaceClass),
574 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
575 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
576 "getImplClass", implClass),
577 "getImplMethodSignature", implMethodSignature),
578 make.Return(makeIndyCall(
579 pos,
580 syms.lambdaMetafactory,
581 names.altMetaFactory,
582 staticArgs, indyType, serArgs.toList())),
583 null);
584 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
585 if (stmts == null) {
586 stmts = ListBuffer.lb();
587 kInfo.deserializeCases.put(implMethodName, stmts);
588 }
589 /****
590 System.err.printf("+++++++++++++++++\n");
591 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
592 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
593 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
594 System.err.printf("*implMethodKind: %d\n", implMethodKind);
595 System.err.printf("*implClass: '%s'\n", implClass);
596 System.err.printf("*implMethodName: '%s'\n", implMethodName);
597 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
598 ****/
599 stmts.append(stmt);
600 }
602 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
603 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
604 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
605 testExpr.setType(syms.booleanType);
606 return testExpr;
607 }
609 private JCExpression deserTest(JCExpression prev, String func, String lit) {
610 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
611 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
612 JCMethodInvocation eqtest = make.Apply(
613 List.<JCExpression>nil(),
614 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
615 List.<JCExpression>of(make.Literal(lit)));
616 eqtest.setType(syms.booleanType);
617 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
618 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
619 compound.setType(syms.booleanType);
620 return compound;
621 }
623 private JCExpression deserGetter(String func, Type type) {
624 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
625 }
627 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
628 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
629 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
630 return make.Apply(
631 List.<JCExpression>nil(),
632 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
633 args).setType(type);
634 }
636 /**
637 * Create new synthetic method with given flags, name, type, owner
638 */
639 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
640 return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
641 }
643 /**
644 * Create new synthetic variable with given flags, name, type, owner
645 */
646 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
647 return makeSyntheticVar(flags, names.fromString(name), type, owner);
648 }
650 /**
651 * Create new synthetic variable with given flags, name, type, owner
652 */
653 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
654 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
655 }
657 /**
658 * Set varargsElement field on a given tree (must be either a new class tree
659 * or a method call tree)
660 */
661 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
662 if (varargsElement != null) {
663 switch (tree.getTag()) {
664 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
665 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
666 default: throw new AssertionError();
667 }
668 }
669 }
671 /**
672 * Convert method/constructor arguments by inserting appropriate cast
673 * as required by type-erasure - this is needed when bridging a lambda/method
674 * reference, as the bridged signature might require downcast to be compatible
675 * with the generated signature.
676 */
677 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
678 Assert.check(meth.kind == Kinds.MTH);
679 List<Type> formals = types.erasure(meth.type).getParameterTypes();
680 if (varargsElement != null) {
681 Assert.check((meth.flags() & VARARGS) != 0);
682 }
683 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
684 }
686 // </editor-fold>
688 /**
689 * Generate an adapter method "bridge" for a method reference which cannot
690 * be used directly.
691 */
692 private class MemberReferenceBridger {
694 private final JCMemberReference tree;
695 private final ReferenceTranslationContext localContext;
696 private final ListBuffer<JCExpression> args = ListBuffer.lb();
697 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
699 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
700 this.tree = tree;
701 this.localContext = localContext;
702 }
704 /**
705 * Generate the bridge
706 */
707 JCMethodDecl bridge() {
708 int prevPos = make.pos;
709 try {
710 make.at(tree);
711 Type samDesc = localContext.bridgedRefSig();
712 List<Type> samPTypes = samDesc.getParameterTypes();
714 //an extra argument is prepended to the signature of the bridge in case
715 //the member reference is an instance method reference (in which case
716 //the receiver expression is passed to the bridge itself).
717 Type recType = null;
718 switch (tree.kind) {
719 case IMPLICIT_INNER:
720 recType = tree.sym.owner.type.getEnclosingType();
721 break;
722 case BOUND:
723 recType = tree.getQualifierExpression().type;
724 break;
725 case UNBOUND:
726 recType = samPTypes.head;
727 samPTypes = samPTypes.tail;
728 break;
729 }
731 //generate the parameter list for the bridged member reference - the
732 //bridge signature will match the signature of the target sam descriptor
734 VarSymbol rcvr = (recType == null)
735 ? null
736 : addParameter("rec$", recType, false);
738 List<Type> refPTypes = tree.sym.type.getParameterTypes();
739 int refSize = refPTypes.size();
740 int samSize = samPTypes.size();
741 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; // Last parameter to copy from referenced method
743 List<Type> l = refPTypes;
744 // Use parameter types of the referenced method, excluding final var args
745 for (int i = 0; l.nonEmpty() && i < last; ++i) {
746 addParameter("x$" + i, l.head, true);
747 l = l.tail;
748 }
749 // Flatten out the var args
750 for (int i = last; i < samSize; ++i) {
751 addParameter("xva$" + i, tree.varargsElement, true);
752 }
754 //generate the bridge method declaration
755 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
756 localContext.bridgeSym.name,
757 make.QualIdent(samDesc.getReturnType().tsym),
758 List.<JCTypeParameter>nil(),
759 params.toList(),
760 tree.sym.type.getThrownTypes() == null
761 ? List.<JCExpression>nil()
762 : make.Types(tree.sym.type.getThrownTypes()),
763 null,
764 null);
765 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
766 bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
768 //bridge method body generation - this can be either a method call or a
769 //new instance creation expression, depending on the member reference kind
770 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
771 ? bridgeExpressionInvoke(rcvr)
772 : bridgeExpressionNew();
774 //the body is either a return expression containing a method call,
775 //or the method call itself, depending on whether the return type of
776 //the bridge is non-void/void.
777 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
779 return bridgeDecl;
780 } finally {
781 make.at(prevPos);
782 }
783 }
785 /**
786 * determine the receiver of the bridged method call - the receiver can
787 * be either the synthetic receiver parameter or a type qualifier; the
788 * original qualifier expression is never used here, as it might refer
789 * to symbols not available in the static context of the bridge
790 */
791 private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) {
792 JCExpression qualifier =
793 tree.sym.isStatic() ?
794 make.Type(tree.sym.owner.type) :
795 (rcvr != null) ?
796 make.Ident(rcvr) :
797 tree.getQualifierExpression();
799 //create the qualifier expression
800 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
801 select.sym = tree.sym;
802 select.type = tree.sym.erasure(types);
804 //create the method call expression
805 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
806 convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType());
808 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
809 setVarargsIfNeeded(apply, tree.varargsElement);
810 return apply;
811 }
813 /**
814 * the enclosing expression is either 'null' (no enclosing type) or set
815 * to the first bridge synthetic parameter
816 */
817 private JCExpression bridgeExpressionNew() {
818 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
819 //create the array creation expression
820 JCNewArray newArr = make.NewArray(make.Type(types.elemtype(tree.getQualifierExpression().type)),
821 List.of(make.Ident(params.first())),
822 null);
823 newArr.type = tree.getQualifierExpression().type;
824 return newArr;
825 } else {
826 JCExpression encl = null;
827 switch (tree.kind) {
828 case UNBOUND:
829 case IMPLICIT_INNER:
830 encl = make.Ident(params.first());
831 }
833 //create the instance creation expression
834 JCNewClass newClass = make.NewClass(encl,
835 List.<JCExpression>nil(),
836 make.Type(tree.getQualifierExpression().type),
837 convertArgs(tree.sym, args.toList(), tree.varargsElement),
838 null);
839 newClass.constructor = tree.sym;
840 newClass.constructorType = tree.sym.erasure(types);
841 newClass.type = tree.getQualifierExpression().type;
842 setVarargsIfNeeded(newClass, tree.varargsElement);
843 return newClass;
844 }
845 }
847 private VarSymbol addParameter(String name, Type p, boolean genArg) {
848 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
849 params.append(make.VarDef(vsym, null));
850 if (genArg) {
851 args.append(make.Ident(vsym));
852 }
853 return vsym;
854 }
855 }
857 /**
858 * Bridges a member reference - this is needed when:
859 * * Var args in the referenced method need to be flattened away
860 * * super is used
861 */
862 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
863 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
864 }
866 /**
867 * Generate an indy method call to the meta factory
868 */
869 private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
870 boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
871 //determine the static bsm args
872 Type mtype = types.erasure(tree.descriptorType);
873 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
874 List<Object> staticArgs = List.<Object>of(
875 new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(tree.type.tsym), types),
876 new Pool.MethodHandle(refKind, refSym, types),
877 new MethodType(mtype.getParameterTypes(),
878 mtype.getReturnType(),
879 mtype.getThrownTypes(),
880 syms.methodClass));
882 //computed indy arg types
883 ListBuffer<Type> indy_args_types = ListBuffer.lb();
884 for (JCExpression arg : indy_args) {
885 indy_args_types.append(arg.type);
886 }
888 //finally, compute the type of the indy call
889 MethodType indyType = new MethodType(indy_args_types.toList(),
890 tree.type,
891 List.<Type>nil(),
892 syms.methodClass);
894 Name metafactoryName = needsAltMetafactory ?
895 names.altMetaFactory : names.metaFactory;
897 if (needsAltMetafactory) {
898 ListBuffer<Object> markers = ListBuffer.lb();
899 for (Symbol t : tree.targets.tail) {
900 if (t != syms.serializableType.tsym) {
901 markers.append(t);
902 }
903 }
904 int flags = isSerializable? FLAG_SERIALIZABLE : 0;
905 boolean hasMarkers = markers.nonEmpty();
906 flags |= hasMarkers ? FLAG_MARKERS : 0;
907 staticArgs = staticArgs.append(flags);
908 if (hasMarkers) {
909 staticArgs = staticArgs.append(markers.length());
910 staticArgs = staticArgs.appendList(markers.toList());
911 }
912 if (isSerializable) {
913 addDeserializationCase(refKind, refSym, tree.type, samSym,
914 tree, staticArgs, indyType);
915 }
916 }
918 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
919 }
921 /**
922 * Generate an indy method call with given name, type and static bootstrap
923 * arguments types
924 */
925 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
926 int prevPos = make.pos;
927 try {
928 make.at(pos);
929 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
930 syms.stringType,
931 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
933 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
934 bsmName, bsm_staticArgs, List.<Type>nil());
936 DynamicMethodSymbol dynSym =
937 new DynamicMethodSymbol(names.lambda,
938 syms.noSymbol,
939 bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual,
940 (MethodSymbol)bsm,
941 indyType,
942 staticArgs.toArray());
944 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
945 qualifier.sym = dynSym;
946 qualifier.type = indyType.getReturnType();
948 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
949 proxyCall.type = indyType.getReturnType();
950 return proxyCall;
951 } finally {
952 make.at(prevPos);
953 }
954 }
955 //where
956 private List<Type> bsmStaticArgToTypes(List<Object> args) {
957 ListBuffer<Type> argtypes = ListBuffer.lb();
958 for (Object arg : args) {
959 argtypes.append(bsmStaticArgToType(arg));
960 }
961 return argtypes.toList();
962 }
964 private Type bsmStaticArgToType(Object arg) {
965 Assert.checkNonNull(arg);
966 if (arg instanceof ClassSymbol) {
967 return syms.classType;
968 } else if (arg instanceof Integer) {
969 return syms.intType;
970 } else if (arg instanceof Long) {
971 return syms.longType;
972 } else if (arg instanceof Float) {
973 return syms.floatType;
974 } else if (arg instanceof Double) {
975 return syms.doubleType;
976 } else if (arg instanceof String) {
977 return syms.stringType;
978 } else if (arg instanceof Pool.MethodHandle) {
979 return syms.methodHandleType;
980 } else if (arg instanceof MethodType) {
981 return syms.methodTypeType;
982 } else {
983 Assert.error("bad static arg " + arg.getClass());
984 return null;
985 }
986 }
988 /**
989 * Get the opcode associated with this method reference
990 */
991 private int referenceKind(Symbol refSym) {
992 if (refSym.isConstructor()) {
993 return ClassFile.REF_newInvokeSpecial;
994 } else {
995 if (refSym.isStatic()) {
996 return ClassFile.REF_invokeStatic;
997 } else if (refSym.enclClass().isInterface()) {
998 return ClassFile.REF_invokeInterface;
999 } else {
1000 return ClassFile.REF_invokeVirtual;
1001 }
1002 }
1003 }
1005 // </editor-fold>
1007 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
1008 /**
1009 * This visitor collects information about translation of a lambda expression.
1010 * More specifically, it keeps track of the enclosing contexts and captured locals
1011 * accessed by the lambda being translated (as well as other useful info).
1012 */
1013 class LambdaAnalyzer extends TreeScanner {
1015 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1016 private List<Frame> frameStack;
1018 /**
1019 * keep the count of lambda expression (used to generate unambiguous
1020 * names)
1021 */
1022 private int lambdaCount = 0;
1024 /**
1025 * keep the count of lambda expression defined in given context (used to
1026 * generate unambiguous names for serializable lambdas)
1027 */
1028 private Map<String, Integer> serializableLambdaCounts =
1029 new HashMap<String, Integer>();
1031 /**
1032 * maps for fake clinit symbols to be used as owners of lambda occurring in
1033 * a static var init context
1034 */
1035 private Map<ClassSymbol, Symbol> clinits =
1036 new HashMap<ClassSymbol, Symbol>();
1038 private void analyzeClass(JCClassDecl tree) {
1039 frameStack = List.nil();
1040 scan(tree);
1041 }
1043 @Override
1044 public void visitBlock(JCBlock tree) {
1045 List<Frame> prevStack = frameStack;
1046 try {
1047 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1048 frameStack = frameStack.prepend(new Frame(tree));
1049 }
1050 super.visitBlock(tree);
1051 }
1052 finally {
1053 frameStack = prevStack;
1054 }
1055 }
1057 @Override
1058 public void visitClassDef(JCClassDecl tree) {
1059 List<Frame> prevStack = frameStack;
1060 Map<String, Integer> prevSerializableLambdaCount = serializableLambdaCounts;
1061 Map<ClassSymbol, Symbol> prevClinits = clinits;
1062 try {
1063 serializableLambdaCounts = new HashMap<String, Integer>();
1064 prevClinits = new HashMap<ClassSymbol, Symbol>();
1065 if (directlyEnclosingLambda() != null) {
1066 tree.sym.owner = owner();
1067 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) contextMap.get(directlyEnclosingLambda());
1068 Type encl = lambdaContext.enclosingType();
1069 if (encl.hasTag(NONE)) {
1070 //if the translated lambda body occurs in a static context,
1071 //any class declaration within it must be made static
1072 //@@@TODO: What about nested classes within lambda?
1073 tree.sym.flags_field |= STATIC;
1074 ((ClassType) tree.sym.type).setEnclosingType(Type.noType);
1075 } else {
1076 //if the translated lambda body is in an instance context
1077 //the enclosing type of any class declaration within it
1078 //must be updated to point to the new enclosing type (if any)
1079 ((ClassType) tree.sym.type).setEnclosingType(encl);
1080 }
1081 }
1082 frameStack = frameStack.prepend(new Frame(tree));
1083 super.visitClassDef(tree);
1084 }
1085 finally {
1086 frameStack = prevStack;
1087 serializableLambdaCounts = prevSerializableLambdaCount;
1088 clinits = prevClinits;
1089 }
1090 if (!tree.sym.isStatic() && directlyEnclosingLambda() != null) {
1091 // Any (non-static) class defined within a lambda is an implicit 'this' reference
1092 // because its constructor will reference the enclosing class
1093 ((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1094 }
1095 }
1097 @Override
1098 public void visitIdent(JCIdent tree) {
1099 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1100 if (tree.sym.kind == VAR &&
1101 tree.sym.owner.kind == MTH &&
1102 tree.type.constValue() == null) {
1103 TranslationContext<?> localContext = context();
1104 while (localContext != null) {
1105 if (localContext.tree.getTag() == LAMBDA) {
1106 JCTree block = capturedDecl(localContext.depth, tree.sym);
1107 if (block == null) break;
1108 ((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR);
1109 }
1110 localContext = localContext.prev;
1111 }
1112 } else if (tree.sym.owner.kind == TYP) {
1113 TranslationContext<?> localContext = context();
1114 while (localContext != null) {
1115 if (localContext.tree.hasTag(LAMBDA)) {
1116 JCTree block = capturedDecl(localContext.depth, tree.sym);
1117 if (block == null) break;
1118 switch (block.getTag()) {
1119 case CLASSDEF:
1120 JCClassDecl cdecl = (JCClassDecl)block;
1121 ((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS);
1122 break;
1123 default:
1124 Assert.error("bad block kind");
1125 }
1126 }
1127 localContext = localContext.prev;
1128 }
1129 }
1130 }
1131 super.visitIdent(tree);
1132 }
1134 @Override
1135 public void visitLambda(JCLambda tree) {
1136 List<Frame> prevStack = frameStack;
1137 try {
1138 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1139 frameStack = frameStack.prepend(new Frame(tree));
1140 for (JCVariableDecl param : tree.params) {
1141 context.addSymbol(param.sym, PARAM);
1142 frameStack.head.addLocal(param.sym);
1143 }
1144 contextMap.put(tree, context);
1145 scan(tree.body);
1146 context.complete();
1147 }
1148 finally {
1149 frameStack = prevStack;
1150 }
1151 }
1153 @Override
1154 public void visitMethodDef(JCMethodDecl tree) {
1155 List<Frame> prevStack = frameStack;
1156 try {
1157 frameStack = frameStack.prepend(new Frame(tree));
1158 super.visitMethodDef(tree);
1159 }
1160 finally {
1161 frameStack = prevStack;
1162 }
1163 }
1165 @Override
1166 public void visitNewClass(JCNewClass tree) {
1167 if (lambdaNewClassFilter(context(), tree)) {
1168 ((LambdaTranslationContext) context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1169 }
1170 super.visitNewClass(tree);
1171 }
1173 @Override
1174 public void visitReference(JCMemberReference tree) {
1175 scan(tree.getQualifierExpression());
1176 contextMap.put(tree, makeReferenceContext(tree));
1177 }
1179 @Override
1180 public void visitSelect(JCFieldAccess tree) {
1181 if (context() != null && lambdaSelectSymbolFilter(tree.sym)) {
1182 TranslationContext<?> localContext = context();
1183 while (localContext != null) {
1184 if (localContext.tree.hasTag(LAMBDA)) {
1185 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1186 if (clazz == null) break;
1187 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1188 }
1189 localContext = localContext.prev;
1190 }
1191 scan(tree.selected);
1192 } else {
1193 super.visitSelect(tree);
1194 }
1195 }
1197 @Override
1198 public void visitVarDef(JCVariableDecl tree) {
1199 TranslationContext<?> context = context();
1200 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1201 (LambdaTranslationContext)context :
1202 null;
1203 if (ltc != null) {
1204 if (frameStack.head.tree.hasTag(LAMBDA)) {
1205 ltc.addSymbol(tree.sym, LOCAL_VAR);
1206 }
1207 // Check for type variables (including as type arguments).
1208 // If they occur within class nested in a lambda, mark for erasure
1209 Type type = tree.sym.asType();
1210 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1211 ltc.addSymbol(tree.sym, TYPE_VAR);
1212 }
1213 }
1215 List<Frame> prevStack = frameStack;
1216 try {
1217 if (tree.sym.owner.kind == MTH) {
1218 frameStack.head.addLocal(tree.sym);
1219 }
1220 frameStack = frameStack.prepend(new Frame(tree));
1221 super.visitVarDef(tree);
1222 }
1223 finally {
1224 frameStack = prevStack;
1225 }
1226 }
1228 private Name lambdaName() {
1229 return names.lambda.append(names.fromString("" + lambdaCount++));
1230 }
1232 private Name serializedLambdaName(Symbol owner) {
1233 StringBuilder buf = new StringBuilder();
1234 buf.append(names.lambda);
1235 buf.append(owner.name);
1236 buf.append('$');
1237 int methTypeHash = methodSig(owner.type).hashCode();
1238 buf.append(methTypeHash);
1239 buf.append('$');
1240 String temp = buf.toString();
1241 Integer count = serializableLambdaCounts.get(temp);
1242 if (count == null) {
1243 count = 0;
1244 }
1245 buf.append(count++);
1246 serializableLambdaCounts.put(temp, count);
1247 return names.fromString(buf.toString());
1248 }
1250 /**
1251 * Return a valid owner given the current declaration stack
1252 * (required to skip synthetic lambda symbols)
1253 */
1254 private Symbol owner() {
1255 return owner(false);
1256 }
1258 @SuppressWarnings("fallthrough")
1259 private Symbol owner(boolean skipLambda) {
1260 List<Frame> frameStack2 = frameStack;
1261 while (frameStack2.nonEmpty()) {
1262 switch (frameStack2.head.tree.getTag()) {
1263 case VARDEF:
1264 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1265 frameStack2 = frameStack2.tail;
1266 break;
1267 }
1268 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1269 return initSym(cdecl.sym,
1270 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1271 case BLOCK:
1272 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1273 return initSym(cdecl2.sym,
1274 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1275 case CLASSDEF:
1276 return ((JCClassDecl)frameStack2.head.tree).sym;
1277 case METHODDEF:
1278 return ((JCMethodDecl)frameStack2.head.tree).sym;
1279 case LAMBDA:
1280 if (!skipLambda)
1281 return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym;
1282 default:
1283 frameStack2 = frameStack2.tail;
1284 }
1285 }
1286 Assert.error();
1287 return null;
1288 }
1290 private Symbol initSym(ClassSymbol csym, long flags) {
1291 boolean isStatic = (flags & STATIC) != 0;
1292 if (isStatic) {
1293 //static clinits are generated in Gen - so we need to fake them
1294 Symbol clinit = clinits.get(csym);
1295 if (clinit == null) {
1296 clinit = makeSyntheticMethod(STATIC,
1297 names.clinit,
1298 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1299 csym);
1300 clinits.put(csym, clinit);
1301 }
1302 return clinit;
1303 } else {
1304 //get the first constructor and treat it as the instance init sym
1305 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1306 return s;
1307 }
1308 }
1309 Assert.error("init not found");
1310 return null;
1311 }
1313 private JCTree directlyEnclosingLambda() {
1314 if (frameStack.isEmpty()) {
1315 return null;
1316 }
1317 List<Frame> frameStack2 = frameStack;
1318 while (frameStack2.nonEmpty()) {
1319 switch (frameStack2.head.tree.getTag()) {
1320 case CLASSDEF:
1321 case METHODDEF:
1322 return null;
1323 case LAMBDA:
1324 return frameStack2.head.tree;
1325 default:
1326 frameStack2 = frameStack2.tail;
1327 }
1328 }
1329 Assert.error();
1330 return null;
1331 }
1333 private boolean inClassWithinLambda() {
1334 if (frameStack.isEmpty()) {
1335 return false;
1336 }
1337 List<Frame> frameStack2 = frameStack;
1338 boolean classFound = false;
1339 while (frameStack2.nonEmpty()) {
1340 switch (frameStack2.head.tree.getTag()) {
1341 case LAMBDA:
1342 return classFound;
1343 case CLASSDEF:
1344 classFound = true;
1345 frameStack2 = frameStack2.tail;
1346 break;
1347 default:
1348 frameStack2 = frameStack2.tail;
1349 }
1350 }
1351 // No lambda
1352 return false;
1353 }
1355 /**
1356 * Return the declaration corresponding to a symbol in the enclosing
1357 * scope; the depth parameter is used to filter out symbols defined
1358 * in nested scopes (which do not need to undergo capture).
1359 */
1360 private JCTree capturedDecl(int depth, Symbol sym) {
1361 int currentDepth = frameStack.size() - 1;
1362 for (Frame block : frameStack) {
1363 switch (block.tree.getTag()) {
1364 case CLASSDEF:
1365 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1366 if (sym.isMemberOf(clazz, types)) {
1367 return currentDepth > depth ? null : block.tree;
1368 }
1369 break;
1370 case VARDEF:
1371 if (((JCVariableDecl)block.tree).sym == sym &&
1372 sym.owner.kind == MTH) { //only locals are captured
1373 return currentDepth > depth ? null : block.tree;
1374 }
1375 break;
1376 case BLOCK:
1377 case METHODDEF:
1378 case LAMBDA:
1379 if (block.locals != null && block.locals.contains(sym)) {
1380 return currentDepth > depth ? null : block.tree;
1381 }
1382 break;
1383 default:
1384 Assert.error("bad decl kind " + block.tree.getTag());
1385 }
1386 currentDepth--;
1387 }
1388 return null;
1389 }
1391 private TranslationContext<?> context() {
1392 for (Frame frame : frameStack) {
1393 TranslationContext<?> context = contextMap.get(frame.tree);
1394 if (context != null) {
1395 return context;
1396 }
1397 }
1398 return null;
1399 }
1401 /**
1402 * This is used to filter out those identifiers that needs to be adjusted
1403 * when translating away lambda expressions
1404 */
1405 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1406 return (sym.kind == VAR || sym.kind == MTH)
1407 && !sym.isStatic()
1408 && sym.name != names.init;
1409 }
1411 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1412 return (sym.kind == VAR || sym.kind == MTH) &&
1413 !sym.isStatic() &&
1414 (sym.name == names._this ||
1415 sym.name == names._super);
1416 }
1418 /**
1419 * This is used to filter out those new class expressions that need to
1420 * be qualified with an enclosing tree
1421 */
1422 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1423 if (context != null
1424 && tree.encl == null
1425 && tree.def == null
1426 && !tree.type.getEnclosingType().hasTag(NONE)) {
1427 Type encl = tree.type.getEnclosingType();
1428 Type current = context.owner.enclClass().type;
1429 while (!current.hasTag(NONE)) {
1430 if (current.tsym.isSubClass(encl.tsym, types)) {
1431 return true;
1432 }
1433 current = current.getEnclosingType();
1434 }
1435 return false;
1436 } else {
1437 return false;
1438 }
1439 }
1441 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1442 return new LambdaTranslationContext(tree);
1443 }
1445 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1446 return new ReferenceTranslationContext(tree);
1447 }
1449 private class Frame {
1450 final JCTree tree;
1451 List<Symbol> locals;
1453 public Frame(JCTree tree) {
1454 this.tree = tree;
1455 }
1457 void addLocal(Symbol sym) {
1458 if (locals == null) {
1459 locals = List.nil();
1460 }
1461 locals = locals.prepend(sym);
1462 }
1463 }
1465 /**
1466 * This class is used to store important information regarding translation of
1467 * lambda expression/method references (see subclasses).
1468 */
1469 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1471 /** the underlying (untranslated) tree */
1472 T tree;
1474 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1475 Symbol owner;
1477 /** the depth of this lambda expression in the frame stack */
1478 int depth;
1480 /** the enclosing translation context (set for nested lambdas/mref) */
1481 TranslationContext<?> prev;
1483 TranslationContext(T tree) {
1484 this.tree = tree;
1485 this.owner = owner();
1486 this.depth = frameStack.size() - 1;
1487 this.prev = context();
1488 }
1490 /** does this functional expression need to be created using alternate metafactory? */
1491 boolean needsAltMetafactory() {
1492 return (tree.targets.length() > 1 ||
1493 isSerializable());
1494 }
1496 /** does this functional expression require serialization support? */
1497 boolean isSerializable() {
1498 for (Symbol target : tree.targets) {
1499 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1500 return true;
1501 }
1502 }
1503 return false;
1504 }
1505 }
1507 /**
1508 * This class retains all the useful information about a lambda expression;
1509 * the contents of this class are filled by the LambdaAnalyzer visitor,
1510 * and the used by the main translation routines in order to adjust references
1511 * to captured locals/members, etc.
1512 */
1513 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1515 /** variable in the enclosing context to which this lambda is assigned */
1516 Symbol self;
1518 /** map from original to translated lambda parameters */
1519 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1521 /** map from original to translated lambda locals */
1522 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1524 /** map from variables in enclosing scope to translated synthetic parameters */
1525 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1527 /** map from class symbols to translated synthetic parameters (for captured member access) */
1528 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1530 /** map from original to translated lambda locals */
1531 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1533 /** the synthetic symbol for the method hoisting the translated lambda */
1534 Symbol translatedSym;
1536 List<JCVariableDecl> syntheticParams;
1538 LambdaTranslationContext(JCLambda tree) {
1539 super(tree);
1540 Frame frame = frameStack.head;
1541 if (frame.tree.hasTag(VARDEF)) {
1542 self = ((JCVariableDecl)frame.tree).sym;
1543 }
1544 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1545 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1546 }
1548 /**
1549 * Translate a symbol of a given kind into something suitable for the
1550 * synthetic lambda body
1551 */
1552 Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
1553 switch (skind) {
1554 case CAPTURED_THIS:
1555 return sym; // self represented
1556 case TYPE_VAR:
1557 // Just erase the type var
1558 return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner);
1559 default:
1560 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1561 }
1562 }
1564 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1565 Map<Symbol, Symbol> transMap = null;
1566 String preferredName;
1567 switch (skind) {
1568 case CAPTURED_THIS:
1569 transMap = capturedThis;
1570 preferredName = "encl$" + capturedThis.size();
1571 break;
1572 case CAPTURED_VAR:
1573 transMap = capturedLocals;
1574 preferredName = "cap$" + capturedLocals.size();
1575 break;
1576 case LOCAL_VAR:
1577 transMap = lambdaLocals;
1578 preferredName = sym.name.toString();
1579 break;
1580 case PARAM:
1581 transMap = lambdaParams;
1582 preferredName = sym.name.toString();
1583 break;
1584 case TYPE_VAR:
1585 transMap = typeVars;
1586 preferredName = sym.name.toString();
1587 break;
1588 default: throw new AssertionError();
1589 }
1590 if (!transMap.containsKey(sym)) {
1591 transMap.put(sym, translate(preferredName, sym, skind));
1592 }
1593 }
1595 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1596 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1597 for (LambdaSymbolKind skind : skinds) {
1598 switch (skind) {
1599 case CAPTURED_THIS:
1600 translationMap.putAll(capturedThis);
1601 break;
1602 case CAPTURED_VAR:
1603 translationMap.putAll(capturedLocals);
1604 break;
1605 case LOCAL_VAR:
1606 translationMap.putAll(lambdaLocals);
1607 break;
1608 case PARAM:
1609 translationMap.putAll(lambdaParams);
1610 break;
1611 case TYPE_VAR:
1612 translationMap.putAll(typeVars);
1613 break;
1614 default: throw new AssertionError();
1615 }
1616 }
1617 return translationMap;
1618 }
1620 /**
1621 * The translatedSym is not complete/accurate until the analysis is
1622 * finished. Once the analysis is finished, the translatedSym is
1623 * "completed" -- updated with type information, access modifiers,
1624 * and full parameter list.
1625 */
1626 void complete() {
1627 if (syntheticParams != null) {
1628 return;
1629 }
1630 boolean inInterface = translatedSym.owner.isInterface();
1631 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1632 boolean needInstance = thisReferenced || inInterface;
1634 // If instance access isn't needed, make it static
1635 // Interface methods much be public default methods, otherwise make it private
1636 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE);
1638 //compute synthetic params
1639 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1641 // The signature of the method is augmented with the following
1642 // synthetic parameters:
1643 //
1644 // 1) reference to enclosing contexts captured by the lambda expression
1645 // 2) enclosing locals captured by the lambda expression
1646 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1647 params.append(make.VarDef((VarSymbol) thisSym, null));
1648 }
1650 syntheticParams = params.toList();
1652 //prepend synthetic args to translated lambda method signature
1653 translatedSym.type = types.createMethodTypeWithParameters(
1654 generatedLambdaSig(),
1655 TreeInfo.types(syntheticParams));
1656 }
1658 Type enclosingType() {
1659 return owner.isStatic() ?
1660 Type.noType :
1661 owner.enclClass().type;
1662 }
1664 Type generatedLambdaSig() {
1665 return types.erasure(tree.descriptorType);
1666 }
1667 }
1669 /**
1670 * This class retains all the useful information about a method reference;
1671 * the contents of this class are filled by the LambdaAnalyzer visitor,
1672 * and the used by the main translation routines in order to adjust method
1673 * references (i.e. in case a bridge is needed)
1674 */
1675 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1677 final boolean isSuper;
1678 final Symbol bridgeSym;
1680 ReferenceTranslationContext(JCMemberReference tree) {
1681 super(tree);
1682 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1683 this.bridgeSym = needsBridge()
1684 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1685 lambdaName().append(names.fromString("$bridge")), null,
1686 owner.enclClass())
1687 : null;
1688 }
1690 /**
1691 * Get the opcode associated with this method reference
1692 */
1693 int referenceKind() {
1694 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1695 }
1697 boolean needsVarArgsConversion() {
1698 return tree.varargsElement != null;
1699 }
1701 /**
1702 * @return Is this an array operation like clone()
1703 */
1704 boolean isArrayOp() {
1705 return tree.sym.owner == syms.arrayClass;
1706 }
1708 /**
1709 * Does this reference needs a bridge (i.e. var args need to be
1710 * expanded or "super" is used)
1711 */
1712 final boolean needsBridge() {
1713 return isSuper || needsVarArgsConversion() || isArrayOp();
1714 }
1716 Type generatedRefSig() {
1717 return types.erasure(tree.sym.type);
1718 }
1720 Type bridgedRefSig() {
1721 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1722 }
1723 }
1724 }
1725 // </editor-fold>
1727 enum LambdaSymbolKind {
1728 CAPTURED_VAR,
1729 CAPTURED_THIS,
1730 LOCAL_VAR,
1731 PARAM,
1732 TYPE_VAR;
1733 }
1735 /**
1736 * ****************************************************************
1737 * Signature Generation
1738 * ****************************************************************
1739 */
1741 private String methodSig(Type type) {
1742 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1743 sg.assembleSig(type);
1744 return sg.toString();
1745 }
1747 private String classSig(Type type) {
1748 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1749 sg.assembleClassSig(type);
1750 return sg.toString();
1751 }
1753 /**
1754 * Signature Generation
1755 */
1756 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1758 /**
1759 * An output buffer for type signatures.
1760 */
1761 StringBuilder sb = new StringBuilder();
1763 L2MSignatureGenerator() {
1764 super(types);
1765 }
1767 @Override
1768 protected void append(char ch) {
1769 sb.append(ch);
1770 }
1772 @Override
1773 protected void append(byte[] ba) {
1774 sb.append(new String(ba));
1775 }
1777 @Override
1778 protected void append(Name name) {
1779 sb.append(name.toString());
1780 }
1782 @Override
1783 public String toString() {
1784 return sb.toString();
1785 }
1786 }
1787 }