Tue, 26 Feb 2013 09:04:19 +0000
8008436: javac should not issue a warning for overriding equals without hasCode if hashCode has been overriden by a superclass
Reviewed-by: jjg, mcimadamore
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.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"),
129 syms.serializedLambdaType, deserMethodSym);
130 }
132 private void addMethod(JCTree decl) {
133 appendedMethodList = appendedMethodList.prepend(decl);
134 }
135 }
137 // <editor-fold defaultstate="collapsed" desc="Instantiating">
138 private static final Context.Key<LambdaToMethod> unlambdaKey =
139 new Context.Key<LambdaToMethod>();
141 public static LambdaToMethod instance(Context context) {
142 LambdaToMethod instance = context.get(unlambdaKey);
143 if (instance == null) {
144 instance = new LambdaToMethod(context);
145 }
146 return instance;
147 }
149 private LambdaToMethod(Context context) {
150 names = Names.instance(context);
151 syms = Symtab.instance(context);
152 rs = Resolve.instance(context);
153 make = TreeMaker.instance(context);
154 types = Types.instance(context);
155 transTypes = TransTypes.instance(context);
156 analyzer = new LambdaAnalyzer();
157 }
158 // </editor-fold>
160 // <editor-fold defaultstate="collapsed" desc="translate methods">
161 @Override
162 public <T extends JCTree> T translate(T tree) {
163 TranslationContext<?> newContext = contextMap.get(tree);
164 return translate(tree, newContext != null ? newContext : context);
165 }
167 public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
168 TranslationContext<?> prevContext = context;
169 try {
170 context = newContext;
171 return super.translate(tree);
172 }
173 finally {
174 context = prevContext;
175 }
176 }
178 public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
179 ListBuffer<T> buf = ListBuffer.lb();
180 for (T tree : trees) {
181 buf.append(translate(tree, newContext));
182 }
183 return buf.toList();
184 }
186 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
187 this.make = make;
188 this.attrEnv = env;
189 this.context = null;
190 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
191 return translate(cdef);
192 }
193 // </editor-fold>
195 // <editor-fold defaultstate="collapsed" desc="visitor methods">
196 /**
197 * Visit a class.
198 * Maintain the translatedMethodList across nested classes.
199 * Append the translatedMethodList to the class after it is translated.
200 * @param tree
201 */
202 @Override
203 public void visitClassDef(JCClassDecl tree) {
204 if (tree.sym.owner.kind == PCK) {
205 //analyze class
206 analyzer.analyzeClass(tree);
207 }
208 KlassInfo prevKlassInfo = kInfo;
209 try {
210 kInfo = new KlassInfo(tree.sym);
211 super.visitClassDef(tree);
212 if (!kInfo.deserializeCases.isEmpty()) {
213 kInfo.addMethod(makeDeserializeMethod(tree.sym));
214 }
215 //add all translated instance methods here
216 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
217 tree.defs = tree.defs.appendList(newMethods);
218 for (JCTree lambda : newMethods) {
219 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
220 }
221 result = tree;
222 } finally {
223 kInfo = prevKlassInfo;
224 }
225 }
227 /**
228 * Translate a lambda into a method to be inserted into the class.
229 * Then replace the lambda site with an invokedynamic call of to lambda
230 * meta-factory, which will use the lambda method.
231 * @param tree
232 */
233 @Override
234 public void visitLambda(JCLambda tree) {
235 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
236 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
237 MethodType lambdaType = (MethodType) sym.type;
239 //create the method declaration hoisting the lambda body
240 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
241 sym.name,
242 make.QualIdent(lambdaType.getReturnType().tsym),
243 List.<JCTypeParameter>nil(),
244 localContext.syntheticParams,
245 lambdaType.getThrownTypes() == null ?
246 List.<JCExpression>nil() :
247 make.Types(lambdaType.getThrownTypes()),
248 null,
249 null);
250 lambdaDecl.sym = sym;
251 lambdaDecl.type = lambdaType;
253 //translate lambda body
254 //As the lambda body is translated, all references to lambda locals,
255 //captured variables, enclosing members are adjusted accordingly
256 //to refer to the static method parameters (rather than i.e. acessing to
257 //captured members directly).
258 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
260 //Add the method to the list of methods to be added to this class.
261 kInfo.addMethod(lambdaDecl);
263 //now that we have generated a method for the lambda expression,
264 //we can translate the lambda into a method reference pointing to the newly
265 //created method.
266 //
267 //Note that we need to adjust the method handle so that it will match the
268 //signature of the SAM descriptor - this means that the method reference
269 //should be added the following synthetic arguments:
270 //
271 // * the "this" argument if it is an instance method
272 // * enclosing locals captured by the lambda expression
274 ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
276 if (!sym.isStatic()) {
277 syntheticInits.append(makeThis(
278 sym.owner.enclClass().asType(),
279 localContext.owner.enclClass()));
280 }
282 //add captured locals
283 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
284 if (fv != localContext.self) {
285 JCTree captured_local = make.Ident(fv).setType(fv.type);
286 syntheticInits.append((JCExpression) captured_local);
287 }
288 }
290 //then, determine the arguments to the indy call
291 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
293 //build a sam instance using an indy call to the meta-factory
294 int refKind = referenceKind(sym);
296 //convert to an invokedynamic call
297 result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
298 }
300 private JCIdent makeThis(Type type, Symbol owner) {
301 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
302 names._this,
303 type,
304 owner);
305 return make.Ident(_this);
306 }
308 /**
309 * Translate a method reference into an invokedynamic call to the
310 * meta-factory.
311 * @param tree
312 */
313 @Override
314 public void visitReference(JCMemberReference tree) {
315 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
317 //first determine the method symbol to be used to generate the sam instance
318 //this is either the method reference symbol, or the bridged reference symbol
319 Symbol refSym = localContext.needsBridge() ?
320 localContext.bridgeSym :
321 tree.sym;
323 //build the bridge method, if needed
324 if (localContext.needsBridge()) {
325 bridgeMemberReference(tree, localContext);
326 }
328 //the qualifying expression is treated as a special captured arg
329 JCExpression init;
330 switch(tree.kind) {
332 case IMPLICIT_INNER: /** Inner :: new */
333 case SUPER: /** super :: instMethod */
334 init = makeThis(
335 localContext.owner.enclClass().asType(),
336 localContext.owner.enclClass());
337 break;
339 case BOUND: /** Expr :: instMethod */
340 init = tree.getQualifierExpression();
341 break;
343 case UNBOUND: /** Type :: instMethod */
344 case STATIC: /** Type :: staticMethod */
345 case TOPLEVEL: /** Top level :: new */
346 case ARRAY_CTOR: /** ArrayType :: new */
347 init = null;
348 break;
350 default:
351 throw new InternalError("Should not have an invalid kind");
352 }
354 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
357 //build a sam instance using an indy call to the meta-factory
358 result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
359 }
361 /**
362 * Translate identifiers within a lambda to the mapped identifier
363 * @param tree
364 */
365 @Override
366 public void visitIdent(JCIdent tree) {
367 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
368 super.visitIdent(tree);
369 } else {
370 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
371 if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
372 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
373 result = make.Ident(translatedSym).setType(tree.type);
374 } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
375 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
376 result = make.Ident(translatedSym).setType(tree.type);
377 } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
378 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
379 result = make.Ident(translatedSym).setType(translatedSym.type);
380 } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
381 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
382 result = make.Ident(translatedSym).setType(tree.type);
383 } else {
384 if (tree.sym.owner.kind == Kinds.TYP) {
385 for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
386 if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
387 JCExpression enclRef = make.Ident(encl_entry.getValue());
388 result = tree.sym.name == names._this
389 ? enclRef.setType(tree.type)
390 : make.Select(enclRef, tree.sym).setType(tree.type);
391 result = tree;
392 return;
393 }
394 }
395 }
396 //access to untranslated symbols (i.e. compile-time constants,
397 //members defined inside the lambda body, etc.) )
398 super.visitIdent(tree);
399 }
400 }
401 }
403 @Override
404 public void visitVarDef(JCVariableDecl tree) {
405 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
406 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
407 JCExpression init = translate(tree.init);
408 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
409 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
410 JCExpression init = translate(tree.init);
411 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
412 result = make.VarDef(xsym, init);
413 // Replace the entered symbol for this variable
414 Scope sc = tree.sym.owner.members();
415 if (sc != null) {
416 sc.remove(tree.sym);
417 sc.enter(xsym);
418 }
419 } else {
420 super.visitVarDef(tree);
421 }
422 }
424 // </editor-fold>
426 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
428 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
429 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
430 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
431 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
432 }
434 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
435 Type restype = lambdaMethodDecl.type.getReturnType();
436 boolean isLambda_void = expr.type.hasTag(VOID);
437 boolean isTarget_void = restype.hasTag(VOID);
438 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
439 if (isTarget_void) {
440 //target is void:
441 // BODY;
442 JCStatement stat = make.Exec(expr);
443 return make.Block(0, List.<JCStatement>of(stat));
444 } else if (isLambda_void && isTarget_Void) {
445 //void to Void conversion:
446 // BODY; return null;
447 ListBuffer<JCStatement> stats = ListBuffer.lb();
448 stats.append(make.Exec(expr));
449 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
450 return make.Block(0, stats.toList());
451 } else {
452 //non-void to non-void conversion:
453 // return (TYPE)BODY;
454 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
455 return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
456 }
457 }
459 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
460 final Type restype = lambdaMethodDecl.type.getReturnType();
461 final boolean isTarget_void = restype.hasTag(VOID);
462 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
464 class LambdaBodyTranslator extends TreeTranslator {
466 @Override
467 public void visitClassDef(JCClassDecl tree) {
468 //do NOT recurse on any inner classes
469 result = tree;
470 }
472 @Override
473 public void visitLambda(JCLambda tree) {
474 //do NOT recurse on any nested lambdas
475 result = tree;
476 }
478 @Override
479 public void visitReturn(JCReturn tree) {
480 boolean isLambda_void = tree.expr == null;
481 if (isTarget_void && !isLambda_void) {
482 //Void to void conversion:
483 // { TYPE $loc = RET-EXPR; return; }
484 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
485 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
486 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
487 } else if (!isTarget_void || !isLambda_void) {
488 //non-void to non-void conversion:
489 // return (TYPE)RET-EXPR;
490 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
491 result = tree;
492 } else {
493 result = tree;
494 }
496 }
497 }
499 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
500 if (completeNormally && isTarget_Void) {
501 //there's no return statement and the lambda (possibly inferred)
502 //return type is java.lang.Void; emit a synthetic return statement
503 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
504 }
505 return trans_block;
506 }
508 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
509 ListBuffer<JCCase> cases = ListBuffer.lb();
510 ListBuffer<JCBreak> breaks = ListBuffer.lb();
511 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
512 JCBreak br = make.Break(null);
513 breaks.add(br);
514 List<JCStatement> stmts = entry.getValue().append(br).toList();
515 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
516 }
517 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
518 for (JCBreak br : breaks) {
519 br.target = sw;
520 }
521 JCBlock body = make.Block(0L, List.<JCStatement>of(
522 sw,
523 make.Throw(makeNewClass(
524 syms.illegalArgumentExceptionType,
525 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
526 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
527 names.deserializeLambda,
528 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
529 List.<JCTypeParameter>nil(),
530 List.of(make.VarDef(kInfo.deserParamSym, null)),
531 List.<JCExpression>nil(),
532 body,
533 null);
534 deser.sym = kInfo.deserMethodSym;
535 deser.type = kInfo.deserMethodSym.type;
536 //System.err.printf("DESER: '%s'\n", deser);
537 return deser;
538 }
540 /** Make an attributed class instance creation expression.
541 * @param ctype The class type.
542 * @param args The constructor arguments.
543 */
544 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
545 JCNewClass tree = make.NewClass(null,
546 null, make.QualIdent(ctype.tsym), args, null);
547 tree.constructor = rs.resolveConstructor(
548 null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
549 tree.type = ctype;
550 return tree;
551 }
553 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
554 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
555 String functionalInterfaceClass = classSig(targetType);
556 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
557 String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
558 String implClass = classSig(refSym.owner.type);
559 String implMethodName = refSym.getQualifiedName().toString();
560 String implMethodSignature = methodSig(types.erasure(refSym.type));
562 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
563 ListBuffer<JCExpression> serArgs = ListBuffer.lb();
564 int i = 0;
565 for (Type t : indyType.getParameterTypes()) {
566 List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
567 List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
568 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
569 ++i;
570 }
571 JCStatement stmt = make.If(
572 deserTest(deserTest(deserTest(deserTest(deserTest(
573 kindTest,
574 "getFunctionalInterfaceClass", functionalInterfaceClass),
575 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
576 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
577 "getImplClass", implClass),
578 "getImplMethodSignature", implMethodSignature),
579 make.Return(makeIndyCall(
580 pos,
581 syms.lambdaMetafactory,
582 names.altMetaFactory,
583 staticArgs, indyType, serArgs.toList())),
584 null);
585 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
586 if (stmts == null) {
587 stmts = ListBuffer.lb();
588 kInfo.deserializeCases.put(implMethodName, stmts);
589 }
590 /****
591 System.err.printf("+++++++++++++++++\n");
592 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
593 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
594 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
595 System.err.printf("*implMethodKind: %d\n", implMethodKind);
596 System.err.printf("*implClass: '%s'\n", implClass);
597 System.err.printf("*implMethodName: '%s'\n", implMethodName);
598 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
599 ****/
600 stmts.append(stmt);
601 }
603 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
604 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
605 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
606 testExpr.setType(syms.booleanType);
607 return testExpr;
608 }
610 private JCExpression deserTest(JCExpression prev, String func, String lit) {
611 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
612 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
613 JCMethodInvocation eqtest = make.Apply(
614 List.<JCExpression>nil(),
615 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
616 List.<JCExpression>of(make.Literal(lit)));
617 eqtest.setType(syms.booleanType);
618 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
619 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
620 compound.setType(syms.booleanType);
621 return compound;
622 }
624 private JCExpression deserGetter(String func, Type type) {
625 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
626 }
628 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
629 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
630 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
631 return make.Apply(
632 List.<JCExpression>nil(),
633 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
634 args).setType(type);
635 }
637 /**
638 * Create new synthetic method with given flags, name, type, owner
639 */
640 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
641 return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
642 }
644 /**
645 * Create new synthetic variable with given flags, name, type, owner
646 */
647 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
648 return makeSyntheticVar(flags, names.fromString(name), type, owner);
649 }
651 /**
652 * Create new synthetic variable with given flags, name, type, owner
653 */
654 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
655 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
656 }
658 /**
659 * Set varargsElement field on a given tree (must be either a new class tree
660 * or a method call tree)
661 */
662 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
663 if (varargsElement != null) {
664 switch (tree.getTag()) {
665 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
666 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
667 default: throw new AssertionError();
668 }
669 }
670 }
672 /**
673 * Convert method/constructor arguments by inserting appropriate cast
674 * as required by type-erasure - this is needed when bridging a lambda/method
675 * reference, as the bridged signature might require downcast to be compatible
676 * with the generated signature.
677 */
678 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
679 Assert.check(meth.kind == Kinds.MTH);
680 List<Type> formals = types.erasure(meth.type).getParameterTypes();
681 if (varargsElement != null) {
682 Assert.check((meth.flags() & VARARGS) != 0);
683 }
684 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
685 }
687 // </editor-fold>
689 /**
690 * Generate an adapter method "bridge" for a method reference which cannot
691 * be used directly.
692 */
693 private class MemberReferenceBridger {
695 private final JCMemberReference tree;
696 private final ReferenceTranslationContext localContext;
697 private final ListBuffer<JCExpression> args = ListBuffer.lb();
698 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
700 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
701 this.tree = tree;
702 this.localContext = localContext;
703 }
705 /**
706 * Generate the bridge
707 */
708 JCMethodDecl bridge() {
709 int prevPos = make.pos;
710 try {
711 make.at(tree);
712 Type samDesc = localContext.bridgedRefSig();
713 List<Type> samPTypes = samDesc.getParameterTypes();
715 //an extra argument is prepended to the signature of the bridge in case
716 //the member reference is an instance method reference (in which case
717 //the receiver expression is passed to the bridge itself).
718 Type recType = null;
719 switch (tree.kind) {
720 case IMPLICIT_INNER:
721 recType = tree.sym.owner.type.getEnclosingType();
722 break;
723 case BOUND:
724 recType = tree.getQualifierExpression().type;
725 break;
726 case UNBOUND:
727 recType = samPTypes.head;
728 samPTypes = samPTypes.tail;
729 break;
730 }
732 //generate the parameter list for the bridged member reference - the
733 //bridge signature will match the signature of the target sam descriptor
735 VarSymbol rcvr = (recType == null)
736 ? null
737 : addParameter("rec$", recType, false);
739 List<Type> refPTypes = tree.sym.type.getParameterTypes();
740 int refSize = refPTypes.size();
741 int samSize = samPTypes.size();
742 // Last parameter to copy from referenced method
743 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
745 List<Type> l = refPTypes;
746 // Use parameter types of the referenced method, excluding final var args
747 for (int i = 0; l.nonEmpty() && i < last; ++i) {
748 addParameter("x$" + i, l.head, true);
749 l = l.tail;
750 }
751 // Flatten out the var args
752 for (int i = last; i < samSize; ++i) {
753 addParameter("xva$" + i, tree.varargsElement, true);
754 }
756 //generate the bridge method declaration
757 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
758 localContext.bridgeSym.name,
759 make.QualIdent(samDesc.getReturnType().tsym),
760 List.<JCTypeParameter>nil(),
761 params.toList(),
762 tree.sym.type.getThrownTypes() == null
763 ? List.<JCExpression>nil()
764 : make.Types(tree.sym.type.getThrownTypes()),
765 null,
766 null);
767 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
768 bridgeDecl.type = localContext.bridgeSym.type =
769 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
771 //bridge method body generation - this can be either a method call or a
772 //new instance creation expression, depending on the member reference kind
773 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
774 ? bridgeExpressionInvoke(rcvr)
775 : bridgeExpressionNew();
777 //the body is either a return expression containing a method call,
778 //or the method call itself, depending on whether the return type of
779 //the bridge is non-void/void.
780 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
782 return bridgeDecl;
783 } finally {
784 make.at(prevPos);
785 }
786 }
788 /**
789 * determine the receiver of the bridged method call - the receiver can
790 * be either the synthetic receiver parameter or a type qualifier; the
791 * original qualifier expression is never used here, as it might refer
792 * to symbols not available in the static context of the bridge
793 */
794 private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) {
795 JCExpression qualifier =
796 tree.sym.isStatic() ?
797 make.Type(tree.sym.owner.type) :
798 (rcvr != null) ?
799 make.Ident(rcvr) :
800 tree.getQualifierExpression();
802 //create the qualifier expression
803 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
804 select.sym = tree.sym;
805 select.type = tree.sym.erasure(types);
807 //create the method call expression
808 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
809 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
810 setType(tree.sym.erasure(types).getReturnType());
812 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
813 setVarargsIfNeeded(apply, tree.varargsElement);
814 return apply;
815 }
817 /**
818 * the enclosing expression is either 'null' (no enclosing type) or set
819 * to the first bridge synthetic parameter
820 */
821 private JCExpression bridgeExpressionNew() {
822 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
823 //create the array creation expression
824 JCNewArray newArr = make.NewArray(
825 make.Type(types.elemtype(tree.getQualifierExpression().type)),
826 List.of(make.Ident(params.first())),
827 null);
828 newArr.type = tree.getQualifierExpression().type;
829 return newArr;
830 } else {
831 JCExpression encl = null;
832 switch (tree.kind) {
833 case UNBOUND:
834 case IMPLICIT_INNER:
835 encl = make.Ident(params.first());
836 }
838 //create the instance creation expression
839 JCNewClass newClass = make.NewClass(encl,
840 List.<JCExpression>nil(),
841 make.Type(tree.getQualifierExpression().type),
842 convertArgs(tree.sym, args.toList(), tree.varargsElement),
843 null);
844 newClass.constructor = tree.sym;
845 newClass.constructorType = tree.sym.erasure(types);
846 newClass.type = tree.getQualifierExpression().type;
847 setVarargsIfNeeded(newClass, tree.varargsElement);
848 return newClass;
849 }
850 }
852 private VarSymbol addParameter(String name, Type p, boolean genArg) {
853 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
854 params.append(make.VarDef(vsym, null));
855 if (genArg) {
856 args.append(make.Ident(vsym));
857 }
858 return vsym;
859 }
860 }
862 /**
863 * Bridges a member reference - this is needed when:
864 * * Var args in the referenced method need to be flattened away
865 * * super is used
866 */
867 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
868 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
869 }
871 /**
872 * Generate an indy method call to the meta factory
873 */
874 private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
875 boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
876 //determine the static bsm args
877 Type mtype = types.erasure(tree.descriptorType);
878 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
879 List<Object> staticArgs = List.<Object>of(
880 new Pool.MethodHandle(ClassFile.REF_invokeInterface,
881 types.findDescriptorSymbol(tree.type.tsym), types),
882 new Pool.MethodHandle(refKind, refSym, types),
883 new MethodType(mtype.getParameterTypes(),
884 mtype.getReturnType(),
885 mtype.getThrownTypes(),
886 syms.methodClass));
888 //computed indy arg types
889 ListBuffer<Type> indy_args_types = ListBuffer.lb();
890 for (JCExpression arg : indy_args) {
891 indy_args_types.append(arg.type);
892 }
894 //finally, compute the type of the indy call
895 MethodType indyType = new MethodType(indy_args_types.toList(),
896 tree.type,
897 List.<Type>nil(),
898 syms.methodClass);
900 Name metafactoryName = needsAltMetafactory ?
901 names.altMetaFactory : names.metaFactory;
903 if (needsAltMetafactory) {
904 ListBuffer<Object> markers = ListBuffer.lb();
905 for (Symbol t : tree.targets.tail) {
906 if (t != syms.serializableType.tsym) {
907 markers.append(t);
908 }
909 }
910 int flags = isSerializable? FLAG_SERIALIZABLE : 0;
911 boolean hasMarkers = markers.nonEmpty();
912 flags |= hasMarkers ? FLAG_MARKERS : 0;
913 staticArgs = staticArgs.append(flags);
914 if (hasMarkers) {
915 staticArgs = staticArgs.append(markers.length());
916 staticArgs = staticArgs.appendList(markers.toList());
917 }
918 if (isSerializable) {
919 addDeserializationCase(refKind, refSym, tree.type, samSym,
920 tree, staticArgs, indyType);
921 }
922 }
924 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
925 }
927 /**
928 * Generate an indy method call with given name, type and static bootstrap
929 * arguments types
930 */
931 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
932 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
933 int prevPos = make.pos;
934 try {
935 make.at(pos);
936 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
937 syms.stringType,
938 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
940 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
941 bsmName, bsm_staticArgs, List.<Type>nil());
943 DynamicMethodSymbol dynSym =
944 new DynamicMethodSymbol(names.lambda,
945 syms.noSymbol,
946 bsm.isStatic() ?
947 ClassFile.REF_invokeStatic :
948 ClassFile.REF_invokeVirtual,
949 (MethodSymbol)bsm,
950 indyType,
951 staticArgs.toArray());
953 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
954 qualifier.sym = dynSym;
955 qualifier.type = indyType.getReturnType();
957 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
958 proxyCall.type = indyType.getReturnType();
959 return proxyCall;
960 } finally {
961 make.at(prevPos);
962 }
963 }
964 //where
965 private List<Type> bsmStaticArgToTypes(List<Object> args) {
966 ListBuffer<Type> argtypes = ListBuffer.lb();
967 for (Object arg : args) {
968 argtypes.append(bsmStaticArgToType(arg));
969 }
970 return argtypes.toList();
971 }
973 private Type bsmStaticArgToType(Object arg) {
974 Assert.checkNonNull(arg);
975 if (arg instanceof ClassSymbol) {
976 return syms.classType;
977 } else if (arg instanceof Integer) {
978 return syms.intType;
979 } else if (arg instanceof Long) {
980 return syms.longType;
981 } else if (arg instanceof Float) {
982 return syms.floatType;
983 } else if (arg instanceof Double) {
984 return syms.doubleType;
985 } else if (arg instanceof String) {
986 return syms.stringType;
987 } else if (arg instanceof Pool.MethodHandle) {
988 return syms.methodHandleType;
989 } else if (arg instanceof MethodType) {
990 return syms.methodTypeType;
991 } else {
992 Assert.error("bad static arg " + arg.getClass());
993 return null;
994 }
995 }
997 /**
998 * Get the opcode associated with this method reference
999 */
1000 private int referenceKind(Symbol refSym) {
1001 if (refSym.isConstructor()) {
1002 return ClassFile.REF_newInvokeSpecial;
1003 } else {
1004 if (refSym.isStatic()) {
1005 return ClassFile.REF_invokeStatic;
1006 } else if (refSym.enclClass().isInterface()) {
1007 return ClassFile.REF_invokeInterface;
1008 } else {
1009 return ClassFile.REF_invokeVirtual;
1010 }
1011 }
1012 }
1014 // </editor-fold>
1016 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
1017 /**
1018 * This visitor collects information about translation of a lambda expression.
1019 * More specifically, it keeps track of the enclosing contexts and captured locals
1020 * accessed by the lambda being translated (as well as other useful info).
1021 */
1022 class LambdaAnalyzer extends TreeScanner {
1024 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1025 private List<Frame> frameStack;
1027 /**
1028 * keep the count of lambda expression (used to generate unambiguous
1029 * names)
1030 */
1031 private int lambdaCount = 0;
1033 /**
1034 * keep the count of lambda expression defined in given context (used to
1035 * generate unambiguous names for serializable lambdas)
1036 */
1037 private Map<String, Integer> serializableLambdaCounts =
1038 new HashMap<String, Integer>();
1040 /**
1041 * maps for fake clinit symbols to be used as owners of lambda occurring in
1042 * a static var init context
1043 */
1044 private Map<ClassSymbol, Symbol> clinits =
1045 new HashMap<ClassSymbol, Symbol>();
1047 private void analyzeClass(JCClassDecl tree) {
1048 frameStack = List.nil();
1049 scan(tree);
1050 }
1052 @Override
1053 public void visitBlock(JCBlock tree) {
1054 List<Frame> prevStack = frameStack;
1055 try {
1056 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1057 frameStack = frameStack.prepend(new Frame(tree));
1058 }
1059 super.visitBlock(tree);
1060 }
1061 finally {
1062 frameStack = prevStack;
1063 }
1064 }
1066 @Override
1067 public void visitClassDef(JCClassDecl tree) {
1068 List<Frame> prevStack = frameStack;
1069 Map<String, Integer> prevSerializableLambdaCount =
1070 serializableLambdaCounts;
1071 Map<ClassSymbol, Symbol> prevClinits = clinits;
1072 try {
1073 serializableLambdaCounts = new HashMap<String, Integer>();
1074 prevClinits = new HashMap<ClassSymbol, Symbol>();
1075 if (directlyEnclosingLambda() != null) {
1076 tree.sym.owner = owner();
1077 if (tree.sym.hasOuterInstance()) {
1078 //if a class is defined within a lambda, the lambda must capture
1079 //its enclosing instance (if any)
1080 ((LambdaTranslationContext) context())
1081 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1082 }
1083 }
1084 frameStack = frameStack.prepend(new Frame(tree));
1085 super.visitClassDef(tree);
1086 }
1087 finally {
1088 frameStack = prevStack;
1089 serializableLambdaCounts = prevSerializableLambdaCount;
1090 clinits = prevClinits;
1091 }
1092 }
1094 @Override
1095 public void visitIdent(JCIdent tree) {
1096 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1097 if (tree.sym.kind == VAR &&
1098 tree.sym.owner.kind == MTH &&
1099 tree.type.constValue() == null) {
1100 TranslationContext<?> localContext = context();
1101 while (localContext != null) {
1102 if (localContext.tree.getTag() == LAMBDA) {
1103 JCTree block = capturedDecl(localContext.depth, tree.sym);
1104 if (block == null) break;
1105 ((LambdaTranslationContext)localContext)
1106 .addSymbol(tree.sym, CAPTURED_VAR);
1107 }
1108 localContext = localContext.prev;
1109 }
1110 } else if (tree.sym.owner.kind == TYP) {
1111 TranslationContext<?> localContext = context();
1112 while (localContext != null) {
1113 if (localContext.tree.hasTag(LAMBDA)) {
1114 JCTree block = capturedDecl(localContext.depth, tree.sym);
1115 if (block == null) break;
1116 switch (block.getTag()) {
1117 case CLASSDEF:
1118 JCClassDecl cdecl = (JCClassDecl)block;
1119 ((LambdaTranslationContext)localContext)
1120 .addSymbol(cdecl.sym, CAPTURED_THIS);
1121 break;
1122 default:
1123 Assert.error("bad block kind");
1124 }
1125 }
1126 localContext = localContext.prev;
1127 }
1128 }
1129 }
1130 super.visitIdent(tree);
1131 }
1133 @Override
1134 public void visitLambda(JCLambda tree) {
1135 List<Frame> prevStack = frameStack;
1136 try {
1137 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1138 frameStack = frameStack.prepend(new Frame(tree));
1139 for (JCVariableDecl param : tree.params) {
1140 context.addSymbol(param.sym, PARAM);
1141 frameStack.head.addLocal(param.sym);
1142 }
1143 contextMap.put(tree, context);
1144 scan(tree.body);
1145 context.complete();
1146 }
1147 finally {
1148 frameStack = prevStack;
1149 }
1150 }
1152 @Override
1153 public void visitMethodDef(JCMethodDecl tree) {
1154 List<Frame> prevStack = frameStack;
1155 try {
1156 frameStack = frameStack.prepend(new Frame(tree));
1157 super.visitMethodDef(tree);
1158 }
1159 finally {
1160 frameStack = prevStack;
1161 }
1162 }
1164 @Override
1165 public void visitNewClass(JCNewClass tree) {
1166 if (lambdaNewClassFilter(context(), tree)) {
1167 ((LambdaTranslationContext) context())
1168 .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
1282 .get(frameStack2.head.tree)).translatedSym;
1283 default:
1284 frameStack2 = frameStack2.tail;
1285 }
1286 }
1287 Assert.error();
1288 return null;
1289 }
1291 private Symbol initSym(ClassSymbol csym, long flags) {
1292 boolean isStatic = (flags & STATIC) != 0;
1293 if (isStatic) {
1294 //static clinits are generated in Gen - so we need to fake them
1295 Symbol clinit = clinits.get(csym);
1296 if (clinit == null) {
1297 clinit = makeSyntheticMethod(STATIC,
1298 names.clinit,
1299 new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
1300 csym);
1301 clinits.put(csym, clinit);
1302 }
1303 return clinit;
1304 } else {
1305 //get the first constructor and treat it as the instance init sym
1306 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1307 return s;
1308 }
1309 }
1310 Assert.error("init not found");
1311 return null;
1312 }
1314 private JCTree directlyEnclosingLambda() {
1315 if (frameStack.isEmpty()) {
1316 return null;
1317 }
1318 List<Frame> frameStack2 = frameStack;
1319 while (frameStack2.nonEmpty()) {
1320 switch (frameStack2.head.tree.getTag()) {
1321 case CLASSDEF:
1322 case METHODDEF:
1323 return null;
1324 case LAMBDA:
1325 return frameStack2.head.tree;
1326 default:
1327 frameStack2 = frameStack2.tail;
1328 }
1329 }
1330 Assert.error();
1331 return null;
1332 }
1334 private boolean inClassWithinLambda() {
1335 if (frameStack.isEmpty()) {
1336 return false;
1337 }
1338 List<Frame> frameStack2 = frameStack;
1339 boolean classFound = false;
1340 while (frameStack2.nonEmpty()) {
1341 switch (frameStack2.head.tree.getTag()) {
1342 case LAMBDA:
1343 return classFound;
1344 case CLASSDEF:
1345 classFound = true;
1346 frameStack2 = frameStack2.tail;
1347 break;
1348 default:
1349 frameStack2 = frameStack2.tail;
1350 }
1351 }
1352 // No lambda
1353 return false;
1354 }
1356 /**
1357 * Return the declaration corresponding to a symbol in the enclosing
1358 * scope; the depth parameter is used to filter out symbols defined
1359 * in nested scopes (which do not need to undergo capture).
1360 */
1361 private JCTree capturedDecl(int depth, Symbol sym) {
1362 int currentDepth = frameStack.size() - 1;
1363 for (Frame block : frameStack) {
1364 switch (block.tree.getTag()) {
1365 case CLASSDEF:
1366 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1367 if (sym.isMemberOf(clazz, types)) {
1368 return currentDepth > depth ? null : block.tree;
1369 }
1370 break;
1371 case VARDEF:
1372 if (((JCVariableDecl)block.tree).sym == sym &&
1373 sym.owner.kind == MTH) { //only locals are captured
1374 return currentDepth > depth ? null : block.tree;
1375 }
1376 break;
1377 case BLOCK:
1378 case METHODDEF:
1379 case LAMBDA:
1380 if (block.locals != null && block.locals.contains(sym)) {
1381 return currentDepth > depth ? null : block.tree;
1382 }
1383 break;
1384 default:
1385 Assert.error("bad decl kind " + block.tree.getTag());
1386 }
1387 currentDepth--;
1388 }
1389 return null;
1390 }
1392 private TranslationContext<?> context() {
1393 for (Frame frame : frameStack) {
1394 TranslationContext<?> context = contextMap.get(frame.tree);
1395 if (context != null) {
1396 return context;
1397 }
1398 }
1399 return null;
1400 }
1402 /**
1403 * This is used to filter out those identifiers that needs to be adjusted
1404 * when translating away lambda expressions
1405 */
1406 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1407 return (sym.kind == VAR || sym.kind == MTH)
1408 && !sym.isStatic()
1409 && sym.name != names.init;
1410 }
1412 private boolean lambdaSelectSymbolFilter(Symbol sym) {
1413 return (sym.kind == VAR || sym.kind == MTH) &&
1414 !sym.isStatic() &&
1415 (sym.name == names._this ||
1416 sym.name == names._super);
1417 }
1419 /**
1420 * This is used to filter out those new class expressions that need to
1421 * be qualified with an enclosing tree
1422 */
1423 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1424 if (context != null
1425 && tree.encl == null
1426 && tree.def == null
1427 && !tree.type.getEnclosingType().hasTag(NONE)) {
1428 Type encl = tree.type.getEnclosingType();
1429 Type current = context.owner.enclClass().type;
1430 while (!current.hasTag(NONE)) {
1431 if (current.tsym.isSubClass(encl.tsym, types)) {
1432 return true;
1433 }
1434 current = current.getEnclosingType();
1435 }
1436 return false;
1437 } else {
1438 return false;
1439 }
1440 }
1442 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1443 return new LambdaTranslationContext(tree);
1444 }
1446 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1447 return new ReferenceTranslationContext(tree);
1448 }
1450 private class Frame {
1451 final JCTree tree;
1452 List<Symbol> locals;
1454 public Frame(JCTree tree) {
1455 this.tree = tree;
1456 }
1458 void addLocal(Symbol sym) {
1459 if (locals == null) {
1460 locals = List.nil();
1461 }
1462 locals = locals.prepend(sym);
1463 }
1464 }
1466 /**
1467 * This class is used to store important information regarding translation of
1468 * lambda expression/method references (see subclasses).
1469 */
1470 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1472 /** the underlying (untranslated) tree */
1473 T tree;
1475 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1476 Symbol owner;
1478 /** the depth of this lambda expression in the frame stack */
1479 int depth;
1481 /** the enclosing translation context (set for nested lambdas/mref) */
1482 TranslationContext<?> prev;
1484 TranslationContext(T tree) {
1485 this.tree = tree;
1486 this.owner = owner();
1487 this.depth = frameStack.size() - 1;
1488 this.prev = context();
1489 }
1491 /** does this functional expression need to be created using alternate metafactory? */
1492 boolean needsAltMetafactory() {
1493 return (tree.targets.length() > 1 ||
1494 isSerializable());
1495 }
1497 /** does this functional expression require serialization support? */
1498 boolean isSerializable() {
1499 for (Symbol target : tree.targets) {
1500 if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
1501 return true;
1502 }
1503 }
1504 return false;
1505 }
1506 }
1508 /**
1509 * This class retains all the useful information about a lambda expression;
1510 * the contents of this class are filled by the LambdaAnalyzer visitor,
1511 * and the used by the main translation routines in order to adjust references
1512 * to captured locals/members, etc.
1513 */
1514 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1516 /** variable in the enclosing context to which this lambda is assigned */
1517 Symbol self;
1519 /** map from original to translated lambda parameters */
1520 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
1522 /** map from original to translated lambda locals */
1523 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
1525 /** map from variables in enclosing scope to translated synthetic parameters */
1526 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>();
1528 /** map from class symbols to translated synthetic parameters (for captured member access) */
1529 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
1531 /** map from original to translated lambda locals */
1532 Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
1534 /** the synthetic symbol for the method hoisting the translated lambda */
1535 Symbol translatedSym;
1537 List<JCVariableDecl> syntheticParams;
1539 LambdaTranslationContext(JCLambda tree) {
1540 super(tree);
1541 Frame frame = frameStack.head;
1542 if (frame.tree.hasTag(VARDEF)) {
1543 self = ((JCVariableDecl)frame.tree).sym;
1544 }
1545 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
1546 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
1547 }
1549 /**
1550 * Translate a symbol of a given kind into something suitable for the
1551 * synthetic lambda body
1552 */
1553 Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
1554 switch (skind) {
1555 case CAPTURED_THIS:
1556 return sym; // self represented
1557 case TYPE_VAR:
1558 // Just erase the type var
1559 return new VarSymbol(sym.flags(), names.fromString(name),
1560 types.erasure(sym.type), sym.owner);
1561 default:
1562 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1563 }
1564 }
1566 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1567 Map<Symbol, Symbol> transMap = null;
1568 String preferredName;
1569 switch (skind) {
1570 case CAPTURED_THIS:
1571 transMap = capturedThis;
1572 preferredName = "encl$" + capturedThis.size();
1573 break;
1574 case CAPTURED_VAR:
1575 transMap = capturedLocals;
1576 preferredName = "cap$" + capturedLocals.size();
1577 break;
1578 case LOCAL_VAR:
1579 transMap = lambdaLocals;
1580 preferredName = sym.name.toString();
1581 break;
1582 case PARAM:
1583 transMap = lambdaParams;
1584 preferredName = sym.name.toString();
1585 break;
1586 case TYPE_VAR:
1587 transMap = typeVars;
1588 preferredName = sym.name.toString();
1589 break;
1590 default: throw new AssertionError();
1591 }
1592 if (!transMap.containsKey(sym)) {
1593 transMap.put(sym, translate(preferredName, sym, skind));
1594 }
1595 }
1597 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
1598 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
1599 for (LambdaSymbolKind skind : skinds) {
1600 switch (skind) {
1601 case CAPTURED_THIS:
1602 translationMap.putAll(capturedThis);
1603 break;
1604 case CAPTURED_VAR:
1605 translationMap.putAll(capturedLocals);
1606 break;
1607 case LOCAL_VAR:
1608 translationMap.putAll(lambdaLocals);
1609 break;
1610 case PARAM:
1611 translationMap.putAll(lambdaParams);
1612 break;
1613 case TYPE_VAR:
1614 translationMap.putAll(typeVars);
1615 break;
1616 default: throw new AssertionError();
1617 }
1618 }
1619 return translationMap;
1620 }
1622 /**
1623 * The translatedSym is not complete/accurate until the analysis is
1624 * finished. Once the analysis is finished, the translatedSym is
1625 * "completed" -- updated with type information, access modifiers,
1626 * and full parameter list.
1627 */
1628 void complete() {
1629 if (syntheticParams != null) {
1630 return;
1631 }
1632 boolean inInterface = translatedSym.owner.isInterface();
1633 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1634 boolean needInstance = thisReferenced || inInterface;
1636 // If instance access isn't needed, make it static
1637 // Interface methods much be public default methods, otherwise make it private
1638 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
1639 (inInterface? PUBLIC | DEFAULT : PRIVATE);
1641 //compute synthetic params
1642 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
1644 // The signature of the method is augmented with the following
1645 // synthetic parameters:
1646 //
1647 // 1) reference to enclosing contexts captured by the lambda expression
1648 // 2) enclosing locals captured by the lambda expression
1649 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
1650 params.append(make.VarDef((VarSymbol) thisSym, null));
1651 }
1653 syntheticParams = params.toList();
1655 //prepend synthetic args to translated lambda method signature
1656 translatedSym.type = types.createMethodTypeWithParameters(
1657 generatedLambdaSig(),
1658 TreeInfo.types(syntheticParams));
1659 }
1661 Type generatedLambdaSig() {
1662 return types.erasure(tree.descriptorType);
1663 }
1664 }
1666 /**
1667 * This class retains all the useful information about a method reference;
1668 * the contents of this class are filled by the LambdaAnalyzer visitor,
1669 * and the used by the main translation routines in order to adjust method
1670 * references (i.e. in case a bridge is needed)
1671 */
1672 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
1674 final boolean isSuper;
1675 final Symbol bridgeSym;
1677 ReferenceTranslationContext(JCMemberReference tree) {
1678 super(tree);
1679 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
1680 this.bridgeSym = needsBridge()
1681 ? makeSyntheticMethod(isSuper ? 0 : STATIC,
1682 lambdaName().append(names.fromString("$bridge")), null,
1683 owner.enclClass())
1684 : null;
1685 }
1687 /**
1688 * Get the opcode associated with this method reference
1689 */
1690 int referenceKind() {
1691 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
1692 }
1694 boolean needsVarArgsConversion() {
1695 return tree.varargsElement != null;
1696 }
1698 /**
1699 * @return Is this an array operation like clone()
1700 */
1701 boolean isArrayOp() {
1702 return tree.sym.owner == syms.arrayClass;
1703 }
1705 boolean isPrivateConstructor() {
1706 return tree.sym.name == names.init &&
1707 (tree.sym.flags() & PRIVATE) != 0;
1708 }
1710 /**
1711 * Does this reference needs a bridge (i.e. var args need to be
1712 * expanded or "super" is used)
1713 */
1714 final boolean needsBridge() {
1715 return isSuper || needsVarArgsConversion() || isArrayOp() || isPrivateConstructor();
1716 }
1718 Type generatedRefSig() {
1719 return types.erasure(tree.sym.type);
1720 }
1722 Type bridgedRefSig() {
1723 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
1724 }
1725 }
1726 }
1727 // </editor-fold>
1729 enum LambdaSymbolKind {
1730 CAPTURED_VAR,
1731 CAPTURED_THIS,
1732 LOCAL_VAR,
1733 PARAM,
1734 TYPE_VAR;
1735 }
1737 /**
1738 * ****************************************************************
1739 * Signature Generation
1740 * ****************************************************************
1741 */
1743 private String methodSig(Type type) {
1744 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1745 sg.assembleSig(type);
1746 return sg.toString();
1747 }
1749 private String classSig(Type type) {
1750 L2MSignatureGenerator sg = new L2MSignatureGenerator();
1751 sg.assembleClassSig(type);
1752 return sg.toString();
1753 }
1755 /**
1756 * Signature Generation
1757 */
1758 private class L2MSignatureGenerator extends Types.SignatureGenerator {
1760 /**
1761 * An output buffer for type signatures.
1762 */
1763 StringBuilder sb = new StringBuilder();
1765 L2MSignatureGenerator() {
1766 super(types);
1767 }
1769 @Override
1770 protected void append(char ch) {
1771 sb.append(ch);
1772 }
1774 @Override
1775 protected void append(byte[] ba) {
1776 sb.append(new String(ba));
1777 }
1779 @Override
1780 protected void append(Name name) {
1781 sb.append(name.toString());
1782 }
1784 @Override
1785 public String toString() {
1786 return sb.toString();
1787 }
1788 }
1789 }