From 8a4ea09fa220f74f2236fc85e197eadf83b65875 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Fri, 13 Sep 2024 12:04:31 +0000 Subject: [PATCH] 8336492: Regression in lambda serialization Reviewed-by: vromero --- .../com/sun/tools/javac/code/Flags.java | 7 +- .../com/sun/tools/javac/comp/Attr.java | 61 +- .../sun/tools/javac/comp/CaptureScanner.java | 99 + .../sun/tools/javac/comp/LambdaToMethod.java | 1663 ++++++----------- .../com/sun/tools/javac/comp/Lower.java | 137 +- .../com/sun/tools/javac/comp/TransTypes.java | 1 + .../com/sun/tools/javac/jvm/ClassWriter.java | 2 +- .../com/sun/tools/javac/tree/JCTree.java | 2 + .../com/sun/tools/javac/tree/TreeScanner.java | 2 +- .../jdk/internal/vm/Continuation/Scoped.java | 22 +- .../javac/MethodParameters/LambdaTest.out | 4 +- .../javac/MethodParameters/LocalClassTest.out | 2 +- .../javac/T8019486/WrongLNTForLambdaTest.java | 6 +- .../EnclosingMethod/EnclosingMethodTest.java | 5 - .../tools/javac/lambda/CaptureVarOrder.java | 47 + .../lambda/SerializedLambdaInLocalClass.java | 95 + 16 files changed, 895 insertions(+), 1260 deletions(-) create mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java create mode 100644 test/langtools/tools/javac/lambda/CaptureVarOrder.java create mode 100644 test/langtools/tools/javac/lambda/SerializedLambdaInLocalClass.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 346aab18a60..a2937b73a14 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -285,7 +285,12 @@ public class Flags { /** * Flag that marks a synthetic method body for a lambda expression */ - public static final long LAMBDA_METHOD = 1L<<49; + public static final long LAMBDA_METHOD = 1L<<49; //MethodSymbols only + + /** + * Flag that marks a synthetic local capture field in a local/anon class + */ + public static final long LOCAL_CAPTURE_FIELD = 1L<<49; //VarSymbols only /** * Flag to control recursion in TransTypes diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 44ccbade4a9..07a136c1700 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -182,6 +182,7 @@ public class Attr extends JCTree.Visitor { unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType); unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType); recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext); + initBlockType = new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass); } /** Switch: reifiable types in instanceof enabled? @@ -628,6 +629,7 @@ public class Attr extends JCTree.Visitor { final ResultInfo unknownTypeInfo; final ResultInfo unknownTypeExprInfo; final ResultInfo recoveryInfo; + final MethodType initBlockType; Type pt() { return resultInfo.pt; @@ -1421,7 +1423,7 @@ public class Attr extends JCTree.Visitor { // created BLOCK-method. Symbol fakeOwner = new MethodSymbol(tree.flags | BLOCK | - env.info.scope.owner.flags() & STRICTFP, names.empty, null, + env.info.scope.owner.flags() & STRICTFP, names.empty, initBlockType, env.info.scope.owner); final Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner))); @@ -3524,62 +3526,26 @@ public class Attr extends JCTree.Visitor { } } - /* Map to hold 'fake' clinit methods. If a lambda is used to initialize a - * static field and that lambda has type annotations, these annotations will - * also be stored at these fake clinit methods. - * - * LambdaToMethod also use fake clinit methods so they can be reused. - * Also as LTM is a phase subsequent to attribution, the methods from - * clinits can be safely removed by LTM to save memory. - */ - private Map clinits = new HashMap<>(); - - public MethodSymbol removeClinit(ClassSymbol sym) { - return clinits.remove(sym); - } - /* This method returns an environment to be used to attribute a lambda * expression. * * The owner of this environment is a method symbol. If the current owner - * is not a method, for example if the lambda is used to initialize - * a field, then if the field is: - * - * - an instance field, we use the first constructor. - * - a static field, we create a fake clinit method. + * is not a method (e.g. if the lambda occurs in a field initializer), then + * a synthetic method symbol owner is created. */ public Env lambdaEnv(JCLambda that, Env env) { Env lambdaEnv; Symbol owner = env.info.scope.owner; if (owner.kind == VAR && owner.owner.kind == TYP) { - //field initializer + // If the lambda is nested in a field initializer, we need to create a fake init method. + // Uniqueness of this symbol is not important (as e.g. annotations will be added on the + // init symbol's owner). ClassSymbol enclClass = owner.enclClass(); - Symbol newScopeOwner = env.info.scope.owner; - /* if the field isn't static, then we can get the first constructor - * and use it as the owner of the environment. This is what - * LTM code is doing to look for type annotations so we are fine. - */ - if ((owner.flags() & STATIC) == 0) { - for (Symbol s : enclClass.members_field.getSymbolsByName(names.init)) { - newScopeOwner = s; - break; - } - } else { - /* if the field is static then we need to create a fake clinit - * method, this method can later be reused by LTM. - */ - MethodSymbol clinit = clinits.get(enclClass); - if (clinit == null) { - Type clinitType = new MethodType(List.nil(), - syms.voidType, List.nil(), syms.methodClass); - clinit = new MethodSymbol(STATIC | SYNTHETIC | PRIVATE, - names.clinit, clinitType, enclClass); - clinit.params = List.nil(); - clinits.put(enclClass, clinit); - } - newScopeOwner = clinit; - } - lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared(newScopeOwner))); + Name initName = owner.isStatic() ? names.clinit : names.init; + MethodSymbol initSym = new MethodSymbol(BLOCK | (owner.isStatic() ? STATIC : 0) | SYNTHETIC | PRIVATE, + initName, initBlockType, enclClass); + initSym.params = List.nil(); + lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared(initSym))); } else { lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup())); } @@ -3936,6 +3902,7 @@ public class Attr extends JCTree.Visitor { inferenceContext -> setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType), inferenceContext.asInstType(primaryTarget), checkContext)); } else { + fExpr.owner = env.info.scope.owner; if (pt.hasTag(CLASS)) { fExpr.target = primaryTarget; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java new file mode 100644 index 00000000000..ef8a3ff584d --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.comp; + +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.List; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.SequencedSet; +import java.util.Set; + +import static com.sun.tools.javac.code.Kinds.Kind.MTH; +import static com.sun.tools.javac.code.Kinds.Kind.VAR; + +/** + * A visitor which collects the set of local variables "captured" by a given tree. + */ +public class CaptureScanner extends TreeScanner { + + /** + * The tree under analysis. + */ + private final JCTree tree; + + /** + * The set of local variable declarations encountered in the tree under analysis. + */ + private final Set seenVars = new HashSet<>(); + + /** + * The set of captured local variables accessed from within the tree under analysis. + */ + private final SequencedSet fvs = new LinkedHashSet<>(); + + public CaptureScanner(JCTree ownerTree) { + this.tree = ownerTree; + } + + @Override + public void visitIdent(JCTree.JCIdent tree) { + Symbol sym = tree.sym; + if (sym.kind == VAR && sym.owner.kind == MTH) { + Symbol.VarSymbol vsym = (Symbol.VarSymbol) sym; + if (vsym.getConstValue() == null && !seenVars.contains(vsym)) { + addFreeVar(vsym); + } + } + } + + /** + * Add free variable to fvs list unless it is already there. + */ + protected void addFreeVar(Symbol.VarSymbol v) { + fvs.add(v); + } + + @Override + public void visitVarDef(JCTree.JCVariableDecl tree) { + if (tree.sym.owner.kind == MTH) { + seenVars.add(tree.sym); + } + super.visitVarDef(tree); + } + + /** + * Obtains the list of captured local variables in the tree under analysis. + */ + List analyzeCaptures() { + scan(tree); + return List.from(fvs); + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 2c3d79c0ab6..adfc3ceaa0d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -25,50 +25,87 @@ package com.sun.tools.javac.comp; -import com.sun.tools.javac.code.Symbol.MethodHandleSymbol; -import com.sun.tools.javac.code.Types.SignatureGenerator.InvalidSignatureException; -import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; -import com.sun.tools.javac.resources.CompilerProperties.Errors; -import com.sun.tools.javac.resources.CompilerProperties.Fragments; -import com.sun.tools.javac.tree.*; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; -import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.tree.TreeTranslator; import com.sun.tools.javac.code.Attribute; +import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; +import com.sun.tools.javac.code.Symbol.MethodHandleSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Types; -import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*; +import com.sun.tools.javac.code.Types.SignatureGenerator.InvalidSignatureException; +import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Notes; -import com.sun.tools.javac.util.*; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCBreak; +import com.sun.tools.javac.tree.JCTree.JCCase; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCFunctionalExpression; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCLambda; +import com.sun.tools.javac.tree.JCTree.JCMemberReference; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCReturn; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCSwitch; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.Tag; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeTranslator; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.DiagnosticSource; +import com.sun.tools.javac.util.InvalidUtfException; +import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Options; -import java.util.EnumMap; +import javax.lang.model.element.ElementKind; +import java.lang.invoke.LambdaMetafactory; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; -import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; -import static com.sun.tools.javac.code.Flags.*; -import static com.sun.tools.javac.code.Kinds.Kind.*; -import static com.sun.tools.javac.code.TypeTag.*; -import static com.sun.tools.javac.tree.JCTree.Tag.*; - -import javax.lang.model.element.ElementKind; - -import com.sun.tools.javac.main.Option; +import static com.sun.tools.javac.code.Flags.ABSTRACT; +import static com.sun.tools.javac.code.Flags.BLOCK; +import static com.sun.tools.javac.code.Flags.DEFAULT; +import static com.sun.tools.javac.code.Flags.FINAL; +import static com.sun.tools.javac.code.Flags.INTERFACE; +import static com.sun.tools.javac.code.Flags.LAMBDA_METHOD; +import static com.sun.tools.javac.code.Flags.LOCAL_CAPTURE_FIELD; +import static com.sun.tools.javac.code.Flags.PARAMETER; +import static com.sun.tools.javac.code.Flags.PRIVATE; +import static com.sun.tools.javac.code.Flags.STATIC; +import static com.sun.tools.javac.code.Flags.STRICTFP; +import static com.sun.tools.javac.code.Flags.SYNTHETIC; +import static com.sun.tools.javac.code.Kinds.Kind.MTH; +import static com.sun.tools.javac.code.Kinds.Kind.TYP; +import static com.sun.tools.javac.code.Kinds.Kind.VAR; +import static com.sun.tools.javac.code.TypeTag.BOT; +import static com.sun.tools.javac.code.TypeTag.VOID; /** * This pass desugars lambda expressions into static methods @@ -80,31 +117,28 @@ import com.sun.tools.javac.main.Option; */ public class LambdaToMethod extends TreeTranslator { - private Attr attr; - private JCDiagnostic.Factory diags; - private Log log; - private Lower lower; - private Names names; - private Symtab syms; - private Resolve rs; - private Operators operators; + private final Attr attr; + private final JCDiagnostic.Factory diags; + private final Log log; + private final Lower lower; + private final Names names; + private final Symtab syms; + private final Resolve rs; + private final Operators operators; private TreeMaker make; - private Types types; - private TransTypes transTypes; + private final Types types; + private final TransTypes transTypes; private Env attrEnv; - /** the analyzer scanner */ - private LambdaAnalyzerPreprocessor analyzer; - - /** map from lambda trees to translation contexts */ - private Map> contextMap; - - /** current translation context (visitor argument) */ - private TranslationContext context; - /** info about the current class being processed */ private KlassInfo kInfo; + /** translation context of the current lambda expression */ + private LambdaTranslationContext lambdaContext; + + /** the variable whose initializer is pending */ + private VarSymbol pendingVar; + /** dump statistics about lambda code generation */ private final boolean dumpLambdaToMethodStats; @@ -121,13 +155,13 @@ public class LambdaToMethod extends TreeTranslator { private final boolean deduplicateLambdas; /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */ - public static final int FLAG_SERIALIZABLE = 1 << 0; + public static final int FLAG_SERIALIZABLE = LambdaMetafactory.FLAG_SERIALIZABLE; /** Flag for alternate metafactories indicating the lambda object has multiple targets */ - public static final int FLAG_MARKERS = 1 << 1; + public static final int FLAG_MARKERS = LambdaMetafactory.FLAG_MARKERS; /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */ - public static final int FLAG_BRIDGES = 1 << 2; + public static final int FLAG_BRIDGES = LambdaMetafactory.FLAG_BRIDGES; // protected static final Context.Key unlambdaKey = new Context.Key<>(); @@ -151,18 +185,17 @@ public class LambdaToMethod extends TreeTranslator { make = TreeMaker.instance(context); types = Types.instance(context); transTypes = TransTypes.instance(context); - analyzer = new LambdaAnalyzerPreprocessor(); Options options = Options.instance(context); dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats"); attr = Attr.instance(context); forceSerializable = options.isSet("forceSerializable"); boolean lineDebugInfo = - options.isUnset(Option.G_CUSTOM) || - options.isSet(Option.G_CUSTOM, "lines"); + options.isUnset(Option.G_CUSTOM) || + options.isSet(Option.G_CUSTOM, "lines"); boolean varDebugInfo = - options.isUnset(Option.G_CUSTOM) - ? options.isSet(Option.G) - : options.isSet(Option.G_CUSTOM, "vars"); + options.isUnset(Option.G_CUSTOM) + ? options.isSet(Option.G) + : options.isSet(Option.G_CUSTOM, "vars"); debugLinesOrVars = lineDebugInfo || varDebugInfo; verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication"); deduplicateLambdas = options.getBoolean("deduplicateLambdas", true); @@ -180,7 +213,6 @@ public class LambdaToMethod extends TreeTranslator { this.tree = tree; } - @Override public int hashCode() { int hashCode = this.hashCode; @@ -203,18 +235,18 @@ public class LambdaToMethod extends TreeTranslator { /** * list of methods to append */ - private ListBuffer appendedMethodList; + private ListBuffer appendedMethodList = new ListBuffer<>(); - private Map dedupedLambdas; + private final Map dedupedLambdas = new HashMap<>(); - private Map dynMethSyms = new HashMap<>(); + private final Map dynMethSyms = new HashMap<>(); /** * list of deserialization cases */ - private final Map> deserializeCases; + private final Map> deserializeCases = new HashMap<>(); - /** + /** * deserialize method symbol */ private final MethodSymbol deserMethodSym; @@ -226,11 +258,10 @@ public class LambdaToMethod extends TreeTranslator { private final JCClassDecl clazz; + private final Map syntheticNames = new HashMap<>(); + private KlassInfo(JCClassDecl clazz) { this.clazz = clazz; - appendedMethodList = new ListBuffer<>(); - dedupedLambdas = new HashMap<>(); - deserializeCases = new HashMap<>(); MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType, List.nil(), syms.methodClass); deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym); @@ -241,62 +272,50 @@ public class LambdaToMethod extends TreeTranslator { private void addMethod(JCTree decl) { appendedMethodList = appendedMethodList.prepend(decl); } - } - // - @Override - public T translate(T tree) { - TranslationContext newContext = contextMap.get(tree); - return translate(tree, newContext != null ? newContext : context); - } - - T translate(T tree, TranslationContext newContext) { - TranslationContext prevContext = context; - try { - context = newContext; - return super.translate(tree); - } - finally { - context = prevContext; + int syntheticNameIndex(StringBuilder buf, int start) { + String temp = buf.toString(); + Integer count = syntheticNames.get(temp); + if (count == null) { + count = start; + } + syntheticNames.put(temp, count + 1); + return count; } } - List translate(List trees, TranslationContext newContext) { - ListBuffer buf = new ListBuffer<>(); - for (T tree : trees) { - buf.append(translate(tree, newContext)); - } - return buf.toList(); - } - + // public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { this.make = make; this.attrEnv = env; - this.context = null; - this.contextMap = new HashMap<>(); - cdef = analyzer.analyzeAndPreprocessClass((JCClassDecl) cdef); return translate(cdef); } - // - // /** * Visit a class. * Maintain the translatedMethodList across nested classes. * Append the translatedMethodList to the class after it is translated. - * @param tree */ @Override public void visitClassDef(JCClassDecl tree) { KlassInfo prevKlassInfo = kInfo; + DiagnosticSource prevSource = log.currentSource(); + LambdaTranslationContext prevLambdaContext = lambdaContext; + VarSymbol prevPendingVar = pendingVar; try { kInfo = new KlassInfo(tree); + log.useSource(tree.sym.sourcefile); + lambdaContext = null; + pendingVar = null; super.visitClassDef(tree); + if (prevLambdaContext != null) { + tree.sym.owner = prevLambdaContext.translatedSym; + } if (!kInfo.deserializeCases.isEmpty()) { int prevPos = make.pos; try { make.at(tree); - kInfo.addMethod(makeDeserializeMethod(tree.sym)); + kInfo.addMethod(makeDeserializeMethod()); } finally { make.at(prevPos); } @@ -310,6 +329,9 @@ public class LambdaToMethod extends TreeTranslator { result = tree; } finally { kInfo = prevKlassInfo; + log.useSource(prevSource.getFile()); + lambdaContext = prevLambdaContext; + pendingVar = prevPendingVar; } } @@ -317,11 +339,10 @@ public class LambdaToMethod extends TreeTranslator { * Translate a lambda into a method to be inserted into the class. * Then replace the lambda site with an invokedynamic call of to lambda * meta-factory, which will use the lambda method. - * @param tree */ @Override public void visitLambda(JCLambda tree) { - LambdaTranslationContext localContext = (LambdaTranslationContext)context; + LambdaTranslationContext localContext = new LambdaTranslationContext(tree); MethodSymbol sym = localContext.translatedSym; MethodType lambdaType = (MethodType) sym.type; @@ -332,26 +353,26 @@ public class LambdaToMethod extends TreeTranslator { lambda and attach it to the implementation method. */ - Symbol owner = localContext.owner; + Symbol owner = tree.owner; apportionTypeAnnotations(tree, owner::getRawTypeAttributes, owner::setTypeAttributes, sym::setTypeAttributes); - - boolean init; - if ((init = (owner.name == names.init)) || owner.name == names.clinit) { - owner = owner.owner; + final long ownerFlags = owner.flags(); + if ((ownerFlags & Flags.BLOCK) != 0) { + ClassSymbol cs = (ClassSymbol) owner.owner; + boolean isStaticInit = (ownerFlags & Flags.STATIC) != 0; apportionTypeAnnotations(tree, - init ? owner::getInitTypeAttributes : owner::getClassInitTypeAttributes, - init ? owner::setInitTypeAttributes : owner::setClassInitTypeAttributes, + isStaticInit ? cs::getClassInitTypeAttributes : cs::getInitTypeAttributes, + isStaticInit ? cs::setClassInitTypeAttributes : cs::setInitTypeAttributes, sym::appendUniqueTypeAttributes); } - if (localContext.self != null && localContext.self.getKind() == ElementKind.FIELD) { - owner = localContext.self; + + if (pendingVar != null && pendingVar.getKind() == ElementKind.FIELD) { apportionTypeAnnotations(tree, - owner::getRawTypeAttributes, - owner::setTypeAttributes, + pendingVar::getRawTypeAttributes, + pendingVar::setTypeAttributes, sym::appendUniqueTypeAttributes); } } @@ -363,35 +384,13 @@ public class LambdaToMethod extends TreeTranslator { List.nil(), localContext.syntheticParams, lambdaType.getThrownTypes() == null ? - List.nil() : - make.Types(lambdaType.getThrownTypes()), + List.nil() : + make.Types(lambdaType.getThrownTypes()), null, null); lambdaDecl.sym = sym; lambdaDecl.type = lambdaType; - //translate lambda body - //As the lambda body is translated, all references to lambda locals, - //captured variables, enclosing members are adjusted accordingly - //to refer to the static method parameters (rather than i.e. accessing - //captured members directly). - lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); - - boolean dedupe = false; - if (deduplicateLambdas && !debugLinesOrVars && !localContext.isSerializable()) { - DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body); - DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda); - if (existing != null) { - sym = existing.symbol; - dedupe = true; - if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym)); - } - } - if (!dedupe) { - //Add the method to the list of methods to be added to this class. - kInfo.addMethod(lambdaDecl); - } - //now that we have generated a method for the lambda expression, //we can translate the lambda into a method reference pointing to the newly //created method. @@ -408,46 +407,72 @@ public class LambdaToMethod extends TreeTranslator { if (!sym.isStatic()) { syntheticInits.append(makeThis( sym.owner.enclClass().asType(), - localContext.owner.enclClass())); + tree.owner.enclClass())); } //add captured locals - for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { - if (fv != localContext.self) { - JCExpression captured_local = make.Ident(fv).setType(fv.type); - syntheticInits.append(captured_local); - } + for (Symbol fv : localContext.capturedVars) { + JCExpression captured_local = make.Ident(fv).setType(fv.type); + syntheticInits.append(captured_local); } //then, determine the arguments to the indy call - List indy_args = translate(syntheticInits.toList(), localContext.prev); + List indy_args = translate(syntheticInits.toList()); + + LambdaTranslationContext prevLambdaContext = lambdaContext; + try { + lambdaContext = localContext; + //translate lambda body + //As the lambda body is translated, all references to lambda locals, + //captured variables, enclosing members are adjusted accordingly + //to refer to the static method parameters (rather than i.e. accessing + //captured members directly). + lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); + } finally { + lambdaContext = prevLambdaContext; + } + + boolean dedupe = false; + if (deduplicateLambdas && !debugLinesOrVars && !isSerializable(tree)) { + DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body); + DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda); + if (existing != null) { + sym = existing.symbol; + dedupe = true; + if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym)); + } + } + if (!dedupe) { + //Add the method to the list of methods to be added to this class. + kInfo.addMethod(lambdaDecl); + } //convert to an invokedynamic call - result = makeMetafactoryIndyCall(context, sym.asHandle(), indy_args); + result = makeMetafactoryIndyCall(tree, sym.asHandle(), localContext.translatedSym, indy_args); } // where - // Reassign type annotations from the source that should really belong to the lambda - private void apportionTypeAnnotations(JCLambda tree, - Supplier> source, - Consumer> owner, - Consumer> lambda) { + // Reassign type annotations from the source that should really belong to the lambda + private void apportionTypeAnnotations(JCLambda tree, + Supplier> source, + Consumer> owner, + Consumer> lambda) { - ListBuffer ownerTypeAnnos = new ListBuffer<>(); - ListBuffer lambdaTypeAnnos = new ListBuffer<>(); + ListBuffer ownerTypeAnnos = new ListBuffer<>(); + ListBuffer lambdaTypeAnnos = new ListBuffer<>(); - for (Attribute.TypeCompound tc : source.get()) { - if (tc.position.onLambda == tree) { - lambdaTypeAnnos.append(tc); - } else { - ownerTypeAnnos.append(tc); - } - } - if (lambdaTypeAnnos.nonEmpty()) { - owner.accept(ownerTypeAnnos.toList()); - lambda.accept(lambdaTypeAnnos.toList()); + for (Attribute.TypeCompound tc : source.get()) { + if (tc.position.onLambda == tree) { + lambdaTypeAnnos.append(tc); + } else { + ownerTypeAnnos.append(tc); } } + if (lambdaTypeAnnos.nonEmpty()) { + owner.accept(ownerTypeAnnos.toList()); + lambda.accept(lambdaTypeAnnos.toList()); + } + } private JCIdent makeThis(Type type, Symbol owner) { VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, @@ -460,65 +485,46 @@ public class LambdaToMethod extends TreeTranslator { /** * Translate a method reference into an invokedynamic call to the * meta-factory. - * @param tree */ @Override public void visitReference(JCMemberReference tree) { - ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; - //first determine the method symbol to be used to generate the sam instance //this is either the method reference symbol, or the bridged reference symbol MethodSymbol refSym = (MethodSymbol)tree.sym; //the qualifying expression is treated as a special captured arg - JCExpression init; - switch(tree.kind) { - - case IMPLICIT_INNER: /** Inner :: new */ - case SUPER: /** super :: instMethod */ - init = makeThis( - localContext.owner.enclClass().asType(), - localContext.owner.enclClass()); - break; - - case BOUND: /** Expr :: instMethod */ - init = transTypes.coerce(attrEnv, tree.getQualifierExpression(), - types.erasure(tree.sym.owner.type)); - init = attr.makeNullCheck(init); - break; - - case UNBOUND: /** Type :: instMethod */ - case STATIC: /** Type :: staticMethod */ - case TOPLEVEL: /** Top level :: new */ - case ARRAY_CTOR: /** ArrayType :: new */ - init = null; - break; - - default: - throw new InternalError("Should not have an invalid kind"); - } - - List indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev); + JCExpression init = switch (tree.kind) { + case IMPLICIT_INNER, /* Inner :: new */ + SUPER -> /* super :: instMethod */ + makeThis(tree.owner.enclClass().asType(), tree.owner.enclClass()); + case BOUND -> /* Expr :: instMethod */ + attr.makeNullCheck(transTypes.coerce(attrEnv, tree.getQualifierExpression(), + types.erasure(tree.sym.owner.type))); + case UNBOUND, /* Type :: instMethod */ + STATIC, /* Type :: staticMethod */ + TOPLEVEL, /* Top level :: new */ + ARRAY_CTOR -> /* ArrayType :: new */ + null; + }; + List indy_args = (init == null) ? + List.nil() : translate(List.of(init)); //build a sam instance using an indy call to the meta-factory - result = makeMetafactoryIndyCall(localContext, refSym.asHandle(), indy_args); + result = makeMetafactoryIndyCall(tree, refSym.asHandle(), refSym, indy_args); } /** * Translate identifiers within a lambda to the mapped identifier - * @param tree */ @Override public void visitIdent(JCIdent tree) { - if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { + if (lambdaContext == null) { super.visitIdent(tree); } else { int prevPos = make.pos; try { make.at(tree); - - LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; JCTree ltree = lambdaContext.translate(tree); if (ltree != null) { result = ltree; @@ -535,13 +541,18 @@ public class LambdaToMethod extends TreeTranslator { @Override public void visitVarDef(JCVariableDecl tree) { - LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; - if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { - tree.init = translate(tree.init); - tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); - result = tree; - } else { - super.visitVarDef(tree); + VarSymbol prevPendingVar = pendingVar; + try { + pendingVar = tree.sym; + if (lambdaContext != null) { + tree.sym = lambdaContext.addLocal(tree.sym); + tree.init = translate(tree.init); + result = tree; + } else { + super.visitVarDef(tree); + } + } finally { + pendingVar = prevPendingVar; } } @@ -609,7 +620,7 @@ public class LambdaToMethod extends TreeTranslator { if (isTarget_void && !isLambda_void) { //Void to void conversion: // { TYPE $loc = RET-EXPR; return; } - VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); + VarSymbol loc = new VarSymbol(SYNTHETIC, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); JCVariableDecl varDef = make.VarDef(loc, tree.expr); result = make.Block(0, List.of(varDef, make.Return(null))); } else { @@ -628,7 +639,7 @@ public class LambdaToMethod extends TreeTranslator { return trans_block; } - private JCMethodDecl makeDeserializeMethod(Symbol kSym) { + private JCMethodDecl makeDeserializeMethod() { ListBuffer cases = new ListBuffer<>(); ListBuffer breaks = new ListBuffer<>(); for (Map.Entry> entry : kInfo.deserializeCases.entrySet()) { @@ -644,16 +655,16 @@ public class LambdaToMethod extends TreeTranslator { JCBlock body = make.Block(0L, List.of( sw, make.Throw(makeNewClass( - syms.illegalArgumentExceptionType, - List.of(make.Literal("Invalid lambda deserialization")))))); + syms.illegalArgumentExceptionType, + List.of(make.Literal("Invalid lambda deserialization")))))); JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()), - names.deserializeLambda, - make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym), - List.nil(), - List.of(make.VarDef(kInfo.deserParamSym, null)), - List.nil(), - body, - null); + names.deserializeLambda, + make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym), + List.nil(), + List.of(make.VarDef(kInfo.deserParamSym, null)), + List.nil(), + body, + null); deser.sym = kInfo.deserMethodSym; deser.type = kInfo.deserMethodSym.type; //System.err.printf("DESER: '%s'\n", deser); @@ -667,7 +678,7 @@ public class LambdaToMethod extends TreeTranslator { */ JCNewClass makeNewClass(Type ctype, List args, Symbol cons) { JCNewClass tree = make.NewClass(null, - null, make.QualIdent(ctype.tsym), args, null); + null, make.QualIdent(ctype.tsym), args, null); tree.constructor = cons; tree.type = ctype; return tree; @@ -680,7 +691,7 @@ public class LambdaToMethod extends TreeTranslator { JCNewClass makeNewClass(Type ctype, List args) { return makeNewClass(ctype, args, rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil())); - } + } private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym, DiagnosticPosition pos, List staticArgs, MethodType indyType) { @@ -711,17 +722,17 @@ public class LambdaToMethod extends TreeTranslator { } JCStatement stmt = make.If( deserTest(deserTest(deserTest(deserTest(deserTest( - kindTest, - "getFunctionalInterfaceClass", functionalInterfaceClass), - "getFunctionalInterfaceMethodName", functionalInterfaceMethodName), - "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature), - "getImplClass", implClass), - "getImplMethodSignature", implMethodSignature), + kindTest, + "getFunctionalInterfaceClass", functionalInterfaceClass), + "getFunctionalInterfaceMethodName", functionalInterfaceMethodName), + "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature), + "getImplClass", implClass), + "getImplMethodSignature", implMethodSignature), make.Return(makeIndyCall( - pos, - syms.lambdaMetafactory, - names.altMetafactory, - staticArgs, indyType, serArgs.toList(), samSym.name)), + pos, + syms.lambdaMetafactory, + names.altMetafactory, + staticArgs, indyType, serArgs.toList(), samSym.name)), null); ListBuffer stmts = kInfo.deserializeCases.get(implMethodName); if (stmts == null) { @@ -742,8 +753,8 @@ public class LambdaToMethod extends TreeTranslator { } private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) { - JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2); - testExpr.operator = operators.resolveBinary(testExpr, JCTree.Tag.EQ, argType, argType); + JCBinary testExpr = make.Binary(Tag.EQ, arg1, arg2); + testExpr.operator = operators.resolveBinary(testExpr, Tag.EQ, argType, argType); testExpr.setType(syms.booleanType); return testExpr; } @@ -756,8 +767,8 @@ public class LambdaToMethod extends TreeTranslator { make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt), List.of(make.Literal(lit))); eqtest.setType(syms.booleanType); - JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest); - compound.operator = operators.resolveBinary(compound, JCTree.Tag.AND, syms.booleanType, syms.booleanType); + JCBinary compound = make.Binary(Tag.AND, prev, eqtest); + compound.operator = operators.resolveBinary(compound, Tag.AND, syms.booleanType, syms.booleanType); compound.setType(syms.booleanType); return compound; } @@ -770,9 +781,9 @@ public class LambdaToMethod extends TreeTranslator { MethodType getmt = new MethodType(argTypes, type, List.nil(), syms.methodClass); Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.nil()); return make.Apply( - List.nil(), - make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt), - args).setType(type); + List.nil(), + make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt), + args).setType(type); } /** @@ -782,29 +793,20 @@ public class LambdaToMethod extends TreeTranslator { return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner); } - /** - * Create new synthetic variable with given flags, name, type, owner - */ - private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { - return new VarSymbol(flags | SYNTHETIC, name, type, owner); - } - - // - private MethodType typeToMethodType(Type mt) { Type type = types.erasure(mt); return new MethodType(type.getParameterTypes(), - type.getReturnType(), - type.getThrownTypes(), - syms.methodClass); + type.getReturnType(), + type.getThrownTypes(), + syms.methodClass); } /** * Generate an indy method call to the meta factory */ - private JCExpression makeMetafactoryIndyCall(TranslationContext context, - MethodHandleSymbol refSym, List indy_args) { - JCFunctionalExpression tree = context.tree; + private JCExpression makeMetafactoryIndyCall(JCFunctionalExpression tree, + MethodHandleSymbol refSym, MethodSymbol nonDedupedRefSym, + List indy_args) { //determine the static bsm args MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym); List staticArgs = List.of( @@ -824,10 +826,17 @@ public class LambdaToMethod extends TreeTranslator { List.nil(), syms.methodClass); - Name metafactoryName = context.needsAltMetafactory() ? + List bridges = bridges(tree); + boolean isSerializable = isSerializable(tree); + boolean needsAltMetafactory = tree.target.isIntersection() || + isSerializable || bridges.length() > 1; + + dumpStats(tree, needsAltMetafactory, nonDedupedRefSym); + + Name metafactoryName = needsAltMetafactory ? names.altMetafactory : names.metafactory; - if (context.needsAltMetafactory()) { + if (needsAltMetafactory) { ListBuffer markers = new ListBuffer<>(); List targets = tree.target.isIntersection() ? types.directSupertypes(tree.target) : @@ -835,14 +844,14 @@ public class LambdaToMethod extends TreeTranslator { for (Type t : targets) { t = types.erasure(t); if (t.tsym != syms.serializableType.tsym && - t.tsym != tree.type.tsym && - t.tsym != syms.objectType.tsym) { + t.tsym != tree.type.tsym && + t.tsym != syms.objectType.tsym) { markers.append(t); } } - int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0; + int flags = isSerializable ? FLAG_SERIALIZABLE : 0; boolean hasMarkers = markers.nonEmpty(); - boolean hasBridges = context.bridges.nonEmpty(); + boolean hasBridges = bridges.nonEmpty(); if (hasMarkers) { flags |= FLAG_MARKERS; } @@ -855,15 +864,15 @@ public class LambdaToMethod extends TreeTranslator { staticArgs = staticArgs.appendList(List.convert(LoadableConstant.class, markers.toList())); } if (hasBridges) { - staticArgs = staticArgs.append(LoadableConstant.Int(context.bridges.length() - 1)); - for (Symbol s : context.bridges) { + staticArgs = staticArgs.append(LoadableConstant.Int(bridges.length() - 1)); + for (Symbol s : bridges) { Type s_erasure = s.erasure(types); if (!types.isSameType(s_erasure, samSym.erasure(types))) { staticArgs = staticArgs.append(((MethodType)s.erasure(types))); } } } - if (context.isSerializable()) { + if (isSerializable) { int prevPos = make.pos; try { make.at(kInfo.clazz); @@ -889,18 +898,18 @@ public class LambdaToMethod extends TreeTranslator { try { make.at(pos); List bsm_staticArgs = List.of(syms.methodHandleLookupType, - syms.stringType, - syms.methodTypeType).appendList(staticArgs.map(types::constantType)); + syms.stringType, + syms.methodTypeType).appendList(staticArgs.map(types::constantType)); MethodSymbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, bsmName, bsm_staticArgs, List.nil()); DynamicMethodSymbol dynSym = new DynamicMethodSymbol(methName, - syms.noSymbol, - bsm.asHandle(), - indyType, - staticArgs.toArray(new LoadableConstant[staticArgs.length()])); + syms.noSymbol, + bsm.asHandle(), + indyType, + staticArgs.toArray(new LoadableConstant[staticArgs.length()])); JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); DynamicMethodSymbol existing = kInfo.dynMethSyms.putIfAbsent( dynSym.poolKey(types), dynSym); @@ -915,833 +924,337 @@ public class LambdaToMethod extends TreeTranslator { } } - // - /** - * This visitor collects information about translation of a lambda expression. - * More specifically, it keeps track of the enclosing contexts and captured locals - * accessed by the lambda being translated (as well as other useful info). - * It also translates away problems for LambdaToMethod. - */ - class LambdaAnalyzerPreprocessor extends TreeTranslator { + List bridges(JCFunctionalExpression tree) { + ClassSymbol csym = + types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE); + return types.functionalInterfaceBridges(csym); + } - /** the frame stack - used to reconstruct translation info about enclosing scopes */ - private List frameStack; - - /** - * keep the count of lambda expression (used to generate unambiguous - * names) - */ - private int lambdaCount = 0; - - /** - * keep the count of lambda expression defined in given context (used to - * generate unambiguous names for serializable lambdas) - */ - private class SyntheticMethodNameCounter { - private Map map = new HashMap<>(); - int getIndex(StringBuilder buf) { - String temp = buf.toString(); - Integer count = map.get(temp); - if (count == null) { - count = 0; - } - ++count; - map.put(temp, count); - return count; - } + /** does this functional expression require serialization support? */ + boolean isSerializable(JCFunctionalExpression tree) { + if (forceSerializable) { + return true; } - private SyntheticMethodNameCounter syntheticMethodNameCounts = - new SyntheticMethodNameCounter(); - - private Map localClassDefs; - - /** - * maps for fake clinit symbols to be used as owners of lambda occurring in - * a static var init context - */ - private Map clinits = new HashMap<>(); - - private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { - frameStack = List.nil(); - localClassDefs = new HashMap<>(); - return translate(tree); - } - - @Override - public void visitBlock(JCBlock tree) { - List prevStack = frameStack; - try { - if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { - frameStack = frameStack.prepend(new Frame(tree)); - } - super.visitBlock(tree); - } - finally { - frameStack = prevStack; - } - } - - @Override - public void visitClassDef(JCClassDecl tree) { - List prevStack = frameStack; - int prevLambdaCount = lambdaCount; - SyntheticMethodNameCounter prevSyntheticMethodNameCounts = - syntheticMethodNameCounts; - Map prevClinits = clinits; - DiagnosticSource prevSource = log.currentSource(); - try { - log.useSource(tree.sym.sourcefile); - lambdaCount = 0; - syntheticMethodNameCounts = new SyntheticMethodNameCounter(); - prevClinits = new HashMap<>(); - if (tree.sym.owner.kind == MTH) { - localClassDefs.put(tree.sym, tree); - } - if (directlyEnclosingLambda() != null) { - tree.sym.owner = owner(); - } - frameStack = frameStack.prepend(new Frame(tree)); - super.visitClassDef(tree); - } - finally { - log.useSource(prevSource.getFile()); - frameStack = prevStack; - lambdaCount = prevLambdaCount; - syntheticMethodNameCounts = prevSyntheticMethodNameCounts; - clinits = prevClinits; - } - } - - @Override - public void visitIdent(JCIdent tree) { - if (context() != null && lambdaIdentSymbolFilter(tree.sym)) { - if (tree.sym.kind == VAR && - tree.sym.owner.kind == MTH && - tree.type.constValue() == null) { - TranslationContext localContext = context(); - while (localContext != null) { - if (localContext.tree.getTag() == LAMBDA) { - JCTree block = capturedDecl(localContext.depth, tree.sym); - if (block == null) break; - ((LambdaTranslationContext)localContext) - .addSymbol(tree.sym, CAPTURED_VAR); - } - localContext = localContext.prev; - } - } else if (tree.sym.owner.kind == TYP) { - TranslationContext localContext = context(); - while (localContext != null && !localContext.owner.isStatic()) { - if (localContext.tree.hasTag(LAMBDA)) { - JCTree block = capturedDecl(localContext.depth, tree.sym); - if (block == null) break; - switch (block.getTag()) { - case CLASSDEF: - JCClassDecl cdecl = (JCClassDecl)block; - ((LambdaTranslationContext)localContext) - .addSymbol(cdecl.sym, CAPTURED_THIS); - break; - default: - Assert.error("bad block kind"); - } - } - localContext = localContext.prev; - } - } - } - super.visitIdent(tree); - } - - @Override - public void visitLambda(JCLambda tree) { - analyzeLambda(tree, tree.wasMethodReference ? "mref.stat.1" : "lambda.stat"); - } - - private LambdaTranslationContext analyzeLambda(JCLambda tree, String statKey) { - List prevStack = frameStack; - try { - LambdaTranslationContext context = new LambdaTranslationContext(tree); - frameStack = frameStack.prepend(new Frame(tree)); - for (JCVariableDecl param : tree.params) { - context.addSymbol(param.sym, PARAM); - frameStack.head.addLocal(param.sym); - } - contextMap.put(tree, context); - super.visitLambda(tree); - context.complete(); - if (dumpLambdaToMethodStats) { - log.note(tree, diags.noteKey(statKey, context.needsAltMetafactory(), context.translatedSym)); - } - return context; - } - finally { - frameStack = prevStack; - } - } - - @Override - public void visitMethodDef(JCMethodDecl tree) { - List prevStack = frameStack; - try { - frameStack = frameStack.prepend(new Frame(tree)); - super.visitMethodDef(tree); - } - finally { - frameStack = prevStack; - } - } - - /** - * Method references to local class constructors, may, if the local - * class references local variables, have implicit constructor - * parameters added in Lower; As a result, the invokedynamic bootstrap - * information added in the LambdaToMethod pass will have the wrong - * signature. Hooks between Lower and LambdaToMethod have been added to - * handle normal "new" in this case. This visitor converts potentially - * affected method references into a lambda containing a normal - * expression. - * - * @param tree - */ - @Override - public void visitReference(JCMemberReference tree) { - ReferenceTranslationContext rcontext = new ReferenceTranslationContext(tree); - contextMap.put(tree, rcontext); - super.visitReference(tree); - if (dumpLambdaToMethodStats) { - log.note(tree, Notes.MrefStat(rcontext.needsAltMetafactory(), null)); - } - } - - @Override - public void visitSelect(JCFieldAccess tree) { - if (context() != null && tree.sym.kind == VAR && - (tree.sym.name == names._this || - tree.sym.name == names._super)) { - // A select of this or super means, if we are in a lambda, - // we much have an instance context - TranslationContext localContext = context(); - while (localContext != null && !localContext.owner.isStatic()) { - if (localContext.tree.hasTag(LAMBDA)) { - JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); - if (clazz == null) break; - ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); - } - localContext = localContext.prev; - } - } - super.visitSelect(tree); - } - - @Override - public void visitVarDef(JCVariableDecl tree) { - TranslationContext context = context(); - if (context != null && context instanceof LambdaTranslationContext lambdaContext) { - for (Frame frame : frameStack) { - if (frame.tree.hasTag(VARDEF)) { - //skip variable frames inside a lambda: - continue; - } else if (frame.tree.hasTag(LAMBDA)) { - lambdaContext.addSymbol(tree.sym, LOCAL_VAR); - } else { - break; - } - } - // Check for type variables (including as type arguments). - // If they occur within class nested in a lambda, mark for erasure - Type type = tree.sym.asType(); - } - - List prevStack = frameStack; - try { - if (tree.sym.owner.kind == MTH) { - frameStack.head.addLocal(tree.sym); - } - frameStack = frameStack.prepend(new Frame(tree)); - super.visitVarDef(tree); - } - finally { - frameStack = prevStack; - } - } - - /** - * Return a valid owner given the current declaration stack - * (required to skip synthetic lambda symbols) - */ - private Symbol owner() { - return owner(false); - } - - @SuppressWarnings("fallthrough") - private Symbol owner(boolean skipLambda) { - List frameStack2 = frameStack; - while (frameStack2.nonEmpty()) { - switch (frameStack2.head.tree.getTag()) { - case VARDEF: - if (((JCVariableDecl)frameStack2.head.tree).sym.isDirectlyOrIndirectlyLocal()) { - frameStack2 = frameStack2.tail; - break; - } - JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; - return initSym(cdecl.sym, - ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC); - case BLOCK: - JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; - return initSym(cdecl2.sym, - ((JCBlock)frameStack2.head.tree).flags & STATIC); - case CLASSDEF: - return ((JCClassDecl)frameStack2.head.tree).sym; - case METHODDEF: - return ((JCMethodDecl)frameStack2.head.tree).sym; - case LAMBDA: - if (!skipLambda) - return ((LambdaTranslationContext)contextMap - .get(frameStack2.head.tree)).translatedSym; - default: - frameStack2 = frameStack2.tail; - } - } - Assert.error(); - return null; - } - - private Symbol initSym(ClassSymbol csym, long flags) { - boolean isStatic = (flags & STATIC) != 0; - if (isStatic) { - /* static clinits are generated in Gen, so we need to use a fake - * one. Attr creates a fake clinit method while attributing - * lambda expressions used as initializers of static fields, so - * let's use that one. - */ - MethodSymbol clinit = attr.removeClinit(csym); - if (clinit != null) { - clinits.put(csym, clinit); - return clinit; - } - - /* if no clinit is found at Attr, then let's try at clinits. - */ - clinit = (MethodSymbol)clinits.get(csym); - if (clinit == null) { - /* no luck, let's create a new one - */ - clinit = makePrivateSyntheticMethod(STATIC, - names.clinit, - new MethodType(List.nil(), syms.voidType, - List.nil(), syms.methodClass), - csym); - clinits.put(csym, clinit); - } - return clinit; - } else { - //get the first constructor and treat it as the instance init sym - for (Symbol s : csym.members_field.getSymbolsByName(names.init)) { - return s; - } - } - Assert.error("init not found"); - return null; - } - - private JCTree directlyEnclosingLambda() { - if (frameStack.isEmpty()) { - return null; - } - List frameStack2 = frameStack; - while (frameStack2.nonEmpty()) { - switch (frameStack2.head.tree.getTag()) { - case CLASSDEF: - case METHODDEF: - return null; - case LAMBDA: - return frameStack2.head.tree; - default: - frameStack2 = frameStack2.tail; - } - } - Assert.error(); - return null; - } - - private boolean inClassWithinLambda() { - if (frameStack.isEmpty()) { - return false; - } - List frameStack2 = frameStack; - boolean classFound = false; - while (frameStack2.nonEmpty()) { - switch (frameStack2.head.tree.getTag()) { - case LAMBDA: - return classFound; - case CLASSDEF: - classFound = true; - frameStack2 = frameStack2.tail; - break; - default: - frameStack2 = frameStack2.tail; - } - } - // No lambda - return false; - } - - /** - * Return the declaration corresponding to a symbol in the enclosing - * scope; the depth parameter is used to filter out symbols defined - * in nested scopes (which do not need to undergo capture). - */ - private JCTree capturedDecl(int depth, Symbol sym) { - Assert.check(sym.kind != TYP); - int currentDepth = frameStack.size() - 1; - for (Frame block : frameStack) { - switch (block.tree.getTag()) { - case CLASSDEF: - ClassSymbol clazz = ((JCClassDecl)block.tree).sym; - if (clazz.isSubClass(sym.enclClass(), types)) { - return currentDepth > depth ? null : block.tree; - } - break; - case VARDEF: - if ((((JCVariableDecl)block.tree).sym == sym && - sym.owner.kind == MTH) || //only locals are captured - (block.locals != null && block.locals.contains(sym))) { - return currentDepth > depth ? null : block.tree; - } - break; - case BLOCK: - case METHODDEF: - case LAMBDA: - if (block.locals != null && block.locals.contains(sym)) { - return currentDepth > depth ? null : block.tree; - } - break; - default: - Assert.error("bad decl kind " + block.tree.getTag()); - } - currentDepth--; - } - return null; - } - - private TranslationContext context() { - for (Frame frame : frameStack) { - TranslationContext context = contextMap.get(frame.tree); - if (context != null) { - return context; - } - } - return null; - } - - /** - * This is used to filter out those identifiers that needs to be adjusted - * when translating away lambda expressions - */ - private boolean lambdaIdentSymbolFilter(Symbol sym) { - return (sym.kind == VAR || sym.kind == MTH) - && !sym.isStatic() - && sym.name != names.init; - } - - private class Frame { - final JCTree tree; - List locals; - - public Frame(JCTree tree) { - this.tree = tree; - } - - void addLocal(Symbol sym) { - if (locals == null) { - locals = List.nil(); - } - locals = locals.prepend(sym); - } - } - - /** - * This class is used to store important information regarding translation of - * lambda expression/method references (see subclasses). - */ - abstract class TranslationContext { - - /** the underlying (untranslated) tree */ - final T tree; - - /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ - final Symbol owner; - - /** the depth of this lambda expression in the frame stack */ - final int depth; - - /** the enclosing translation context (set for nested lambdas/mref) */ - final TranslationContext prev; - - /** list of methods to be bridged by the meta-factory */ - final List bridges; - - TranslationContext(T tree) { - this.tree = tree; - this.owner = owner(true); - this.depth = frameStack.size() - 1; - this.prev = context(); - ClassSymbol csym = - types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.target, ABSTRACT | INTERFACE); - this.bridges = types.functionalInterfaceBridges(csym); - } - - /** does this functional expression need to be created using alternate metafactory? */ - boolean needsAltMetafactory() { - return tree.target.isIntersection() || - isSerializable() || - bridges.length() > 1; - } - - /** does this functional expression require serialization support? */ - boolean isSerializable() { - if (forceSerializable) { - return true; - } - return types.asSuper(tree.target, syms.serializableType.tsym) != null; - } - - /** - * @return Name of the enclosing method to be folded into synthetic - * method name - */ - String enclosingMethodName() { - return syntheticMethodNameComponent(owner.name); - } - - /** - * @return Method name in a form that can be folded into a - * component of a synthetic method name - */ - String syntheticMethodNameComponent(Name name) { - if (name == null) { - return "null"; - } - String methodName = name.toString(); - if (methodName.equals("")) { - methodName = "static"; - } else if (methodName.equals("")) { - methodName = "new"; - } - return methodName; - } - } - - /** - * This class retains all the useful information about a lambda expression; - * the contents of this class are filled by the LambdaAnalyzer visitor, - * and the used by the main translation routines in order to adjust references - * to captured locals/members, etc. - */ - class LambdaTranslationContext extends TranslationContext { - - /** variable in the enclosing context to which this lambda is assigned */ - final Symbol self; - - /** variable in the enclosing context to which this lambda is assigned */ - final Symbol assignedTo; - - Map> translatedSymbols; - - /** the synthetic symbol for the method hoisting the translated lambda */ - MethodSymbol translatedSym; - - List syntheticParams; - - LambdaTranslationContext(JCLambda tree) { - super(tree); - Frame frame = frameStack.head; - switch (frame.tree.getTag()) { - case VARDEF: - assignedTo = self = ((JCVariableDecl) frame.tree).sym; - break; - case ASSIGN: - self = null; - assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable()); - break; - default: - assignedTo = self = null; - break; - } - - // This symbol will be filled-in in complete - if (owner.kind == MTH) { - final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner); - this.translatedSym = new MethodSymbol(SYNTHETIC | PRIVATE, null, null, owner.enclClass()) { - @Override - public MethodSymbol originalEnclosingMethod() { - return originalOwner; - } - }; - } else { - this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass()); - } - translatedSymbols = new EnumMap<>(LambdaSymbolKind.class); - - translatedSymbols.put(PARAM, new LinkedHashMap<>()); - translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<>()); - translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<>()); - translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<>()); - } - - /** - * For a serializable lambda, generate a disambiguating string - * which maximizes stability across deserialization. - * - * @return String to differentiate synthetic lambda method names - */ - private String serializedLambdaDisambiguation() { - StringBuilder buf = new StringBuilder(); - // Append the enclosing method signature to differentiate - // overloaded enclosing methods. For lambdas enclosed in - // lambdas, the generated lambda method will not have type yet, - // but the enclosing method's name will have been generated - // with this same method, so it will be unique and never be - // overloaded. - Assert.check( - owner.type != null || - directlyEnclosingLambda() != null); - if (owner.type != null) { - buf.append(typeSig(owner.type, true)); - buf.append(":"); - } - - // Add target type info - buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName()); - buf.append(" "); - - // Add variable assigned to - if (assignedTo != null) { - buf.append(assignedTo.flatName()); - buf.append("="); - } - //add captured locals info: type, name, order - for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) { - if (fv != self) { - buf.append(typeSig(fv.type, true)); - buf.append(" "); - buf.append(fv.flatName()); - buf.append(","); - } - } - - return buf.toString(); - } - - /** - * For a non-serializable lambda, generate a simple method. - * - * @return Name to use for the synthetic lambda method name - */ - private Name lambdaName() { - return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++)); - } - - /** - * For a serializable lambda, generate a method name which maximizes - * name stability across deserialization. - * - * @return Name to use for the synthetic lambda method name - */ - private Name serializedLambdaName() { - StringBuilder buf = new StringBuilder(); - buf.append(names.lambda); - // Append the name of the method enclosing the lambda. - buf.append(enclosingMethodName()); - buf.append('$'); - // Append a hash of the disambiguating string : enclosing method - // signature, etc. - String disam = serializedLambdaDisambiguation(); - buf.append(Integer.toHexString(disam.hashCode())); - buf.append('$'); - // The above appended name components may not be unique, append - // a count based on the above name components. - buf.append(syntheticMethodNameCounts.getIndex(buf)); - String result = buf.toString(); - //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam); - return names.fromString(result); - } - - /** - * Translate a symbol of a given kind into something suitable for the - * synthetic lambda body - */ - Symbol translate(final Symbol sym, LambdaSymbolKind skind) { - Symbol ret; - switch (skind) { - case CAPTURED_THIS: - ret = sym; // self represented - break; - case CAPTURED_VAR: - ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, sym.name, types.erasure(sym.type), translatedSym) { - @Override - public Symbol baseSymbol() { - //keep mapping with original captured symbol - return sym; - } - }; - break; - case LOCAL_VAR: - ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym) { - @Override - public Symbol baseSymbol() { - //keep mapping with original symbol - return sym; - } - }; - ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; - // If sym.data == ElementKind.EXCEPTION_PARAMETER, - // set ret.data = ElementKind.EXCEPTION_PARAMETER too. - // Because method com.sun.tools.javac.jvm.Code.fillExceptionParameterPositions and - // com.sun.tools.javac.jvm.Code.fillLocalVarPosition would use it. - // See JDK-8257740 for more information. - if (((VarSymbol) sym).isExceptionParameter()) { - ((VarSymbol) ret).setData(ElementKind.EXCEPTION_PARAMETER); - } - break; - case PARAM: - ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, sym.name, types.erasure(sym.type), translatedSym); - ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; - // Set ret.data. Same as case LOCAL_VAR above. - if (((VarSymbol) sym).isExceptionParameter()) { - ((VarSymbol) ret).setData(ElementKind.EXCEPTION_PARAMETER); - } - break; - default: - Assert.error(skind.name()); - throw new AssertionError(); - } - if (ret != sym && skind.propagateAnnotations()) { - ret.setDeclarationAttributes(sym.getRawAttributes()); - ret.setTypeAttributes(sym.getRawTypeAttributes()); - } - return ret; - } - - void addSymbol(Symbol sym, LambdaSymbolKind skind) { - Map transMap = getSymbolMap(skind); - if (!transMap.containsKey(sym)) { - transMap.put(sym, translate(sym, skind)); - } - } - - Map getSymbolMap(LambdaSymbolKind skind) { - Map m = translatedSymbols.get(skind); - Assert.checkNonNull(m); - return m; - } - - JCTree translate(JCIdent lambdaIdent) { - for (LambdaSymbolKind kind : LambdaSymbolKind.values()) { - Map m = getSymbolMap(kind); - switch(kind) { - default: - if (m.containsKey(lambdaIdent.sym)) { - Symbol tSym = m.get(lambdaIdent.sym); - JCTree t = make.Ident(tSym).setType(lambdaIdent.type); - return t; - } - break; - } - } - return null; - } - - /** - * The translatedSym is not complete/accurate until the analysis is - * finished. Once the analysis is finished, the translatedSym is - * "completed" -- updated with type information, access modifiers, - * and full parameter list. - */ - void complete() { - if (syntheticParams != null) { - return; - } - boolean inInterface = translatedSym.owner.isInterface(); - boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); - - // If instance access isn't needed, make it static. - // Interface instance methods must be default methods. - // Lambda methods are private synthetic. - // Inherit ACC_STRICT from the enclosing method, or, for clinit, - // from the class. - translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD | - owner.flags_field & STRICTFP | - owner.owner.flags_field & STRICTFP | - PRIVATE | - (thisReferenced? (inInterface? DEFAULT : 0) : STATIC); - - //compute synthetic params - ListBuffer params = new ListBuffer<>(); - ListBuffer parameterSymbols = new ListBuffer<>(); - - // The signature of the method is augmented with the following - // synthetic parameters: - // - // 1) reference to enclosing contexts captured by the lambda expression - // 2) enclosing locals captured by the lambda expression - for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) { - params.append(make.VarDef((VarSymbol) thisSym, null)); - parameterSymbols.append((VarSymbol) thisSym); - } - for (Symbol thisSym : getSymbolMap(PARAM).values()) { - params.append(make.VarDef((VarSymbol) thisSym, null)); - parameterSymbols.append((VarSymbol) thisSym); - } - syntheticParams = params.toList(); - - translatedSym.params = parameterSymbols.toList(); - - // Compute and set the lambda name - translatedSym.name = isSerializable() - ? serializedLambdaName() - : lambdaName(); - - //prepend synthetic args to translated lambda method signature - translatedSym.type = types.createMethodTypeWithParameters( - generatedLambdaSig(), - TreeInfo.types(syntheticParams)); - } - - Type generatedLambdaSig() { - return types.erasure(tree.getDescriptorType(types)); - } - } - - /** - * Simple subclass modelling the translation context of a method reference. - */ - final class ReferenceTranslationContext extends TranslationContext { - - ReferenceTranslationContext(JCMemberReference tree) { - super(tree); + return types.asSuper(tree.target, syms.serializableType.tsym) != null; + } + + void dumpStats(JCFunctionalExpression tree, boolean needsAltMetafactory, Symbol sym) { + if (dumpLambdaToMethodStats) { + if (tree instanceof JCLambda lambda) { + log.note(tree, diags.noteKey(lambda.wasMethodReference ? "mref.stat.1" : "lambda.stat", + needsAltMetafactory, sym)); + } else if (tree instanceof JCMemberReference) { + log.note(tree, Notes.MrefStat(needsAltMetafactory, null)); } } } - // - /* - * These keys provide mappings for various translated lambda symbols - * and the prevailing order must be maintained. + /** + * This class retains all the useful information about a lambda expression, + * and acts as a translation map that is used by the main translation routines + * in order to adjust references to captured locals/members, etc. */ - enum LambdaSymbolKind { - PARAM, // original to translated lambda parameters - LOCAL_VAR, // original to translated lambda locals - CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters - CAPTURED_THIS; // class symbols to translated synthetic parameters (for captured member access) + class LambdaTranslationContext { - boolean propagateAnnotations() { - switch (this) { + /** the underlying (untranslated) tree */ + final JCFunctionalExpression tree; + + /** a translation map from source symbols to translated symbols */ + final Map lambdaProxies = new HashMap<>(); + + /** the list of symbols captured by this lambda expression */ + final List capturedVars; + + /** the synthetic symbol for the method hoisting the translated lambda */ + final MethodSymbol translatedSym; + + /** the list of parameter declarations of the translated lambda method */ + final List syntheticParams; + + LambdaTranslationContext(JCLambda tree) { + this.tree = tree; + // This symbol will be filled-in in complete + Symbol owner = tree.owner; + if (owner.kind == MTH) { + final MethodSymbol originalOwner = (MethodSymbol)owner.clone(owner.owner); + this.translatedSym = new MethodSymbol(0, null, null, owner.enclClass()) { + @Override + public MethodSymbol originalEnclosingMethod() { + return originalOwner; + } + }; + } else { + this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass()); + } + ListBuffer params = new ListBuffer<>(); + ListBuffer parameterSymbols = new ListBuffer<>(); + LambdaCaptureScanner captureScanner = new LambdaCaptureScanner(tree); + capturedVars = captureScanner.analyzeCaptures(); + for (VarSymbol captured : capturedVars) { + VarSymbol trans = addSymbol(captured, LambdaSymbolKind.CAPTURED_VAR); + params.append(make.VarDef(trans, null)); + parameterSymbols.add(trans); + } + for (JCVariableDecl param : tree.params) { + VarSymbol trans = addSymbol(param.sym, LambdaSymbolKind.PARAM); + params.append(make.VarDef(trans, null)); + parameterSymbols.add(trans); + } + syntheticParams = params.toList(); + completeLambdaMethodSymbol(owner, captureScanner.capturesThis); + translatedSym.params = parameterSymbols.toList(); + } + + void completeLambdaMethodSymbol(Symbol owner, boolean thisReferenced) { + boolean inInterface = owner.enclClass().isInterface(); + + // Compute and set the lambda name + Name name = isSerializable(tree) + ? serializedLambdaName(owner) + : lambdaName(owner); + + //prepend synthetic args to translated lambda method signature + Type type = types.createMethodTypeWithParameters( + generatedLambdaSig(), + TreeInfo.types(syntheticParams)); + + // If instance access isn't needed, make it static. + // Interface instance methods must be default methods. + // Lambda methods are private synthetic. + // Inherit ACC_STRICT from the enclosing method, or, for clinit, + // from the class. + long flags = SYNTHETIC | LAMBDA_METHOD | + owner.flags_field & STRICTFP | + owner.owner.flags_field & STRICTFP | + PRIVATE | + (thisReferenced? (inInterface? DEFAULT : 0) : STATIC); + + translatedSym.type = type; + translatedSym.name = name; + translatedSym.flags_field = flags; + } + + /** + * For a serializable lambda, generate a disambiguating string + * which maximizes stability across deserialization. + * + * @return String to differentiate synthetic lambda method names + */ + private String serializedLambdaDisambiguation(Symbol owner) { + StringBuilder buf = new StringBuilder(); + // Append the enclosing method signature to differentiate + // overloaded enclosing methods. For lambdas enclosed in + // lambdas, the generated lambda method will not have type yet, + // but the enclosing method's name will have been generated + // with this same method, so it will be unique and never be + // overloaded. + Assert.check( + owner.type != null || + lambdaContext != null); + if (owner.type != null) { + buf.append(typeSig(owner.type, true)); + buf.append(":"); + } + + // Add target type info + buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName()); + buf.append(" "); + + // Add variable assigned to + if (pendingVar != null) { + buf.append(pendingVar.flatName()); + buf.append("="); + } + //add captured locals info: type, name, order + for (Symbol fv : capturedVars) { + if (fv != owner) { + buf.append(typeSig(fv.type, true)); + buf.append(" "); + buf.append(fv.flatName()); + buf.append(","); + } + } + + return buf.toString(); + } + + /** + * For a non-serializable lambda, generate a simple method. + * + * @return Name to use for the synthetic lambda method name + */ + private Name lambdaName(Symbol owner) { + StringBuilder buf = new StringBuilder(); + buf.append(names.lambda); + buf.append(syntheticMethodNameComponent(owner)); + buf.append("$"); + buf.append(kInfo.syntheticNameIndex(buf, 0)); + return names.fromString(buf.toString()); + } + + /** + * @return Method name in a form that can be folded into a + * component of a synthetic method name + */ + String syntheticMethodNameComponent(Symbol owner) { + long ownerFlags = owner.flags(); + if ((ownerFlags & BLOCK) != 0) { + return (ownerFlags & STATIC) != 0 ? + "static" : "new"; + } else if (owner.isConstructor()) { + return "new"; + } else { + return owner.name.toString(); + } + } + + /** + * For a serializable lambda, generate a method name which maximizes + * name stability across deserialization. + * + * @return Name to use for the synthetic lambda method name + */ + private Name serializedLambdaName(Symbol owner) { + StringBuilder buf = new StringBuilder(); + buf.append(names.lambda); + // Append the name of the method enclosing the lambda. + buf.append(syntheticMethodNameComponent(owner)); + buf.append('$'); + // Append a hash of the disambiguating string : enclosing method + // signature, etc. + String disam = serializedLambdaDisambiguation(owner); + buf.append(Integer.toHexString(disam.hashCode())); + buf.append('$'); + // The above appended name components may not be unique, append + // a count based on the above name components. + buf.append(kInfo.syntheticNameIndex(buf, 1)); + String result = buf.toString(); + //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam); + return names.fromString(result); + } + + /** + * Translate a symbol of a given kind into something suitable for the + * synthetic lambda body + */ + VarSymbol translate(final VarSymbol sym, LambdaSymbolKind skind) { + VarSymbol ret; + boolean propagateAnnos = true; + switch (skind) { case CAPTURED_VAR: - case CAPTURED_THIS: - return false; + Name name = (sym.flags() & LOCAL_CAPTURE_FIELD) != 0 ? + sym.baseSymbol().name : sym.name; + ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym); + propagateAnnos = false; + break; + case LOCAL_VAR: + ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym); + ret.pos = sym.pos; + // If sym.data == ElementKind.EXCEPTION_PARAMETER, + // set ret.data = ElementKind.EXCEPTION_PARAMETER too. + // Because method com.sun.tools.javac.jvm.Code.fillExceptionParameterPositions and + // com.sun.tools.javac.jvm.Code.fillLocalVarPosition would use it. + // See JDK-8257740 for more information. + if (sym.isExceptionParameter()) { + ret.setData(ElementKind.EXCEPTION_PARAMETER); + } + break; + case PARAM: + ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, sym.name, types.erasure(sym.type), translatedSym); + ret.pos = sym.pos; + break; default: - return true; - } + Assert.error(skind.name()); + throw new AssertionError(); + } + if (ret != sym && propagateAnnos) { + ret.setDeclarationAttributes(sym.getRawAttributes()); + ret.setTypeAttributes(sym.getRawTypeAttributes()); + } + return ret; + } + + VarSymbol addLocal(VarSymbol sym) { + return addSymbol(sym, LambdaSymbolKind.LOCAL_VAR); + } + + private VarSymbol addSymbol(VarSymbol sym, LambdaSymbolKind skind) { + return lambdaProxies.computeIfAbsent(sym, s -> translate(s, skind)); + } + + JCTree translate(JCIdent lambdaIdent) { + Symbol tSym = lambdaProxies.get(lambdaIdent.sym); + return tSym != null ? + make.Ident(tSym).setType(lambdaIdent.type) : + null; + } + + Type generatedLambdaSig() { + return types.erasure(tree.getDescriptorType(types)); + } + + /** + * Compute the set of local variables captured by this lambda expression. + * Also determines whether this lambda expression captures the enclosing 'this'. + */ + class LambdaCaptureScanner extends CaptureScanner { + boolean capturesThis; + Set seenClasses = new HashSet<>(); + + LambdaCaptureScanner(JCLambda ownerTree) { + super(ownerTree); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + seenClasses.add(tree.sym); + super.visitClassDef(tree); + } + + @Override + public void visitIdent(JCIdent tree) { + if (!tree.sym.isStatic() && + tree.sym.owner.kind == TYP && + (tree.sym.kind == VAR || tree.sym.kind == MTH) && + !seenClasses.contains(tree.sym.owner)) { + if ((tree.sym.flags() & LOCAL_CAPTURE_FIELD) != 0) { + // a local, captured by Lower - re-capture! + addFreeVar((VarSymbol) tree.sym); + } else { + // a reference to an enclosing field or method, we need to capture 'this' + capturesThis = true; + } + } else { + // might be a local capture + super.visitIdent(tree); + } + } + + @Override + public void visitSelect(JCFieldAccess tree) { + if (tree.sym.kind == VAR && + (tree.sym.name == names._this || + tree.sym.name == names._super) && + !seenClasses.contains(tree.sym.type.tsym)) { + capturesThis = true; + } + super.visitSelect(tree); + } + + @Override + public void visitAnnotation(JCAnnotation tree) { + // do nothing (annotation values look like captured instance fields) + } + } + + /* + * These keys provide mappings for various translated lambda symbols + * and the prevailing order must be maintained. + */ + enum LambdaSymbolKind { + PARAM, // original to translated lambda parameters + LOCAL_VAR, // original to translated lambda locals + CAPTURED_VAR; // variables in enclosing scope to translated synthetic parameters } } @@ -1791,7 +1304,7 @@ public class LambdaToMethod extends TreeTranslator { /** * Are signatures incompatible with JVM spec allowed? - * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation()}. + * Used by {@link LambdaTranslationContext#serializedLambdaDisambiguation(Symbol)}}. */ boolean allowIllegalSignatures; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index c5fd2177d49..62117583a96 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -267,26 +267,22 @@ public class Lower extends TreeTranslator { Map> freevarCache; /** A navigator class for collecting the free variables accessed - * from a local class. There is only one case; all other cases simply - * traverse down the tree. This class doesn't deal with the specific - * of Lower - it's an abstract visitor that is meant to be reused in - * order to share the local variable capture logic. + * from a local class. */ - abstract class BasicFreeVarCollector extends TreeScanner { + class FreeVarCollector extends CaptureScanner { - /** Add all free variables of class c to fvs list - * unless they are already there. - */ - abstract void addFreeVars(ClassSymbol c); - - /** If tree refers to a variable in owner of local class, add it to - * free variables list. - */ - public void visitIdent(JCIdent tree) { - visitSymbol(tree.sym); + FreeVarCollector(JCTree ownerTree) { + super(ownerTree); + } + + void addFreeVars(ClassSymbol c) { + List fvs = freevarCache.get(c); + if (fvs != null) { + for (List l = fvs; l.nonEmpty(); l = l.tail) { + addFreeVar(l.head); + } + } } - // where - abstract void visitSymbol(Symbol _sym); /** If tree refers to a class instance creation expression * add all free variables of the freshly created class. @@ -306,84 +302,6 @@ public class Lower extends TreeTranslator { } super.visitApply(tree); } - - @Override - public void visitYield(JCYield tree) { - scan(tree.value); - } - - } - - /** - * Lower-specific subclass of {@code BasicFreeVarCollector}. - */ - class FreeVarCollector extends BasicFreeVarCollector { - - /** The owner of the local class. - */ - Symbol owner; - - /** The local class. - */ - ClassSymbol clazz; - - /** The list of owner's variables accessed from within the local class, - * without any duplicates. - */ - List fvs; - - FreeVarCollector(ClassSymbol clazz) { - this.clazz = clazz; - this.owner = clazz.owner; - this.fvs = List.nil(); - } - - /** Add free variable to fvs list unless it is already there. - */ - private void addFreeVar(VarSymbol v) { - for (List l = fvs; l.nonEmpty(); l = l.tail) - if (l.head == v) return; - fvs = fvs.prepend(v); - } - - @Override - void addFreeVars(ClassSymbol c) { - List fvs = freevarCache.get(c); - if (fvs != null) { - for (List l = fvs; l.nonEmpty(); l = l.tail) { - addFreeVar(l.head); - } - } - } - - @Override - void visitSymbol(Symbol _sym) { - Symbol sym = _sym; - if (sym.kind == VAR || sym.kind == MTH) { - if (sym != null && sym.owner != owner) - sym = proxies.get(sym); - if (sym != null && sym.owner == owner) { - VarSymbol v = (VarSymbol)sym; - if (v.getConstValue() == null) { - addFreeVar(v); - } - } - } - } - } - - ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) { - if (!c.isDirectlyOrIndirectlyLocal()) { - return null; - } - Symbol currentOwner = c.owner; - while (currentOwner.owner.kind.matches(KindSelector.TYP) && currentOwner.isDirectlyOrIndirectlyLocal()) { - currentOwner = currentOwner.owner; - } - if (currentOwner.owner.kind.matches(KindSelector.VAL_MTH) && c.isSubClass(currentOwner, types)) { - return (ClassSymbol)currentOwner; - } - return null; } /** Return the variables accessed from within a local class, which @@ -395,22 +313,10 @@ public class Lower extends TreeTranslator { if (fvs != null) { return fvs; } - if (c.owner.kind.matches(KindSelector.VAL_MTH) && !c.isStatic()) { - FreeVarCollector collector = new FreeVarCollector(c); - collector.scan(classDef(c)); - fvs = collector.fvs; - freevarCache.put(c, fvs); - return fvs; - } else { - ClassSymbol owner = ownerToCopyFreeVarsFrom(c); - if (owner != null) { - fvs = freevarCache.get(owner); - freevarCache.put(c, fvs); - return fvs; - } else { - return List.nil(); - } - } + FreeVarCollector collector = new FreeVarCollector(classDef(c)); + fvs = collector.analyzeCaptures().reverse(); + freevarCache.put(c, fvs); + return fvs; } Map enumSwitchMap = new LinkedHashMap<>(); @@ -1501,7 +1407,7 @@ public class Lower extends TreeTranslator { * @param owner The class in which the definitions go. */ List freevarDefs(int pos, List freevars, Symbol owner) { - return freevarDefs(pos, freevars, owner, 0); + return freevarDefs(pos, freevars, owner, LOCAL_CAPTURE_FIELD); } List freevarDefs(int pos, List freevars, Symbol owner, @@ -1517,7 +1423,12 @@ public class Lower extends TreeTranslator { proxyName = proxyName(v.name, index++); } while (!proxyNames.add(proxyName)); VarSymbol proxy = new VarSymbol( - flags, proxyName, v.erasure(types), owner); + flags, proxyName, v.erasure(types), owner) { + @Override + public Symbol baseSymbol() { + return v; + } + }; proxies.put(v, proxy); JCVariableDecl vd = make.at(pos).VarDef(proxy, null); vd.vartype = access(vd.vartype); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index bfe35dbec9c..d232f2e6d9f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -679,6 +679,7 @@ public class TransTypes extends TreeTranslator { JCLambda slam = make.Lambda(params.toList(), expr); slam.target = tree.target; + slam.owner = tree.owner; slam.type = tree.type; slam.pos = tree.pos; slam.wasMethodReference = true; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index dfa92efae74..6679bb43fb8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -329,7 +329,7 @@ public class ClassWriter extends ClassFile { int alenIdx = writeAttr(attributeName); ClassSymbol enclClass = c.owner.enclClass(); MethodSymbol enclMethod = - (c.owner.type == null // local to init block + ((c.owner.flags() & BLOCK) != 0 // local to init block || c.owner.kind != MTH) // or member init ? null : ((MethodSymbol)c.owner).originalEnclosingMethod(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 6f3b8b5d8aa..6041da6723a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -807,6 +807,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /** list of target types inferred for this functional expression. */ public Type target; + /** The owner of this functional expression. */ + public Symbol owner; public Type getDescriptorType(Types types) { return target != null ? types.findDescriptorType(target) : types.createErrorType(null); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java index 0336f3c4191..b9ae35da9df 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeScanner.java @@ -273,8 +273,8 @@ public class TreeScanner extends Visitor { } public void visitLambda(JCLambda tree) { - scan(tree.body); scan(tree.params); + scan(tree.body); } public void visitParens(JCParens tree) { diff --git a/test/jdk/jdk/internal/vm/Continuation/Scoped.java b/test/jdk/jdk/internal/vm/Continuation/Scoped.java index b034243d596..1bad8e4aebd 100644 --- a/test/jdk/jdk/internal/vm/Continuation/Scoped.java +++ b/test/jdk/jdk/internal/vm/Continuation/Scoped.java @@ -74,27 +74,27 @@ public class Scoped { frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("No scope: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), A).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("A: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), B).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("B: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter")); frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), C).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("C: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter")); frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), K).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("K: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), null).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); System.out.println("null: " + frames); - assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); + assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); } assertEquals(res.get(), 2); } @@ -119,23 +119,23 @@ public class Scoped { List frames = StackWalker.getInstance().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); - assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1")); + assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1")); frames = StackWalkerHelper.getInstance(C).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); - assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter")); + assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter")); frames = StackWalkerHelper.getInstance(B).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); - assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter")); + assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter")); frames = StackWalkerHelper.getInstance(A).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); - assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); + assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter")); frames = StackWalkerHelper.getInstance(K).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); - assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1")); + assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1")); long r = b+1; }); diff --git a/test/langtools/tools/javac/MethodParameters/LambdaTest.out b/test/langtools/tools/javac/MethodParameters/LambdaTest.out index b03cc76a97b..36b91b5e36d 100644 --- a/test/langtools/tools/javac/MethodParameters/LambdaTest.out +++ b/test/langtools/tools/javac/MethodParameters/LambdaTest.out @@ -1,7 +1,7 @@ class LambdaTest -- LambdaTest.() LambdaTest.foo(i) -LambdaTest.lambda$static$1(arg0)/*synthetic*/ -LambdaTest.lambda$static$0(arg0, arg1)/*synthetic*/ +LambdaTest.lambda$static$0(arg0)/*synthetic*/ +LambdaTest.lambda$static$1(arg0, arg1)/*synthetic*/ static interface LambdaTest$I -- inner LambdaTest$I.m(x) diff --git a/test/langtools/tools/javac/MethodParameters/LocalClassTest.out b/test/langtools/tools/javac/MethodParameters/LocalClassTest.out index 3b95739e74a..7b0028e1fb0 100644 --- a/test/langtools/tools/javac/MethodParameters/LocalClassTest.out +++ b/test/langtools/tools/javac/MethodParameters/LocalClassTest.out @@ -1,7 +1,7 @@ class LocalClassTest$1 -- anon LocalClassTest$1.(final this$0/*implicit*/, final j, final val$i/*synthetic*/) class LocalClassTest$1CapturingLocal$1 -- anon -LocalClassTest$1CapturingLocal$1.(final this$0/*implicit*/, final val$val$i/*synthetic*/) +LocalClassTest$1CapturingLocal$1.(final this$0/*implicit*/, final val$i/*synthetic*/) LocalClassTest$1CapturingLocal$1.test() class LocalClassTest$1CapturingLocal -- inner LocalClassTest$1CapturingLocal.(final this$0/*implicit*/, final j, final val$i/*synthetic*/) diff --git a/test/langtools/tools/javac/T8019486/WrongLNTForLambdaTest.java b/test/langtools/tools/javac/T8019486/WrongLNTForLambdaTest.java index 24c9b81c964..4ddc40fa231 100644 --- a/test/langtools/tools/javac/T8019486/WrongLNTForLambdaTest.java +++ b/test/langtools/tools/javac/T8019486/WrongLNTForLambdaTest.java @@ -136,15 +136,15 @@ public class WrongLNTForLambdaTest { checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Foo.class").toUri()), "lambda$bar$0", simpleLambdaExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), - "Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaWithVarsExpectedLNT); + "Foo.class").toUri()), "lambda$variablesInLambdas$0", lambdaWithVarsExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Foo$1FooBar.class").toUri()), "run", insideLambdaWithVarsExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), - "Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaVoid2VoidExpectedLNT); + "Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaVoid2VoidExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Foo.class").toUri()), "$deserializeLambda$", deserializeExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), - "Foo.class").toUri()), "lambda$variablesInLambdas$3", lambdaBridgeExpectedLNT); + "Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaBridgeExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Foo.class").toUri()), "assignLambda", assignmentExpectedLNT); checkClassFile(new File(Paths.get(System.getProperty("user.dir"), diff --git a/test/langtools/tools/javac/classfiles/attributes/EnclosingMethod/EnclosingMethodTest.java b/test/langtools/tools/javac/classfiles/attributes/EnclosingMethod/EnclosingMethodTest.java index b555014bebe..f15771840f5 100644 --- a/test/langtools/tools/javac/classfiles/attributes/EnclosingMethod/EnclosingMethodTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/EnclosingMethod/EnclosingMethodTest.java @@ -245,7 +245,6 @@ public class EnclosingMethodTest extends TestResult { // anonymous and local classes in lambda @ExpectedEnclosingMethod( info = "EnclosingLambda in EnclosingMethodTest", - enclosingMethod = "", enclosingClazz = EnclosingMethodTest.class ) class EnclosingLambda { @@ -325,7 +324,6 @@ public class EnclosingMethodTest extends TestResult { // anonymous and local classes in lambda @ExpectedEnclosingMethod( info = "EnclosingLambda in notEnclosing01", - enclosingMethod = "", enclosingClazz = notEnclosing01.class ) class EnclosingLambda { @@ -382,7 +380,6 @@ public class EnclosingMethodTest extends TestResult { // anonymous and local classes in lambda @ExpectedEnclosingMethod( info = "EnclosingLambda in notEnclosing02", - enclosingMethod = "", enclosingClazz = notEnclosing02.class ) class EnclosingLambda { @@ -460,7 +457,6 @@ public class EnclosingMethodTest extends TestResult { // anonymous and local classes in lambda @ExpectedEnclosingMethod( info = "EnclosingLambda in notEnclosing03", - enclosingMethod = "", enclosingClazz = notEnclosing03.class ) class EnclosingLambda { @@ -517,7 +513,6 @@ public class EnclosingMethodTest extends TestResult { // anonymous and local classes in lambda @ExpectedEnclosingMethod( info = "EnclosingLambda in notEnclosing04", - enclosingMethod = "", enclosingClazz = notEnclosing04.class ) class EnclosingLambda { diff --git a/test/langtools/tools/javac/lambda/CaptureVarOrder.java b/test/langtools/tools/javac/lambda/CaptureVarOrder.java new file mode 100644 index 00000000000..0bfa061cb4f --- /dev/null +++ b/test/langtools/tools/javac/lambda/CaptureVarOrder.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8336492 + * @summary Regression in lambda serialization + */ + +public class CaptureVarOrder { + static Object m(String s, int i, Object o) { + return new Object() { + final byte B = 0; + void g() { System.out.println(s + i + B + o); } + }; + } + + static Runnable r(String s, int i, Object o) { + final byte B = 0; + return () -> System.out.println(s + i + B + o); + } + + public static void main(String[] args) throws ReflectiveOperationException { + CaptureVarOrder.class.getDeclaredMethod("lambda$r$0", String.class, int.class, Object.class); + m("", 1, null).getClass().getDeclaredConstructor(String.class, int.class, Object.class); + } +} diff --git a/test/langtools/tools/javac/lambda/SerializedLambdaInLocalClass.java b/test/langtools/tools/javac/lambda/SerializedLambdaInLocalClass.java new file mode 100644 index 00000000000..c015182f449 --- /dev/null +++ b/test/langtools/tools/javac/lambda/SerializedLambdaInLocalClass.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8336492 + * @summary Regression in lambda serialization + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.function.*; + +public class SerializedLambdaInLocalClass { + + public static void main(String[] args) { + SerializedLambdaInLocalClass s = new SerializedLambdaInLocalClass(); + s.test(s::f_lambda_in_anon); + s.test(s::f_lambda_in_local); + s.test(s::f_lambda_in_lambda); + } + + void test(IntFunction> fSupplier) { + try { + F f = fSupplier.apply(42).get(); + var baos = new ByteArrayOutputStream(); + // write + try (var oos = new ObjectOutputStream(baos)) { + oos.writeObject(f); + } + byte[] bytes = baos.toByteArray(); + var bais = new ByteArrayInputStream(bytes); + // read + try (var ois = new ObjectInputStream(bais)) { + F f2 = (F)ois.readObject(); + if (f2.getValue() != f.getValue()) { + throw new AssertionError(String.format("Found: %d, expected %d", f2.getValue(), f.getValue())); + } + } + } catch (IOException | ClassNotFoundException ex) { + throw new AssertionError(ex); + } + } + + interface F extends Serializable { + int getValue(); + } + + Supplier f_lambda_in_anon(int x) { + return new Supplier() { + @Override + public F get() { + return () -> x; + } + }; + } + + Supplier f_lambda_in_local(int x) { + class FSupplier implements Supplier { + @Override + public F get() { + return () -> x; + } + } + return new FSupplier(); + } + + Supplier f_lambda_in_lambda(int x) { + return () -> () -> x; + } +}