8008227: Mixing lambdas with anonymous classes leads to NPE thrown by compiler
Disentangle cyclic dependency between static-ness of synthetic lambda method and static-ness of classes nested within lambdas Reviewed-by: jjg
This commit is contained in:
parent
556f6dfebb
commit
eb68b33185
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -125,7 +125,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
|
MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
|
||||||
List.<Type>nil(), syms.methodClass);
|
List.<Type>nil(), syms.methodClass);
|
||||||
deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
|
deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
|
||||||
deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), syms.serializedLambdaType, deserMethodSym);
|
deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
|
||||||
|
syms.serializedLambdaType, deserMethodSym);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMethod(JCTree decl) {
|
private void addMethod(JCTree decl) {
|
||||||
@ -738,7 +739,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
List<Type> refPTypes = tree.sym.type.getParameterTypes();
|
List<Type> refPTypes = tree.sym.type.getParameterTypes();
|
||||||
int refSize = refPTypes.size();
|
int refSize = refPTypes.size();
|
||||||
int samSize = samPTypes.size();
|
int samSize = samPTypes.size();
|
||||||
int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; // Last parameter to copy from referenced method
|
// Last parameter to copy from referenced method
|
||||||
|
int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
|
||||||
|
|
||||||
List<Type> l = refPTypes;
|
List<Type> l = refPTypes;
|
||||||
// Use parameter types of the referenced method, excluding final var args
|
// Use parameter types of the referenced method, excluding final var args
|
||||||
@ -763,7 +765,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
|
bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
|
||||||
bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
|
bridgeDecl.type = localContext.bridgeSym.type =
|
||||||
|
types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
|
||||||
|
|
||||||
//bridge method body generation - this can be either a method call or a
|
//bridge method body generation - this can be either a method call or a
|
||||||
//new instance creation expression, depending on the member reference kind
|
//new instance creation expression, depending on the member reference kind
|
||||||
@ -803,7 +806,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
|
|
||||||
//create the method call expression
|
//create the method call expression
|
||||||
JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
|
JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
|
||||||
convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType());
|
convertArgs(tree.sym, args.toList(), tree.varargsElement)).
|
||||||
|
setType(tree.sym.erasure(types).getReturnType());
|
||||||
|
|
||||||
apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
|
apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
|
||||||
setVarargsIfNeeded(apply, tree.varargsElement);
|
setVarargsIfNeeded(apply, tree.varargsElement);
|
||||||
@ -817,7 +821,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
private JCExpression bridgeExpressionNew() {
|
private JCExpression bridgeExpressionNew() {
|
||||||
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
|
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
|
||||||
//create the array creation expression
|
//create the array creation expression
|
||||||
JCNewArray newArr = make.NewArray(make.Type(types.elemtype(tree.getQualifierExpression().type)),
|
JCNewArray newArr = make.NewArray(
|
||||||
|
make.Type(types.elemtype(tree.getQualifierExpression().type)),
|
||||||
List.of(make.Ident(params.first())),
|
List.of(make.Ident(params.first())),
|
||||||
null);
|
null);
|
||||||
newArr.type = tree.getQualifierExpression().type;
|
newArr.type = tree.getQualifierExpression().type;
|
||||||
@ -872,7 +877,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
Type mtype = types.erasure(tree.descriptorType);
|
Type mtype = types.erasure(tree.descriptorType);
|
||||||
MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
|
MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
|
||||||
List<Object> staticArgs = List.<Object>of(
|
List<Object> staticArgs = List.<Object>of(
|
||||||
new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(tree.type.tsym), types),
|
new Pool.MethodHandle(ClassFile.REF_invokeInterface,
|
||||||
|
types.findDescriptorSymbol(tree.type.tsym), types),
|
||||||
new Pool.MethodHandle(refKind, refSym, types),
|
new Pool.MethodHandle(refKind, refSym, types),
|
||||||
new MethodType(mtype.getParameterTypes(),
|
new MethodType(mtype.getParameterTypes(),
|
||||||
mtype.getReturnType(),
|
mtype.getReturnType(),
|
||||||
@ -922,7 +928,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
* Generate an indy method call with given name, type and static bootstrap
|
* Generate an indy method call with given name, type and static bootstrap
|
||||||
* arguments types
|
* arguments types
|
||||||
*/
|
*/
|
||||||
private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
|
private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
|
||||||
|
List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
|
||||||
int prevPos = make.pos;
|
int prevPos = make.pos;
|
||||||
try {
|
try {
|
||||||
make.at(pos);
|
make.at(pos);
|
||||||
@ -936,7 +943,9 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
DynamicMethodSymbol dynSym =
|
DynamicMethodSymbol dynSym =
|
||||||
new DynamicMethodSymbol(names.lambda,
|
new DynamicMethodSymbol(names.lambda,
|
||||||
syms.noSymbol,
|
syms.noSymbol,
|
||||||
bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual,
|
bsm.isStatic() ?
|
||||||
|
ClassFile.REF_invokeStatic :
|
||||||
|
ClassFile.REF_invokeVirtual,
|
||||||
(MethodSymbol)bsm,
|
(MethodSymbol)bsm,
|
||||||
indyType,
|
indyType,
|
||||||
staticArgs.toArray());
|
staticArgs.toArray());
|
||||||
@ -1057,26 +1066,19 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
@Override
|
@Override
|
||||||
public void visitClassDef(JCClassDecl tree) {
|
public void visitClassDef(JCClassDecl tree) {
|
||||||
List<Frame> prevStack = frameStack;
|
List<Frame> prevStack = frameStack;
|
||||||
Map<String, Integer> prevSerializableLambdaCount = serializableLambdaCounts;
|
Map<String, Integer> prevSerializableLambdaCount =
|
||||||
|
serializableLambdaCounts;
|
||||||
Map<ClassSymbol, Symbol> prevClinits = clinits;
|
Map<ClassSymbol, Symbol> prevClinits = clinits;
|
||||||
try {
|
try {
|
||||||
serializableLambdaCounts = new HashMap<String, Integer>();
|
serializableLambdaCounts = new HashMap<String, Integer>();
|
||||||
prevClinits = new HashMap<ClassSymbol, Symbol>();
|
prevClinits = new HashMap<ClassSymbol, Symbol>();
|
||||||
if (directlyEnclosingLambda() != null) {
|
if (directlyEnclosingLambda() != null) {
|
||||||
tree.sym.owner = owner();
|
tree.sym.owner = owner();
|
||||||
LambdaTranslationContext lambdaContext = (LambdaTranslationContext) contextMap.get(directlyEnclosingLambda());
|
if (tree.sym.hasOuterInstance()) {
|
||||||
Type encl = lambdaContext.enclosingType();
|
//if a class is defined within a lambda, the lambda must capture
|
||||||
if (encl.hasTag(NONE)) {
|
//its enclosing instance (if any)
|
||||||
//if the translated lambda body occurs in a static context,
|
((LambdaTranslationContext) context())
|
||||||
//any class declaration within it must be made static
|
.addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
|
||||||
//@@@TODO: What about nested classes within lambda?
|
|
||||||
tree.sym.flags_field |= STATIC;
|
|
||||||
((ClassType) tree.sym.type).setEnclosingType(Type.noType);
|
|
||||||
} else {
|
|
||||||
//if the translated lambda body is in an instance context
|
|
||||||
//the enclosing type of any class declaration within it
|
|
||||||
//must be updated to point to the new enclosing type (if any)
|
|
||||||
((ClassType) tree.sym.type).setEnclosingType(encl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frameStack = frameStack.prepend(new Frame(tree));
|
frameStack = frameStack.prepend(new Frame(tree));
|
||||||
@ -1087,11 +1089,6 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
serializableLambdaCounts = prevSerializableLambdaCount;
|
serializableLambdaCounts = prevSerializableLambdaCount;
|
||||||
clinits = prevClinits;
|
clinits = prevClinits;
|
||||||
}
|
}
|
||||||
if (!tree.sym.isStatic() && directlyEnclosingLambda() != null) {
|
|
||||||
// Any (non-static) class defined within a lambda is an implicit 'this' reference
|
|
||||||
// because its constructor will reference the enclosing class
|
|
||||||
((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1105,7 +1102,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
if (localContext.tree.getTag() == LAMBDA) {
|
if (localContext.tree.getTag() == LAMBDA) {
|
||||||
JCTree block = capturedDecl(localContext.depth, tree.sym);
|
JCTree block = capturedDecl(localContext.depth, tree.sym);
|
||||||
if (block == null) break;
|
if (block == null) break;
|
||||||
((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR);
|
((LambdaTranslationContext)localContext)
|
||||||
|
.addSymbol(tree.sym, CAPTURED_VAR);
|
||||||
}
|
}
|
||||||
localContext = localContext.prev;
|
localContext = localContext.prev;
|
||||||
}
|
}
|
||||||
@ -1118,7 +1116,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
switch (block.getTag()) {
|
switch (block.getTag()) {
|
||||||
case CLASSDEF:
|
case CLASSDEF:
|
||||||
JCClassDecl cdecl = (JCClassDecl)block;
|
JCClassDecl cdecl = (JCClassDecl)block;
|
||||||
((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS);
|
((LambdaTranslationContext)localContext)
|
||||||
|
.addSymbol(cdecl.sym, CAPTURED_THIS);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Assert.error("bad block kind");
|
Assert.error("bad block kind");
|
||||||
@ -1165,7 +1164,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
@Override
|
@Override
|
||||||
public void visitNewClass(JCNewClass tree) {
|
public void visitNewClass(JCNewClass tree) {
|
||||||
if (lambdaNewClassFilter(context(), tree)) {
|
if (lambdaNewClassFilter(context(), tree)) {
|
||||||
((LambdaTranslationContext) context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
|
((LambdaTranslationContext) context())
|
||||||
|
.addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
|
||||||
}
|
}
|
||||||
super.visitNewClass(tree);
|
super.visitNewClass(tree);
|
||||||
}
|
}
|
||||||
@ -1278,7 +1278,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
return ((JCMethodDecl)frameStack2.head.tree).sym;
|
return ((JCMethodDecl)frameStack2.head.tree).sym;
|
||||||
case LAMBDA:
|
case LAMBDA:
|
||||||
if (!skipLambda)
|
if (!skipLambda)
|
||||||
return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym;
|
return ((LambdaTranslationContext)contextMap
|
||||||
|
.get(frameStack2.head.tree)).translatedSym;
|
||||||
default:
|
default:
|
||||||
frameStack2 = frameStack2.tail;
|
frameStack2 = frameStack2.tail;
|
||||||
}
|
}
|
||||||
@ -1555,7 +1556,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
return sym; // self represented
|
return sym; // self represented
|
||||||
case TYPE_VAR:
|
case TYPE_VAR:
|
||||||
// Just erase the type var
|
// Just erase the type var
|
||||||
return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner);
|
return new VarSymbol(sym.flags(), names.fromString(name),
|
||||||
|
types.erasure(sym.type), sym.owner);
|
||||||
default:
|
default:
|
||||||
return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
|
return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
|
||||||
}
|
}
|
||||||
@ -1633,7 +1635,8 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
|
|
||||||
// If instance access isn't needed, make it static
|
// If instance access isn't needed, make it static
|
||||||
// Interface methods much be public default methods, otherwise make it private
|
// Interface methods much be public default methods, otherwise make it private
|
||||||
translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE);
|
translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
|
||||||
|
(inInterface? PUBLIC | DEFAULT : PRIVATE);
|
||||||
|
|
||||||
//compute synthetic params
|
//compute synthetic params
|
||||||
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
|
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
|
||||||
@ -1655,12 +1658,6 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
TreeInfo.types(syntheticParams));
|
TreeInfo.types(syntheticParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
Type enclosingType() {
|
|
||||||
return owner.isStatic() ?
|
|
||||||
Type.noType :
|
|
||||||
owner.enclClass().type;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type generatedLambdaSig() {
|
Type generatedLambdaSig() {
|
||||||
return types.erasure(tree.descriptorType);
|
return types.erasure(tree.descriptorType);
|
||||||
}
|
}
|
||||||
|
40
langtools/test/tools/javac/lambda/LambdaConv27.java
Normal file
40
langtools/test/tools/javac/lambda/LambdaConv27.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 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 8008227
|
||||||
|
* @summary Mixing lambdas with anonymous classes leads to NPE thrown by compiler
|
||||||
|
* @run main LambdaConv27
|
||||||
|
*/
|
||||||
|
public class LambdaConv27 {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SAM s = ()-> { SAM s2 = ()->{ new Object() { }; }; s2.m(); };
|
||||||
|
s.m();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SAM {
|
||||||
|
void m();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user