From 148e3a9b7a128895abaa2ad7c7f96d8ce003f3b3 Mon Sep 17 00:00:00 2001 From: sadayapalam Date: Wed, 25 May 2016 15:09:13 +0530 Subject: [PATCH] 8129740: Incorrect class file created when passing lambda in inner class constructor Summary: Lambda implementation method must be static when lambda is an argument to an explicit constructor call. Reviewed-by: mcimadamore --- .../sun/tools/javac/comp/LambdaToMethod.java | 145 +++++++++++++++- .../com/sun/tools/javac/tree/TreeMaker.java | 6 + .../com/sun/tools/javac/util/Names.java | 2 + .../AllowEnclosingVarCaptureTest.java | 59 +++++++ .../T8129740/CaptureInCtorChainingTest.java | 54 ++++++ .../T8129740/QualifiedThisAccessTest.java | 157 ++++++++++++++++++ .../lambda/T8129740/SourceForTranslation.java | 122 ++++++++++++++ .../SourceToSourceTranslationTest.java | 46 +++++ .../javac/lambda/T8129740/Universe.java.out | 98 +++++++++++ 9 files changed, 683 insertions(+), 6 deletions(-) create mode 100644 test/tools/javac/lambda/T8129740/AllowEnclosingVarCaptureTest.java create mode 100644 test/tools/javac/lambda/T8129740/CaptureInCtorChainingTest.java create mode 100644 test/tools/javac/lambda/T8129740/QualifiedThisAccessTest.java create mode 100644 test/tools/javac/lambda/T8129740/SourceForTranslation.java create mode 100644 test/tools/javac/lambda/T8129740/SourceToSourceTranslationTest.java create mode 100644 test/tools/javac/lambda/T8129740/Universe.java.out diff --git a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index e50d1927..436ffea6 100644 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -338,6 +338,11 @@ public class LambdaToMethod extends TreeTranslator { syntheticInits.append((JCExpression) captured_local); } } + // add captured outer this instances (used only when `this' capture itself is illegal) + for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) { + JCTree captured_local = make.QualThis(fv.type); + syntheticInits.append((JCExpression) captured_local); + } //then, determine the arguments to the indy call List indy_args = translate(syntheticInits.toList(), localContext.prev); @@ -434,6 +439,32 @@ public class LambdaToMethod extends TreeTranslator { } } + /** + * Translate qualified `this' references within a lambda to the mapped identifier + * @param tree + */ + @Override + public void visitSelect(JCFieldAccess tree) { + if (context == null || !analyzer.lambdaFieldAccessFilter(tree)) { + super.visitSelect(tree); + } else { + int prevPos = make.pos; + try { + make.at(tree); + + LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; + JCTree ltree = lambdaContext.translate(tree); + if (ltree != null) { + result = ltree; + } else { + super.visitSelect(tree); + } + } finally { + make.at(prevPos); + } + } + } + @Override public void visitVarDef(JCVariableDecl tree) { LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; @@ -1125,6 +1156,11 @@ public class LambdaToMethod extends TreeTranslator { */ private int lambdaCount = 0; + /** + * List of types undergoing construction via explicit constructor chaining. + */ + private List typesUnderConstruction; + /** * keep the count of lambda expression defined in given context (used to * generate unambiguous names for serializable lambdas) @@ -1156,10 +1192,35 @@ public class LambdaToMethod extends TreeTranslator { private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { frameStack = List.nil(); + typesUnderConstruction = List.nil(); localClassDefs = new HashMap(); return translate(tree); } + @Override + public void visitApply(JCMethodInvocation tree) { + List previousNascentTypes = typesUnderConstruction; + try { + Name methName = TreeInfo.name(tree.meth); + if (methName == names._this || methName == names._super) { + typesUnderConstruction = typesUnderConstruction.prepend(currentClass()); + } + super.visitApply(tree); + } finally { + typesUnderConstruction = previousNascentTypes; + } + } + // where + private ClassSymbol currentClass() { + for (Frame frame : frameStack) { + if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { + JCClassDecl cdef = (JCClassDecl) frame.tree; + return cdef.sym; + } + } + return null; + } + @Override public void visitBlock(JCBlock tree) { List prevStack = frameStack; @@ -1623,6 +1684,22 @@ public class LambdaToMethod extends TreeTranslator { && sym.name != names.init; } + /** + * This is used to filter out those select nodes that need to be adjusted + * when translating away lambda expressions - at the moment, this is the + * set of nodes that select `this' (qualified this) + */ + private boolean lambdaFieldAccessFilter(JCFieldAccess fAccess) { + LambdaTranslationContext lambdaContext = + context instanceof LambdaTranslationContext ? + (LambdaTranslationContext) context : null; + return lambdaContext != null + && !fAccess.sym.isStatic() + && fAccess.name == names._this + && (fAccess.sym.owner.kind == TYP) + && !lambdaContext.translatedSymbols.get(CAPTURED_OUTER_THIS).isEmpty(); + } + /** * This is used to filter out those new class expressions that need to * be qualified with an enclosing tree @@ -1797,6 +1874,7 @@ public class LambdaToMethod extends TreeTranslator { translatedSymbols.put(LOCAL_VAR, new LinkedHashMap()); translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap()); translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap()); + translatedSymbols.put(CAPTURED_OUTER_THIS, new LinkedHashMap()); translatedSymbols.put(TYPE_VAR, new LinkedHashMap()); freeVarProcessedLocalClasses = new HashSet<>(); @@ -1909,6 +1987,16 @@ public class LambdaToMethod extends TreeTranslator { } }; break; + case CAPTURED_OUTER_THIS: + Name name = names.fromString(new String(sym.flatName().toString() + names.dollarThis)); + ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, 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); ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; @@ -1929,6 +2017,14 @@ public class LambdaToMethod extends TreeTranslator { } void addSymbol(Symbol sym, LambdaSymbolKind skind) { + if (skind == CAPTURED_THIS && sym != null && sym.kind == TYP && !typesUnderConstruction.isEmpty()) { + ClassSymbol currentClass = currentClass(); + if (currentClass != null && typesUnderConstruction.contains(currentClass)) { + // reference must be to enclosing outer instance, mutate capture kind. + Assert.check(sym != currentClass); // should have been caught right in Attr + skind = CAPTURED_OUTER_THIS; + } + } Map transMap = getSymbolMap(skind); if (!transMap.containsKey(sym)) { transMap.put(sym, translate(sym, skind)); @@ -1942,17 +2038,49 @@ public class LambdaToMethod extends TreeTranslator { } JCTree translate(JCIdent lambdaIdent) { - for (Map m : translatedSymbols.values()) { - if (m.containsKey(lambdaIdent.sym)) { - Symbol tSym = m.get(lambdaIdent.sym); - JCTree t = make.Ident(tSym).setType(lambdaIdent.type); - tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); - return t; + 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); + tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); + return t; + } + break; + case CAPTURED_OUTER_THIS: + if (lambdaIdent.sym.owner.kind == TYP && m.containsKey(lambdaIdent.sym.owner)) { + // Transform outer instance variable references anchoring them to the captured synthetic. + Symbol tSym = m.get(lambdaIdent.sym.owner); + JCExpression t = make.Ident(tSym).setType(lambdaIdent.sym.owner.type); + tSym.setTypeAttributes(lambdaIdent.sym.owner.getRawTypeAttributes()); + t = make.Select(t, lambdaIdent.name); + t.setType(lambdaIdent.type); + TreeInfo.setSymbol(t, lambdaIdent.sym); + return t; + } + break; } } return null; } + /* Translate away qualified this expressions, anchoring them to synthetic parameters that + capture the qualified this handle. `fieldAccess' is guaranteed to one such. + */ + public JCTree translate(JCFieldAccess fieldAccess) { + Assert.check(fieldAccess.name == names._this); + Map m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); + if (m.containsKey(fieldAccess.sym.owner)) { + Symbol tSym = m.get(fieldAccess.sym.owner); + JCExpression t = make.Ident(tSym).setType(fieldAccess.sym.owner.type); + tSym.setTypeAttributes(fieldAccess.sym.owner.getRawTypeAttributes()); + return t; + } + return null; + } + /** * The translatedSym is not complete/accurate until the analysis is * finished. Once the analysis is finished, the translatedSym is @@ -1990,6 +2118,10 @@ public class LambdaToMethod extends TreeTranslator { params.append(make.VarDef((VarSymbol) thisSym, null)); parameterSymbols.append((VarSymbol) thisSym); } + for (Symbol thisSym : getSymbolMap(CAPTURED_OUTER_THIS).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); @@ -2138,6 +2270,7 @@ public class LambdaToMethod extends TreeTranslator { 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) + CAPTURED_OUTER_THIS, // used when `this' capture is illegal, but outer this capture is legit (JDK-8129740) TYPE_VAR; // original to translated lambda type variables } diff --git a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java index b39fd064..794aba1b 100644 --- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -621,6 +621,12 @@ public class TreeMaker implements JCTree.Factory { return Ident(new VarSymbol(FINAL, names._this, t, t.tsym)); } + /** Create a tree representing qualified `this' given its type + */ + public JCExpression QualThis(Type t) { + return Select(Type(t), new VarSymbol(FINAL, names._this, t, t.tsym)); + } + /** Create a tree representing a class literal. */ public JCExpression ClassLiteral(ClassSymbol clazz) { diff --git a/src/share/classes/com/sun/tools/javac/util/Names.java b/src/share/classes/com/sun/tools/javac/util/Names.java index cc52e0de..7f8ce460 100644 --- a/src/share/classes/com/sun/tools/javac/util/Names.java +++ b/src/share/classes/com/sun/tools/javac/util/Names.java @@ -176,6 +176,7 @@ public class Names { public final Name lambda; public final Name metafactory; public final Name altMetafactory; + public final Name dollarThis; public final Name.Table table; @@ -234,6 +235,7 @@ public class Names { value = fromString("value"); valueOf = fromString("valueOf"); values = fromString("values"); + dollarThis = fromString("$this"); // class names java_io_Serializable = fromString("java.io.Serializable"); diff --git a/test/tools/javac/lambda/T8129740/AllowEnclosingVarCaptureTest.java b/test/tools/javac/lambda/T8129740/AllowEnclosingVarCaptureTest.java new file mode 100644 index 00000000..f14eb0f3 --- /dev/null +++ b/test/tools/javac/lambda/T8129740/AllowEnclosingVarCaptureTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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 8129740 8133111 8157142 + * @summary Incorrect class file created when passing lambda in inner class constructor + * @run main AllowEnclosingVarCaptureTest + */ + +public class AllowEnclosingVarCaptureTest { + + int var = 0; + + void foo() { + var *= 2; + } + + public static void main(String[] args) { + new AllowEnclosingVarCaptureTest().new Inner(9764); + } + + public class Inner { + public Inner(Runnable r) { + r.run(); + if (var != 66704) + throw new AssertionError("Unexpected output: " + var); + } + + public Inner(int x) { + this(() -> { + var = x + 1234; + AllowEnclosingVarCaptureTest.this.var += 5678; + foo(); + AllowEnclosingVarCaptureTest.this.foo(); + }); + } + } +} diff --git a/test/tools/javac/lambda/T8129740/CaptureInCtorChainingTest.java b/test/tools/javac/lambda/T8129740/CaptureInCtorChainingTest.java new file mode 100644 index 00000000..4d16a611 --- /dev/null +++ b/test/tools/javac/lambda/T8129740/CaptureInCtorChainingTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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 8129740 8133111 8157142 + * @summary Incorrect class file created when passing lambda in inner class constructor + * @run main CaptureInCtorChainingTest + */ + +import java.util.function.Consumer; +import java.util.function.Function; + +public class CaptureInCtorChainingTest { + + CaptureInCtorChainingTest(Function, Void>, Void>, Void> innerClass) { + new InnerClass(innerClass); + } + + void foo(Void v) { } + + class InnerClass { + + InnerClass(Function, Void>, Void>, Void> factory) { + this(factory.apply(o -> o.apply(CaptureInCtorChainingTest.this::foo))); + } + + InnerClass(Void unused) { } + } + + public static void main(String[] args) { + new CaptureInCtorChainingTest(o -> null); + } +} diff --git a/test/tools/javac/lambda/T8129740/QualifiedThisAccessTest.java b/test/tools/javac/lambda/T8129740/QualifiedThisAccessTest.java new file mode 100644 index 00000000..a4596a58 --- /dev/null +++ b/test/tools/javac/lambda/T8129740/QualifiedThisAccessTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, 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 8129740 8133111 8157142 + * @summary Incorrect class file created when passing lambda in inner class constructor + * @run main QualifiedThisAccessTest + */ + +public class QualifiedThisAccessTest { // Not referenced by lambda, so should not be captured. + + public class Universe { // Not referenced by lambda, so should not be captured. + + public String name; + public int galaxiesCount; + + public Universe(String name, int galaxiesCount) { + this.name = name; + this.galaxiesCount = galaxiesCount; + } + + public String toString() { + return "Universe" + name + " of " + galaxiesCount + " galaxies"; + } + + class Galaxy { + + String name; + private int starsCount; + + Galaxy(String name, int starsCount) { + this.name = name; + this.starsCount = starsCount; + } + + public String toString() { + return "galaxy " + name + " of " + starsCount + " solar systems"; + } + + int starsCount() { + return starsCount; + } + + private String name() { + return name; + } + + class SolarSystem { + + String name; + int planetsCount; + + SolarSystem(String name, int planetsCount) { + this.name = name; + this.planetsCount = planetsCount; + } + + public String toString() { + return "Solar System of " + name + " with " + planetsCount + " planets"; + } + + int planetsCount() { + return planetsCount; + } + + SolarSystem copy(SolarSystem s) { + return s; + } + + class Planet { + + String name; + int moonsCount; + + Planet(String name, int moonsCount, Runnable r) { + this.name = name; + this.moonsCount = moonsCount; + r.run(); + } + Planet (String name, int moonsCount) { + this(name, moonsCount, ()-> { + StringBuffer buf = new StringBuffer(); + buf.append("This planet belongs to the galaxy " + + Galaxy.this.name + " with " + starsCount + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Universe.Galaxy.this.name + " with " + starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Galaxy.this.name() + " with " + starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Universe.Galaxy.this.name() + " with " + + (Universe.Galaxy.this).starsCount() + " stars\n"); + + buf.append("This planet belongs to the solar system " + + SolarSystem.this.name + " with " + planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Galaxy.SolarSystem.this.name + " with " + planetsCount() + " planets\n"); + buf.append("This planet belongs to the solar system " + + (SolarSystem.this).name + " with " + planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Universe.Galaxy.SolarSystem.this.name + " with " + + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Universe.Galaxy.SolarSystem.this.name.toLowerCase().toUpperCase() + + " with " + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + copy(Universe.Galaxy.SolarSystem.this).name.toLowerCase().toUpperCase() + + " with " + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + if (!buf.toString().equals(output)) + throw new AssertionError("Unexpected value\n" + buf); + }); + } + + static final String output = + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system SUN with 9 planets\n" + + "This planet belongs to the solar system SUN with 9 planets\n"; + + + public String toString() { + return "Planet " + name + " with " + moonsCount + " moon(s)"; + } + } + } + } + } + public static void main(String[] args) { + new QualifiedThisAccessTest().new Universe("Universe", 12345678).new Galaxy("Mily way", 23456789).new SolarSystem("Sun", 9).new Planet("Earth", 1); + } +} diff --git a/test/tools/javac/lambda/T8129740/SourceForTranslation.java b/test/tools/javac/lambda/T8129740/SourceForTranslation.java new file mode 100644 index 00000000..1fd7d38c --- /dev/null +++ b/test/tools/javac/lambda/T8129740/SourceForTranslation.java @@ -0,0 +1,122 @@ + class Universe { // Not referenced by lambda, so should not be captured. + + public String name; + public int galaxiesCount; + + public Universe(String name, int galaxiesCount) { + this.name = name; + this.galaxiesCount = galaxiesCount; + } + + public String toString() { + return "Universe" + name + " of " + galaxiesCount + " galaxies"; + } + + class Galaxy { + + String name; + private int starsCount; + + Galaxy(String name, int starsCount) { + this.name = name; + this.starsCount = starsCount; + } + + public String toString() { + return "galaxy " + name + " of " + starsCount + " solar systems"; + } + + int starsCount() { + return starsCount; + } + + private String name() { + return name; + } + + class SolarSystem { + + String name; + int planetsCount; + + SolarSystem(String name, int planetsCount) { + this.name = name; + this.planetsCount = planetsCount; + } + + public String toString() { + return "Solar System of " + name + " with " + planetsCount + " planets"; + } + + int planetsCount() { + return planetsCount; + } + + SolarSystem copy(SolarSystem s) { + return s; + } + + class Planet { + + String name; + int moonsCount; + + Planet(String name, int moonsCount, Runnable r) { + this.name = name; + this.moonsCount = moonsCount; + r.run(); + } + Planet (String name, int moonsCount) { + this(name, moonsCount, ()-> { + String n = name; + StringBuffer buf = new StringBuffer(); + buf.append("This planet belongs to the galaxy " + + Galaxy.this.name + " with " + starsCount + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Universe.Galaxy.this.name + " with " + starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Galaxy.this.name() + " with " + starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + + Universe.Galaxy.this.name() + " with " + + (Universe.Galaxy.this).starsCount() + " stars\n"); + + buf.append("This planet belongs to the solar system " + + SolarSystem.this.name + " with " + planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Galaxy.SolarSystem.this.name + " with " + planetsCount() + " planets\n"); + buf.append("This planet belongs to the solar system " + + (SolarSystem.this).name + " with " + planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Universe.Galaxy.SolarSystem.this.name + " with " + + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + Universe.Galaxy.SolarSystem.this.name.toLowerCase().toUpperCase() + + " with " + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + + copy(Universe.Galaxy.SolarSystem.this).name.toLowerCase().toUpperCase() + + " with " + Universe.Galaxy.SolarSystem.this.planetsCount + " planets\n"); + if (!buf.toString().equals(output)) + throw new AssertionError("Unexpected value\n" + buf); + }); + } + + static final String output = + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the galaxy Mily way with 23456789 stars\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system Sun with 9 planets\n" + + "This planet belongs to the solar system SUN with 9 planets\n" + + "This planet belongs to the solar system SUN with 9 planets\n"; + + + public String toString() { + return "Planet " + name + " with " + moonsCount + " moon(s)"; + } + } + } + } + } diff --git a/test/tools/javac/lambda/T8129740/SourceToSourceTranslationTest.java b/test/tools/javac/lambda/T8129740/SourceToSourceTranslationTest.java new file mode 100644 index 00000000..92188b7d --- /dev/null +++ b/test/tools/javac/lambda/T8129740/SourceToSourceTranslationTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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 8129740 8133111 8157142 + * @summary Incorrect class file created when passing lambda in inner class constructor + * @library /tools/javac/lib + * @build ToolBox + * @run compile -XD-printsource SourceForTranslation.java + * @run main SourceToSourceTranslationTest + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class SourceToSourceTranslationTest { + + public static void main(String[] args) throws Exception { + Path path1 = Paths.get(System.getProperty("test.classes"), "Universe.java"); + Path path2 = Paths.get(System.getProperty("test.src"), "Universe.java.out"); + ToolBox.compareLines(path1, path2, null); + } + +} diff --git a/test/tools/javac/lambda/T8129740/Universe.java.out b/test/tools/javac/lambda/T8129740/Universe.java.out new file mode 100644 index 00000000..1d8c0e29 --- /dev/null +++ b/test/tools/javac/lambda/T8129740/Universe.java.out @@ -0,0 +1,98 @@ + +class Universe { + public String name; + public int galaxiesCount; + + public Universe(String name, int galaxiesCount) { + super(); + this.name = name; + this.galaxiesCount = galaxiesCount; + } + + public String toString() { + return "Universe" + name + " of " + galaxiesCount + " galaxies"; + } + + class Galaxy { + String name; + private int starsCount; + + Galaxy(String name, int starsCount) { + super(); + this.name = name; + this.starsCount = starsCount; + } + + public String toString() { + return "galaxy " + name + " of " + starsCount + " solar systems"; + } + + int starsCount() { + return starsCount; + } + + private String name() { + return name; + } + + class SolarSystem { + String name; + int planetsCount; + + SolarSystem(String name, int planetsCount) { + super(); + this.name = name; + this.planetsCount = planetsCount; + } + + public String toString() { + return "Solar System of " + name + " with " + planetsCount + " planets"; + } + + int planetsCount() { + return planetsCount; + } + + SolarSystem copy(SolarSystem s) { + return s; + } + + class Planet { + String name; + int moonsCount; + + Planet(String name, int moonsCount, Runnable r) { + super(); + this.name = name; + this.moonsCount = moonsCount; + r.run(); + } + + Planet(String name, int moonsCount) { + this(name, moonsCount, java.lang.invoke.LambdaMetafactory.metafactory(name, Universe.Galaxy.this, Universe.Galaxy.SolarSystem.this)); + } + static final String output = "This planet belongs to the galaxy Mily way with 23456789 stars\nThis planet belongs to the galaxy Mily way with 23456789 stars\nThis planet belongs to the galaxy Mily way with 23456789 stars\nThis planet belongs to the galaxy Mily way with 23456789 stars\nThis planet belongs to the solar system Sun with 9 planets\nThis planet belongs to the solar system Sun with 9 planets\nThis planet belongs to the solar system Sun with 9 planets\nThis planet belongs to the solar system Sun with 9 planets\nThis planet belongs to the solar system SUN with 9 planets\nThis planet belongs to the solar system SUN with 9 planets\n"; + + public String toString() { + return "Planet " + name + " with " + moonsCount + " moon(s)"; + } + + /*synthetic*/ private static void lambda$new$0(/*synthetic*/ final String name, /*synthetic*/ final Universe.Galaxy Universe$Galaxy$this, /*synthetic*/ final Universe.Galaxy.SolarSystem Universe$Galaxy$SolarSystem$this) { + String n = name; + StringBuffer buf = new StringBuffer(); + buf.append("This planet belongs to the galaxy " + Universe$Galaxy$this.name + " with " + Universe$Galaxy$this.starsCount + " stars\n"); + buf.append("This planet belongs to the galaxy " + Universe$Galaxy$this.name + " with " + Universe$Galaxy$this.starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + Universe$Galaxy$this.name() + " with " + Universe$Galaxy$this.starsCount() + " stars\n"); + buf.append("This planet belongs to the galaxy " + Universe$Galaxy$this.name() + " with " + (Universe$Galaxy$this).starsCount() + " stars\n"); + buf.append("This planet belongs to the solar system " + Universe$Galaxy$SolarSystem$this.name + " with " + Universe$Galaxy$SolarSystem$this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + Universe$Galaxy$SolarSystem$this.name + " with " + Universe$Galaxy$SolarSystem$this.planetsCount() + " planets\n"); + buf.append("This planet belongs to the solar system " + (Universe$Galaxy$SolarSystem$this).name + " with " + Universe$Galaxy$SolarSystem$this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + Universe$Galaxy$SolarSystem$this.name + " with " + Universe$Galaxy$SolarSystem$this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + Universe$Galaxy$SolarSystem$this.name.toLowerCase().toUpperCase() + " with " + Universe$Galaxy$SolarSystem$this.planetsCount + " planets\n"); + buf.append("This planet belongs to the solar system " + Universe$Galaxy$SolarSystem$this.copy(Universe$Galaxy$SolarSystem$this).name.toLowerCase().toUpperCase() + " with " + Universe$Galaxy$SolarSystem$this.planetsCount + " planets\n"); + if (!buf.toString().equals(output)) throw new AssertionError("Unexpected value\n" + buf); + } + } + } + } +} -- GitLab