mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8335159: Move method reference to lambda desugaring before Lower
8336320: NullPointerException: Cannot invoke Type.getTag because type is null after JDK-8334037 Reviewed-by: jlahoda, vromero
This commit is contained in:
parent
a253e0ff4b
commit
4635531950
3 changed files with 307 additions and 353 deletions
|
@ -29,7 +29,6 @@ import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
|
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
|
||||||
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
|
|
||||||
import com.sun.tools.javac.code.*;
|
import com.sun.tools.javac.code.*;
|
||||||
import com.sun.tools.javac.code.Kinds.KindSelector;
|
import com.sun.tools.javac.code.Kinds.KindSelector;
|
||||||
import com.sun.tools.javac.code.Scope.WriteableScope;
|
import com.sun.tools.javac.code.Scope.WriteableScope;
|
||||||
|
@ -39,7 +38,6 @@ import com.sun.tools.javac.main.Option.PkgInfo;
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
||||||
import com.sun.tools.javac.resources.CompilerProperties.Notes;
|
import com.sun.tools.javac.resources.CompilerProperties.Notes;
|
||||||
import com.sun.tools.javac.tree.*;
|
import com.sun.tools.javac.tree.*;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
|
|
||||||
import com.sun.tools.javac.util.*;
|
import com.sun.tools.javac.util.*;
|
||||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
|
@ -67,8 +65,6 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
|
||||||
import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
|
import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
|
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
|
||||||
|
|
||||||
import javax.lang.model.type.TypeKind;
|
|
||||||
|
|
||||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||||
|
|
||||||
/** This pass translates away some syntactic sugar: inner classes,
|
/** This pass translates away some syntactic sugar: inner classes,
|
||||||
|
@ -3903,337 +3899,6 @@ public class Lower extends TreeTranslator {
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitReference(JCMemberReference tree) {
|
|
||||||
if (needsConversionToLambda(tree)) {
|
|
||||||
// Convert to a lambda, and process as such
|
|
||||||
MemberReferenceToLambda conv = new MemberReferenceToLambda(tree);
|
|
||||||
result = translate(conv.lambda());
|
|
||||||
} else {
|
|
||||||
super.visitReference(tree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// where
|
|
||||||
boolean needsVarArgsConversion(JCMemberReference tree) {
|
|
||||||
return tree.varargsElement != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Is this an array operation like clone()
|
|
||||||
*/
|
|
||||||
boolean isArrayOp(JCMemberReference tree) {
|
|
||||||
return tree.sym.owner == syms.arrayClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean receiverAccessible(JCMemberReference tree) {
|
|
||||||
//hack needed to workaround 292 bug (7087658)
|
|
||||||
//when 292 issue is fixed we should remove this and change the backend
|
|
||||||
//code to always generate a method handle to an accessible method
|
|
||||||
return tree.ownerAccessible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Erasure destroys the implementation parameter subtype
|
|
||||||
* relationship for intersection types.
|
|
||||||
* Have similar problems for union types too.
|
|
||||||
*/
|
|
||||||
boolean interfaceParameterIsIntersectionOrUnionType(JCMemberReference tree) {
|
|
||||||
List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
|
|
||||||
for (; tl.nonEmpty(); tl = tl.tail) {
|
|
||||||
Type pt = tl.head;
|
|
||||||
if (isIntersectionOrUnionType(pt))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isIntersectionOrUnionType(Type t) {
|
|
||||||
switch (t.getKind()) {
|
|
||||||
case INTERSECTION:
|
|
||||||
case UNION:
|
|
||||||
return true;
|
|
||||||
case TYPEVAR:
|
|
||||||
TypeVar tv = (TypeVar) t;
|
|
||||||
return isIntersectionOrUnionType(tv.getUpperBound());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference,
|
|
||||||
Symbol currentClass) {
|
|
||||||
return ((targetReference.flags() & PROTECTED) != 0 &&
|
|
||||||
targetReference.packge() != currentClass.packge());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method should be called only when target release <= 14
|
|
||||||
* where LambdaMetaFactory does not spin nestmate classes.
|
|
||||||
*
|
|
||||||
* This method should be removed when --release 14 is not supported.
|
|
||||||
*/
|
|
||||||
boolean isPrivateInOtherClass(JCMemberReference tree) {
|
|
||||||
assert !target.runtimeUseNestAccess();
|
|
||||||
return (tree.sym.flags() & PRIVATE) != 0 &&
|
|
||||||
!types.isSameType(
|
|
||||||
types.erasure(tree.sym.enclClass().asType()),
|
|
||||||
types.erasure(currentClass.asType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this reference need to be converted to a lambda
|
|
||||||
* (i.e. var args need to be expanded or "super" is used)
|
|
||||||
*/
|
|
||||||
boolean needsConversionToLambda(JCMemberReference tree) {
|
|
||||||
return interfaceParameterIsIntersectionOrUnionType(tree) ||
|
|
||||||
tree.hasKind(ReferenceKind.SUPER) ||
|
|
||||||
needsVarArgsConversion(tree) ||
|
|
||||||
isArrayOp(tree) ||
|
|
||||||
(!target.runtimeUseNestAccess() && isPrivateInOtherClass(tree)) ||
|
|
||||||
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, currentClass) ||
|
|
||||||
!receiverAccessible(tree) ||
|
|
||||||
(tree.getMode() == ReferenceMode.NEW &&
|
|
||||||
tree.kind != ReferenceKind.ARRAY_CTOR &&
|
|
||||||
(tree.sym.owner.isDirectlyOrIndirectlyLocal() || tree.sym.owner.isInner()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a method reference which cannot be used directly into a lambda
|
|
||||||
*/
|
|
||||||
private class MemberReferenceToLambda {
|
|
||||||
|
|
||||||
private final JCMemberReference tree;
|
|
||||||
private final ListBuffer<JCExpression> args = new ListBuffer<>();
|
|
||||||
private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
|
|
||||||
private final MethodSymbol owner = new MethodSymbol(0, names.empty, Type.noType, currentClass);
|
|
||||||
|
|
||||||
private JCExpression receiverExpression = null;
|
|
||||||
|
|
||||||
MemberReferenceToLambda(JCMemberReference tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
JCExpression lambda() {
|
|
||||||
int prevPos = make.pos;
|
|
||||||
try {
|
|
||||||
make.at(tree);
|
|
||||||
|
|
||||||
//body generation - this can be either a method call or a
|
|
||||||
//new instance creation expression, depending on the member reference kind
|
|
||||||
VarSymbol rcvr = addParametersReturnReceiver();
|
|
||||||
JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
|
|
||||||
? expressionInvoke(rcvr)
|
|
||||||
: expressionNew();
|
|
||||||
|
|
||||||
JCLambda slam = make.Lambda(params.toList(), expr);
|
|
||||||
slam.target = tree.target;
|
|
||||||
slam.type = tree.type;
|
|
||||||
slam.pos = tree.pos;
|
|
||||||
slam.wasMethodReference = true;
|
|
||||||
if (receiverExpression != null) {
|
|
||||||
// use a let expression so that the receiver expression is evaluated eagerly
|
|
||||||
return make.at(tree.pos).LetExpr(
|
|
||||||
make.VarDef(rcvr, translate(receiverExpression)), slam).setType(tree.type);
|
|
||||||
} else {
|
|
||||||
return slam;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
make.at(prevPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate the parameter list for the converted member reference.
|
|
||||||
*
|
|
||||||
* @return The receiver variable symbol, if any
|
|
||||||
*/
|
|
||||||
VarSymbol addParametersReturnReceiver() {
|
|
||||||
Type samDesc = types.erasure(types.findDescriptorSymbol(tree.target.tsym).type);
|
|
||||||
List<Type> samPTypes = samDesc.getParameterTypes();
|
|
||||||
List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
|
|
||||||
|
|
||||||
// Determine the receiver, if any
|
|
||||||
VarSymbol rcvr;
|
|
||||||
switch (tree.kind) {
|
|
||||||
case BOUND:
|
|
||||||
// The receiver is explicit in the method reference
|
|
||||||
rcvr = new VarSymbol(SYNTHETIC, names.fromString("rec$"), tree.getQualifierExpression().type, owner);
|
|
||||||
rcvr.pos = tree.pos;
|
|
||||||
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
|
|
||||||
break;
|
|
||||||
case UNBOUND:
|
|
||||||
// The receiver is the first parameter, extract it and
|
|
||||||
// adjust the SAM and unerased type lists accordingly
|
|
||||||
rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
|
|
||||||
samPTypes = samPTypes.tail;
|
|
||||||
descPTypes = descPTypes.tail;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rcvr = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
List<Type> implPTypes = tree.sym.type.getParameterTypes();
|
|
||||||
int implSize = implPTypes.size();
|
|
||||||
int samSize = samPTypes.size();
|
|
||||||
// Last parameter to copy from referenced method, exclude final var args
|
|
||||||
int last = needsVarArgsConversion(tree) ? implSize - 1 : implSize;
|
|
||||||
|
|
||||||
// Failsafe -- assure match-up
|
|
||||||
boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
|
|
||||||
|
|
||||||
// Use parameter types of the implementation method unless the unerased
|
|
||||||
// SAM parameter type is an intersection type, in that case use the
|
|
||||||
// erased SAM parameter type so that the supertype relationship
|
|
||||||
// the implementation method parameters is not obscured.
|
|
||||||
// Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
|
|
||||||
// are used as pointers to the current parameter type information
|
|
||||||
// and are thus not usable afterwards.
|
|
||||||
for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
|
|
||||||
// By default use the implementation method parameter type
|
|
||||||
Type parmType = implPTypes.head;
|
|
||||||
if (checkForIntersection) {
|
|
||||||
if (descPTypes.head.getKind() == TypeKind.INTERSECTION) {
|
|
||||||
parmType = samPTypes.head;
|
|
||||||
}
|
|
||||||
// If the unerased parameter type is a type variable whose
|
|
||||||
// bound is an intersection (eg. <T extends A & B>) then
|
|
||||||
// use the SAM parameter type
|
|
||||||
if (descPTypes.head.getKind() == TypeKind.TYPEVAR) {
|
|
||||||
TypeVar tv = (TypeVar) descPTypes.head;
|
|
||||||
if (tv.getUpperBound().getKind() == TypeKind.INTERSECTION) {
|
|
||||||
parmType = samPTypes.head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addParameter("x$" + i, parmType, true);
|
|
||||||
|
|
||||||
// Advance to the next parameter
|
|
||||||
implPTypes = implPTypes.tail;
|
|
||||||
samPTypes = samPTypes.tail;
|
|
||||||
descPTypes = descPTypes.tail;
|
|
||||||
}
|
|
||||||
// Flatten out the var args
|
|
||||||
for (int i = last; i < samSize; ++i) {
|
|
||||||
addParameter("xva$" + i, tree.varargsElement, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rcvr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCExpression makeReceiver(VarSymbol rcvr) {
|
|
||||||
if (rcvr == null) return null;
|
|
||||||
JCExpression rcvrExpr = make.Ident(rcvr);
|
|
||||||
boolean protAccess =
|
|
||||||
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, currentClass);
|
|
||||||
Type rcvrType = tree.ownerAccessible && !protAccess ? tree.sym.enclClass().type
|
|
||||||
: tree.expr.type;
|
|
||||||
if (rcvrType == syms.arrayClass.type) {
|
|
||||||
// Map the receiver type to the actually type, not just "array"
|
|
||||||
rcvrType = tree.getQualifierExpression().type;
|
|
||||||
}
|
|
||||||
if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
|
|
||||||
rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
|
|
||||||
}
|
|
||||||
return rcvrExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* determine the receiver of the method call - the receiver can
|
|
||||||
* be a type qualifier, the synthetic receiver parameter or 'super'.
|
|
||||||
*/
|
|
||||||
private JCExpression expressionInvoke(VarSymbol rcvr) {
|
|
||||||
JCExpression qualifier =
|
|
||||||
(rcvr != null) ?
|
|
||||||
makeReceiver(rcvr) :
|
|
||||||
tree.getQualifierExpression();
|
|
||||||
|
|
||||||
//create the qualifier expression
|
|
||||||
JCFieldAccess select = make.Select(qualifier, tree.sym.name);
|
|
||||||
select.sym = tree.sym;
|
|
||||||
select.type = tree.sym.erasure(types);
|
|
||||||
|
|
||||||
//create the method call expression
|
|
||||||
JCExpression apply = make.Apply(List.nil(), select,
|
|
||||||
convertArgs(tree.sym, args.toList(), tree.varargsElement)).
|
|
||||||
setType(tree.sym.erasure(types).getReturnType());
|
|
||||||
|
|
||||||
apply = transTypes.coerce(attrEnv, apply,
|
|
||||||
types.erasure(tree.referentType.getReturnType()));
|
|
||||||
|
|
||||||
setVarargsIfNeeded(apply, tree.varargsElement);
|
|
||||||
return apply;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lambda body to use for a 'new'.
|
|
||||||
*/
|
|
||||||
private JCExpression expressionNew() {
|
|
||||||
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
|
|
||||||
//create the array creation expression
|
|
||||||
JCNewArray newArr = make.NewArray(
|
|
||||||
make.Type(types.elemtype(tree.getQualifierExpression().type)),
|
|
||||||
List.of(make.Ident(params.first())),
|
|
||||||
null);
|
|
||||||
newArr.type = tree.getQualifierExpression().type;
|
|
||||||
return newArr;
|
|
||||||
} else {
|
|
||||||
//create the instance creation expression
|
|
||||||
//note that method reference syntax does not allow an explicit
|
|
||||||
//enclosing class (so the enclosing class is null)
|
|
||||||
// but this may need to be patched up later with the proxy for the outer this
|
|
||||||
JCNewClass newClass = make.NewClass(null,
|
|
||||||
List.nil(),
|
|
||||||
make.Type(tree.getQualifierExpression().type),
|
|
||||||
convertArgs(tree.sym, args.toList(), tree.varargsElement),
|
|
||||||
null);
|
|
||||||
newClass.constructor = tree.sym;
|
|
||||||
newClass.constructorType = tree.sym.erasure(types);
|
|
||||||
newClass.type = tree.getQualifierExpression().type;
|
|
||||||
setVarargsIfNeeded(newClass, tree.varargsElement);
|
|
||||||
return newClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private VarSymbol addParameter(String name, Type p, boolean genArg) {
|
|
||||||
VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
|
|
||||||
vsym.pos = tree.pos;
|
|
||||||
params.append(make.VarDef(vsym, null));
|
|
||||||
if (genArg) {
|
|
||||||
args.append(make.Ident(vsym));
|
|
||||||
}
|
|
||||||
return vsym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert method/constructor arguments by inserting appropriate cast
|
|
||||||
* as required by type-erasure - this is needed when bridging a lambda/method
|
|
||||||
* reference, as the bridged signature might require downcast to be compatible
|
|
||||||
* with the generated signature.
|
|
||||||
*/
|
|
||||||
private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
|
|
||||||
Assert.check(meth.kind == MTH);
|
|
||||||
List<Type> formals = types.erasure(meth.type).getParameterTypes();
|
|
||||||
if (varargsElement != null) {
|
|
||||||
Assert.check((meth.flags() & VARARGS) != 0);
|
|
||||||
}
|
|
||||||
return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set varargsElement field on a given tree (must be either a new class tree
|
|
||||||
* or a method call tree)
|
|
||||||
*/
|
|
||||||
private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
|
|
||||||
if (varargsElement != null) {
|
|
||||||
switch (tree.getTag()) {
|
|
||||||
case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
|
|
||||||
case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
|
|
||||||
case TYPECAST: setVarargsIfNeeded(((JCTypeCast) tree).expr, varargsElement); break;
|
|
||||||
default: throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitSwitch(JCSwitch tree) {
|
public void visitSwitch(JCSwitch tree) {
|
||||||
List<JCCase> cases = tree.patternSwitch ? addDefaultIfNeeded(tree.patternSwitch,
|
List<JCCase> cases = tree.patternSwitch ? addDefaultIfNeeded(tree.patternSwitch,
|
||||||
tree.wasEnumSelector,
|
tree.wasEnumSelector,
|
||||||
|
|
|
@ -26,10 +26,13 @@
|
||||||
package com.sun.tools.javac.comp;
|
package com.sun.tools.javac.comp;
|
||||||
|
|
||||||
|
|
||||||
|
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
|
||||||
import com.sun.tools.javac.code.*;
|
import com.sun.tools.javac.code.*;
|
||||||
import com.sun.tools.javac.code.Attribute.TypeCompound;
|
import com.sun.tools.javac.code.Attribute.TypeCompound;
|
||||||
import com.sun.tools.javac.code.Source.Feature;
|
import com.sun.tools.javac.code.Source.Feature;
|
||||||
import com.sun.tools.javac.code.Symbol.*;
|
import com.sun.tools.javac.code.Symbol.*;
|
||||||
|
import com.sun.tools.javac.code.Type.TypeVar;
|
||||||
|
import com.sun.tools.javac.jvm.Target;
|
||||||
import com.sun.tools.javac.tree.*;
|
import com.sun.tools.javac.tree.*;
|
||||||
import com.sun.tools.javac.tree.JCTree.*;
|
import com.sun.tools.javac.tree.JCTree.*;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
|
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
|
||||||
|
@ -46,6 +49,8 @@ import static com.sun.tools.javac.code.TypeTag.VOID;
|
||||||
import static com.sun.tools.javac.comp.CompileStates.CompileState;
|
import static com.sun.tools.javac.comp.CompileStates.CompileState;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCBreak;
|
import com.sun.tools.javac.tree.JCTree.JCBreak;
|
||||||
|
|
||||||
|
import javax.lang.model.type.TypeKind;
|
||||||
|
|
||||||
/** This pass translates Generic Java to conventional Java.
|
/** This pass translates Generic Java to conventional Java.
|
||||||
*
|
*
|
||||||
* <p><b>This is NOT part of any supported API.
|
* <p><b>This is NOT part of any supported API.
|
||||||
|
@ -75,6 +80,7 @@ public class TransTypes extends TreeTranslator {
|
||||||
private Attr attr;
|
private Attr attr;
|
||||||
private final Resolve resolve;
|
private final Resolve resolve;
|
||||||
private final CompileStates compileStates;
|
private final CompileStates compileStates;
|
||||||
|
private final Target target;
|
||||||
|
|
||||||
@SuppressWarnings("this-escape")
|
@SuppressWarnings("this-escape")
|
||||||
protected TransTypes(Context context) {
|
protected TransTypes(Context context) {
|
||||||
|
@ -89,6 +95,7 @@ public class TransTypes extends TreeTranslator {
|
||||||
resolve = Resolve.instance(context);
|
resolve = Resolve.instance(context);
|
||||||
annotate = Annotate.instance(context);
|
annotate = Annotate.instance(context);
|
||||||
attr = Attr.instance(context);
|
attr = Attr.instance(context);
|
||||||
|
target = Target.instance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Construct an attributed tree for a cast of expression to target type,
|
/** Construct an attributed tree for a cast of expression to target type,
|
||||||
|
@ -539,6 +546,268 @@ public class TransTypes extends TreeTranslator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitReference(JCMemberReference tree) {
|
||||||
|
if (needsConversionToLambda(tree)) {
|
||||||
|
// Convert to a lambda, and process as such
|
||||||
|
MemberReferenceToLambda conv = new MemberReferenceToLambda(tree);
|
||||||
|
result = translate(conv.lambda());
|
||||||
|
} else {
|
||||||
|
Type t = types.skipTypeVars(tree.expr.type, false);
|
||||||
|
Type receiverTarget = t.isCompound() ? erasure(tree.sym.owner.type) : erasure(t);
|
||||||
|
if (tree.kind == ReferenceKind.UNBOUND) {
|
||||||
|
tree.expr = make.Type(receiverTarget);
|
||||||
|
} else {
|
||||||
|
tree.expr = translate(tree.expr, receiverTarget);
|
||||||
|
}
|
||||||
|
if (!tree.type.isIntersection()) {
|
||||||
|
tree.type = erasure(tree.type);
|
||||||
|
} else {
|
||||||
|
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
|
||||||
|
}
|
||||||
|
result = tree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// where
|
||||||
|
boolean needsVarArgsConversion(JCMemberReference tree) {
|
||||||
|
return tree.varargsElement != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Is this an array operation like clone()
|
||||||
|
*/
|
||||||
|
boolean isArrayOp(JCMemberReference tree) {
|
||||||
|
return tree.sym.owner == syms.arrayClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean receiverAccessible(JCMemberReference tree) {
|
||||||
|
//hack needed to workaround 292 bug (7087658)
|
||||||
|
//when 292 issue is fixed we should remove this and change the backend
|
||||||
|
//code to always generate a method handle to an accessible method
|
||||||
|
return tree.ownerAccessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erasure destroys the implementation parameter subtype
|
||||||
|
* relationship for intersection types.
|
||||||
|
* Have similar problems for union types too.
|
||||||
|
*/
|
||||||
|
boolean interfaceParameterIsIntersectionOrUnionType(JCMemberReference tree) {
|
||||||
|
List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
|
||||||
|
for (; tl.nonEmpty(); tl = tl.tail) {
|
||||||
|
Type pt = tl.head;
|
||||||
|
if (isIntersectionOrUnionType(pt))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isIntersectionOrUnionType(Type t) {
|
||||||
|
return switch (t.getKind()) {
|
||||||
|
case INTERSECTION, UNION -> true;
|
||||||
|
case TYPEVAR -> {
|
||||||
|
TypeVar tv = (TypeVar) t;
|
||||||
|
yield isIntersectionOrUnionType(tv.getUpperBound());
|
||||||
|
}
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference,
|
||||||
|
Symbol currentClass) {
|
||||||
|
return ((targetReference.flags() & PROTECTED) != 0 &&
|
||||||
|
targetReference.packge() != currentClass.packge());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be called only when target release <= 14
|
||||||
|
* where LambdaMetaFactory does not spin nestmate classes.
|
||||||
|
*
|
||||||
|
* This method should be removed when --release 14 is not supported.
|
||||||
|
*/
|
||||||
|
boolean isPrivateInOtherClass(JCMemberReference tree) {
|
||||||
|
return (tree.sym.flags() & PRIVATE) != 0 &&
|
||||||
|
!types.isSameType(
|
||||||
|
types.erasure(tree.sym.enclClass().asType()),
|
||||||
|
types.erasure(env.enclClass.sym.asType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this reference need to be converted to a lambda
|
||||||
|
* (i.e. var args need to be expanded or "super" is used)
|
||||||
|
*/
|
||||||
|
boolean needsConversionToLambda(JCMemberReference tree) {
|
||||||
|
return interfaceParameterIsIntersectionOrUnionType(tree) ||
|
||||||
|
tree.hasKind(ReferenceKind.SUPER) ||
|
||||||
|
needsVarArgsConversion(tree) ||
|
||||||
|
isArrayOp(tree) ||
|
||||||
|
(!target.runtimeUseNestAccess() && isPrivateInOtherClass(tree)) ||
|
||||||
|
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, env.enclClass.sym) ||
|
||||||
|
!receiverAccessible(tree) ||
|
||||||
|
(tree.getMode() == ReferenceMode.NEW &&
|
||||||
|
tree.kind != ReferenceKind.ARRAY_CTOR &&
|
||||||
|
(tree.sym.owner.isDirectlyOrIndirectlyLocal() || tree.sym.owner.isInner()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a method reference which cannot be used directly into a lambda
|
||||||
|
*/
|
||||||
|
private class MemberReferenceToLambda {
|
||||||
|
|
||||||
|
private final JCMemberReference tree;
|
||||||
|
private final ListBuffer<JCExpression> args = new ListBuffer<>();
|
||||||
|
private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
|
||||||
|
private final MethodSymbol owner = new MethodSymbol(0, names.empty, Type.noType, env.enclClass.sym);
|
||||||
|
|
||||||
|
private JCExpression receiverExpression = null;
|
||||||
|
|
||||||
|
MemberReferenceToLambda(JCMemberReference tree) {
|
||||||
|
this.tree = tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
JCExpression lambda() {
|
||||||
|
int prevPos = make.pos;
|
||||||
|
try {
|
||||||
|
make.at(tree);
|
||||||
|
|
||||||
|
//body generation - this can be either a method call or a
|
||||||
|
//new instance creation expression, depending on the member reference kind
|
||||||
|
VarSymbol rcvr = addParametersReturnReceiver();
|
||||||
|
JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
|
||||||
|
? expressionInvoke(rcvr)
|
||||||
|
: expressionNew();
|
||||||
|
|
||||||
|
JCLambda slam = make.Lambda(params.toList(), expr);
|
||||||
|
slam.target = tree.target;
|
||||||
|
slam.type = tree.type;
|
||||||
|
slam.pos = tree.pos;
|
||||||
|
slam.wasMethodReference = true;
|
||||||
|
if (receiverExpression != null) {
|
||||||
|
// use a let expression so that the receiver expression is evaluated eagerly
|
||||||
|
return make.at(tree.pos).LetExpr(
|
||||||
|
make.VarDef(rcvr, receiverExpression), slam).setType(tree.type);
|
||||||
|
} else {
|
||||||
|
return slam;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
make.at(prevPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the parameter list for the converted member reference.
|
||||||
|
*
|
||||||
|
* @return The receiver variable symbol, if any
|
||||||
|
*/
|
||||||
|
VarSymbol addParametersReturnReceiver() {
|
||||||
|
List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
|
||||||
|
|
||||||
|
// Determine the receiver, if any
|
||||||
|
VarSymbol rcvr;
|
||||||
|
switch (tree.kind) {
|
||||||
|
case BOUND:
|
||||||
|
// The receiver is explicit in the method reference
|
||||||
|
rcvr = new VarSymbol(SYNTHETIC, names.fromString("rec$"), tree.getQualifierExpression().type, owner);
|
||||||
|
rcvr.pos = tree.pos;
|
||||||
|
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
|
||||||
|
break;
|
||||||
|
case UNBOUND:
|
||||||
|
// The receiver is the first parameter, extract it and
|
||||||
|
// adjust the SAM and unerased type lists accordingly
|
||||||
|
rcvr = addParameter("rec$", descPTypes.head, false);
|
||||||
|
descPTypes = descPTypes.tail;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rcvr = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<Type> implPTypes = tree.sym.type.getParameterTypes();
|
||||||
|
int implSize = implPTypes.size();
|
||||||
|
int samSize = descPTypes.size();
|
||||||
|
// Last parameter to copy from referenced method, exclude final var args
|
||||||
|
int last = needsVarArgsConversion(tree) ? implSize - 1 : implSize;
|
||||||
|
|
||||||
|
for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
|
||||||
|
// Use the descriptor parameter type
|
||||||
|
Type parmType = descPTypes.head;
|
||||||
|
addParameter("x$" + i, parmType, true);
|
||||||
|
|
||||||
|
// Advance to the next parameter
|
||||||
|
implPTypes = implPTypes.tail;
|
||||||
|
descPTypes = descPTypes.tail;
|
||||||
|
}
|
||||||
|
// Flatten out the var args
|
||||||
|
for (int i = last; i < samSize; ++i) {
|
||||||
|
addParameter("xva$" + i, tree.varargsElement, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rcvr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determine the receiver of the method call - the receiver can
|
||||||
|
* be a type qualifier, the synthetic receiver parameter or 'super'.
|
||||||
|
*/
|
||||||
|
private JCExpression expressionInvoke(VarSymbol rcvr) {
|
||||||
|
JCExpression qualifier =
|
||||||
|
(rcvr != null) ?
|
||||||
|
make.Ident(rcvr) :
|
||||||
|
tree.getQualifierExpression();
|
||||||
|
|
||||||
|
//create the qualifier expression
|
||||||
|
JCFieldAccess select = make.Select(qualifier, tree.sym.name);
|
||||||
|
select.sym = tree.sym;
|
||||||
|
select.type = tree.referentType;
|
||||||
|
|
||||||
|
//create the method call expression
|
||||||
|
JCExpression apply = make.Apply(List.nil(), select,
|
||||||
|
args.toList()).setType(tree.referentType.getReturnType());
|
||||||
|
|
||||||
|
TreeInfo.setVarargsElement(apply, tree.varargsElement);
|
||||||
|
return apply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lambda body to use for a 'new'.
|
||||||
|
*/
|
||||||
|
private JCExpression expressionNew() {
|
||||||
|
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
|
||||||
|
//create the array creation expression
|
||||||
|
JCNewArray newArr = make.NewArray(
|
||||||
|
make.Type(types.elemtype(tree.getQualifierExpression().type)),
|
||||||
|
List.of(make.Ident(params.first())),
|
||||||
|
null);
|
||||||
|
newArr.type = tree.getQualifierExpression().type;
|
||||||
|
return newArr;
|
||||||
|
} else {
|
||||||
|
//create the instance creation expression
|
||||||
|
//note that method reference syntax does not allow an explicit
|
||||||
|
//enclosing class (so the enclosing class is null)
|
||||||
|
// but this may need to be patched up later with the proxy for the outer this
|
||||||
|
JCNewClass newClass = make.NewClass(null,
|
||||||
|
List.nil(),
|
||||||
|
make.Type(tree.getQualifierExpression().type),
|
||||||
|
args.toList(),
|
||||||
|
null);
|
||||||
|
newClass.constructor = tree.sym;
|
||||||
|
newClass.constructorType = tree.sym.erasure(types);
|
||||||
|
newClass.type = tree.getQualifierExpression().type;
|
||||||
|
TreeInfo.setVarargsElement(newClass, tree.varargsElement);
|
||||||
|
return newClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VarSymbol addParameter(String name, Type p, boolean genArg) {
|
||||||
|
VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
|
||||||
|
vsym.pos = tree.pos;
|
||||||
|
params.append(make.VarDef(vsym, null));
|
||||||
|
if (genArg) {
|
||||||
|
args.append(make.Ident(vsym));
|
||||||
|
}
|
||||||
|
return vsym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void visitSwitch(JCSwitch tree) {
|
public void visitSwitch(JCSwitch tree) {
|
||||||
Type selsuper = types.supertype(tree.selector.type);
|
Type selsuper = types.supertype(tree.selector.type);
|
||||||
boolean enumSwitch = selsuper != null &&
|
boolean enumSwitch = selsuper != null &&
|
||||||
|
@ -860,24 +1129,6 @@ public class TransTypes extends TreeTranslator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitReference(JCMemberReference tree) {
|
|
||||||
Type t = types.skipTypeVars(tree.expr.type, false);
|
|
||||||
Type receiverTarget = t.isCompound() ? erasure(tree.sym.owner.type) : erasure(t);
|
|
||||||
if (tree.kind == ReferenceKind.UNBOUND) {
|
|
||||||
tree.expr = make.Type(receiverTarget);
|
|
||||||
} else {
|
|
||||||
tree.expr = translate(tree.expr, receiverTarget);
|
|
||||||
}
|
|
||||||
if (!tree.type.isIntersection()) {
|
|
||||||
tree.type = erasure(tree.type);
|
|
||||||
} else {
|
|
||||||
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
|
|
||||||
}
|
|
||||||
if (tree.varargsElement != null)
|
|
||||||
tree.varargsElement = erasure(tree.varargsElement);
|
|
||||||
result = tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitTypeArray(JCArrayTypeTree tree) {
|
public void visitTypeArray(JCArrayTypeTree tree) {
|
||||||
tree.elemtype = translate(tree.elemtype, null);
|
tree.elemtype = translate(tree.elemtype, null);
|
||||||
tree.type = erasure(tree.type);
|
tree.type = erasure(tree.type);
|
||||||
|
|
38
test/langtools/tools/javac/SuperInit/MrefDoubleTrans.java
Normal file
38
test/langtools/tools/javac/SuperInit/MrefDoubleTrans.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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 8336320
|
||||||
|
* @summary NullPointerException: Cannot invoke Type.getTag because type is null after JDK-8334037
|
||||||
|
* @compile MrefDoubleTrans.java
|
||||||
|
*/
|
||||||
|
class MrefDoubleTrans {
|
||||||
|
public void f() {
|
||||||
|
Runnable r = new I()::m;
|
||||||
|
}
|
||||||
|
|
||||||
|
class I {
|
||||||
|
void m(Object... xs) {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue