8004969: Generate $deserializeLambda$ method
8006763: super in method reference used in anonymous class - ClassFormatError is produced 8005632: Inner classes within lambdas cause build failures 8005653: Lambdas containing inner classes referencing external type variables do not correctly parameterize the inner classes Reviewed-by: mcimadamore
This commit is contained in:
parent
ff25ecbcbb
commit
b2a3c762ff
@ -126,6 +126,7 @@ public class Symtab {
|
||||
public final Type stringBuilderType;
|
||||
public final Type cloneableType;
|
||||
public final Type serializableType;
|
||||
public final Type serializedLambdaType;
|
||||
public final Type methodHandleType;
|
||||
public final Type methodHandleLookupType;
|
||||
public final Type methodTypeType;
|
||||
@ -458,6 +459,7 @@ public class Symtab {
|
||||
cloneableType = enterClass("java.lang.Cloneable");
|
||||
throwableType = enterClass("java.lang.Throwable");
|
||||
serializableType = enterClass("java.io.Serializable");
|
||||
serializedLambdaType = enterClass("java.lang.invoke.SerializedLambda");
|
||||
methodHandleType = enterClass("java.lang.invoke.MethodHandle");
|
||||
methodHandleLookupType = enterClass("java.lang.invoke.MethodHandles$Lookup");
|
||||
methodTypeType = enterClass("java.lang.invoke.MethodType");
|
||||
@ -514,6 +516,7 @@ public class Symtab {
|
||||
synthesizeEmptyInterfaceIfMissing(cloneableType);
|
||||
synthesizeEmptyInterfaceIfMissing(serializableType);
|
||||
synthesizeEmptyInterfaceIfMissing(lambdaMetafactory);
|
||||
synthesizeEmptyInterfaceIfMissing(serializedLambdaType);
|
||||
synthesizeBoxTypeIfMissing(doubleType);
|
||||
synthesizeBoxTypeIfMissing(floatType);
|
||||
synthesizeBoxTypeIfMissing(voidType);
|
||||
|
@ -48,6 +48,7 @@ import static com.sun.tools.javac.code.Scope.*;
|
||||
import static com.sun.tools.javac.code.Symbol.*;
|
||||
import static com.sun.tools.javac.code.Type.*;
|
||||
import static com.sun.tools.javac.code.TypeTag.*;
|
||||
import static com.sun.tools.javac.jvm.ClassFile.externalize;
|
||||
import static com.sun.tools.javac.util.ListBuffer.lb;
|
||||
|
||||
/**
|
||||
@ -4354,4 +4355,172 @@ public class Types {
|
||||
return vis;
|
||||
}
|
||||
// </editor-fold>
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Signature Generation">
|
||||
|
||||
public static abstract class SignatureGenerator {
|
||||
|
||||
private final Types types;
|
||||
|
||||
protected abstract void append(char ch);
|
||||
protected abstract void append(byte[] ba);
|
||||
protected abstract void append(Name name);
|
||||
protected void classReference(ClassSymbol c) { /* by default: no-op */ }
|
||||
|
||||
protected SignatureGenerator(Types types) {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble signature of given type in string buffer.
|
||||
*/
|
||||
public void assembleSig(Type type) {
|
||||
type = type.unannotatedType();
|
||||
switch (type.getTag()) {
|
||||
case BYTE:
|
||||
append('B');
|
||||
break;
|
||||
case SHORT:
|
||||
append('S');
|
||||
break;
|
||||
case CHAR:
|
||||
append('C');
|
||||
break;
|
||||
case INT:
|
||||
append('I');
|
||||
break;
|
||||
case LONG:
|
||||
append('J');
|
||||
break;
|
||||
case FLOAT:
|
||||
append('F');
|
||||
break;
|
||||
case DOUBLE:
|
||||
append('D');
|
||||
break;
|
||||
case BOOLEAN:
|
||||
append('Z');
|
||||
break;
|
||||
case VOID:
|
||||
append('V');
|
||||
break;
|
||||
case CLASS:
|
||||
append('L');
|
||||
assembleClassSig(type);
|
||||
append(';');
|
||||
break;
|
||||
case ARRAY:
|
||||
ArrayType at = (ArrayType) type;
|
||||
append('[');
|
||||
assembleSig(at.elemtype);
|
||||
break;
|
||||
case METHOD:
|
||||
MethodType mt = (MethodType) type;
|
||||
append('(');
|
||||
assembleSig(mt.argtypes);
|
||||
append(')');
|
||||
assembleSig(mt.restype);
|
||||
if (hasTypeVar(mt.thrown)) {
|
||||
for (List<Type> l = mt.thrown; l.nonEmpty(); l = l.tail) {
|
||||
append('^');
|
||||
assembleSig(l.head);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WILDCARD: {
|
||||
Type.WildcardType ta = (Type.WildcardType) type;
|
||||
switch (ta.kind) {
|
||||
case SUPER:
|
||||
append('-');
|
||||
assembleSig(ta.type);
|
||||
break;
|
||||
case EXTENDS:
|
||||
append('+');
|
||||
assembleSig(ta.type);
|
||||
break;
|
||||
case UNBOUND:
|
||||
append('*');
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(ta.kind);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPEVAR:
|
||||
append('T');
|
||||
append(type.tsym.name);
|
||||
append(';');
|
||||
break;
|
||||
case FORALL:
|
||||
Type.ForAll ft = (Type.ForAll) type;
|
||||
assembleParamsSig(ft.tvars);
|
||||
assembleSig(ft.qtype);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("typeSig " + type.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTypeVar(List<Type> l) {
|
||||
while (l.nonEmpty()) {
|
||||
if (l.head.hasTag(TypeTag.TYPEVAR)) {
|
||||
return true;
|
||||
}
|
||||
l = l.tail;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void assembleClassSig(Type type) {
|
||||
type = type.unannotatedType();
|
||||
ClassType ct = (ClassType) type;
|
||||
ClassSymbol c = (ClassSymbol) ct.tsym;
|
||||
classReference(c);
|
||||
Type outer = ct.getEnclosingType();
|
||||
if (outer.allparams().nonEmpty()) {
|
||||
boolean rawOuter =
|
||||
c.owner.kind == Kinds.MTH || // either a local class
|
||||
c.name == types.names.empty; // or anonymous
|
||||
assembleClassSig(rawOuter
|
||||
? types.erasure(outer)
|
||||
: outer);
|
||||
append('.');
|
||||
Assert.check(c.flatname.startsWith(c.owner.enclClass().flatname));
|
||||
append(rawOuter
|
||||
? c.flatname.subName(c.owner.enclClass().flatname.getByteLength() + 1, c.flatname.getByteLength())
|
||||
: c.name);
|
||||
} else {
|
||||
append(externalize(c.flatname));
|
||||
}
|
||||
if (ct.getTypeArguments().nonEmpty()) {
|
||||
append('<');
|
||||
assembleSig(ct.getTypeArguments());
|
||||
append('>');
|
||||
}
|
||||
}
|
||||
|
||||
public void assembleParamsSig(List<Type> typarams) {
|
||||
append('<');
|
||||
for (List<Type> ts = typarams; ts.nonEmpty(); ts = ts.tail) {
|
||||
Type.TypeVar tvar = (Type.TypeVar) ts.head;
|
||||
append(tvar.tsym.name);
|
||||
List<Type> bounds = types.getBounds(tvar);
|
||||
if ((bounds.head.tsym.flags() & INTERFACE) != 0) {
|
||||
append(':');
|
||||
}
|
||||
for (List<Type> l = bounds; l.nonEmpty(); l = l.tail) {
|
||||
append(':');
|
||||
assembleSig(l.head);
|
||||
}
|
||||
}
|
||||
append('>');
|
||||
}
|
||||
|
||||
private void assembleSig(List<Type> types) {
|
||||
for (List<Type> ts = types; ts.nonEmpty(); ts = ts.tail) {
|
||||
assembleSig(ts.head);
|
||||
}
|
||||
}
|
||||
}
|
||||
// </editor-fold>
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
|
||||
import com.sun.tools.javac.tree.TreeMaker;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
import com.sun.tools.javac.tree.TreeTranslator;
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.code.Kinds;
|
||||
import com.sun.tools.javac.code.Scope;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
|
||||
@ -57,9 +57,7 @@ import java.util.Map;
|
||||
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.*;
|
||||
import static com.sun.tools.javac.code.TypeTag.BOT;
|
||||
import static com.sun.tools.javac.code.TypeTag.NONE;
|
||||
import static com.sun.tools.javac.code.TypeTag.VOID;
|
||||
import static com.sun.tools.javac.code.TypeTag.*;
|
||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||
|
||||
/**
|
||||
@ -89,9 +87,51 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
/** current translation context (visitor argument) */
|
||||
private TranslationContext<?> context;
|
||||
|
||||
/** list of translated methods
|
||||
**/
|
||||
private ListBuffer<JCTree> translatedMethodList;
|
||||
/** info about the current class being processed */
|
||||
private KlassInfo kInfo;
|
||||
|
||||
/** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
|
||||
public static final int FLAG_SERIALIZABLE = 1 << 0;
|
||||
|
||||
/** Flag for alternate metafactories indicating the lambda object has multiple targets */
|
||||
public static final int FLAG_MARKERS = 1 << 1;
|
||||
|
||||
private class KlassInfo {
|
||||
|
||||
/**
|
||||
* list of methods to append
|
||||
*/
|
||||
private ListBuffer<JCTree> appendedMethodList;
|
||||
|
||||
/**
|
||||
* list of deserialization cases
|
||||
*/
|
||||
private final Map<String, ListBuffer<JCStatement>> deserializeCases;
|
||||
|
||||
/**
|
||||
* deserialize method symbol
|
||||
*/
|
||||
private final MethodSymbol deserMethodSym;
|
||||
|
||||
/**
|
||||
* deserialize method parameter symbol
|
||||
*/
|
||||
private final VarSymbol deserParamSym;
|
||||
|
||||
private KlassInfo(Symbol kSym) {
|
||||
appendedMethodList = ListBuffer.lb();
|
||||
deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
|
||||
long flags = PRIVATE | STATIC | SYNTHETIC;
|
||||
MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
|
||||
List.<Type>nil(), syms.methodClass);
|
||||
deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
|
||||
deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), syms.serializedLambdaType, deserMethodSym);
|
||||
}
|
||||
|
||||
private void addMethod(JCTree decl) {
|
||||
appendedMethodList = appendedMethodList.prepend(decl);
|
||||
}
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Instantiating">
|
||||
private static final Context.Key<LambdaToMethod> unlambdaKey =
|
||||
@ -112,11 +152,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
make = TreeMaker.instance(context);
|
||||
types = Types.instance(context);
|
||||
transTypes = TransTypes.instance(context);
|
||||
this.analyzer = makeAnalyzer();
|
||||
}
|
||||
|
||||
private LambdaAnalyzer makeAnalyzer() {
|
||||
return new LambdaAnalyzer();
|
||||
analyzer = new LambdaAnalyzer();
|
||||
}
|
||||
// </editor-fold>
|
||||
|
||||
@ -168,18 +204,22 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
//analyze class
|
||||
analyzer.analyzeClass(tree);
|
||||
}
|
||||
ListBuffer<JCTree> prevTranslated = translatedMethodList;
|
||||
KlassInfo prevKlassInfo = kInfo;
|
||||
try {
|
||||
translatedMethodList = ListBuffer.lb();
|
||||
kInfo = new KlassInfo(tree.sym);
|
||||
super.visitClassDef(tree);
|
||||
if (!kInfo.deserializeCases.isEmpty()) {
|
||||
kInfo.addMethod(makeDeserializeMethod(tree.sym));
|
||||
}
|
||||
//add all translated instance methods here
|
||||
tree.defs = tree.defs.appendList(translatedMethodList.toList());
|
||||
for (JCTree lambda : translatedMethodList) {
|
||||
List<JCTree> newMethods = kInfo.appendedMethodList.toList();
|
||||
tree.defs = tree.defs.appendList(newMethods);
|
||||
for (JCTree lambda : newMethods) {
|
||||
tree.sym.members().enter(((JCMethodDecl)lambda).sym);
|
||||
}
|
||||
result = tree;
|
||||
} finally {
|
||||
translatedMethodList = prevTranslated;
|
||||
kInfo = prevKlassInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +257,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
|
||||
|
||||
//Add the method to the list of methods to be added to this class.
|
||||
translatedMethodList = translatedMethodList.prepend(lambdaDecl);
|
||||
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
|
||||
@ -234,7 +274,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
|
||||
if (!sym.isStatic()) {
|
||||
syntheticInits.append(makeThis(
|
||||
sym.owner.asType(),
|
||||
sym.owner.enclClass().asType(),
|
||||
localContext.owner.enclClass()));
|
||||
}
|
||||
|
||||
@ -253,7 +293,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
int refKind = referenceKind(sym);
|
||||
|
||||
//convert to an invokedynamic call
|
||||
result = makeMetaFactoryIndyCall(tree, refKind, sym, indy_args);
|
||||
result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
|
||||
}
|
||||
|
||||
private JCIdent makeThis(Type type, Symbol owner) {
|
||||
@ -291,8 +331,8 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
case IMPLICIT_INNER: /** Inner :: new */
|
||||
case SUPER: /** super :: instMethod */
|
||||
init = makeThis(
|
||||
localContext.owner.owner.asType(),
|
||||
localContext.owner);
|
||||
localContext.owner.enclClass().asType(),
|
||||
localContext.owner.enclClass());
|
||||
break;
|
||||
|
||||
case BOUND: /** Expr :: instMethod */
|
||||
@ -314,7 +354,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
|
||||
|
||||
//build a sam instance using an indy call to the meta-factory
|
||||
result = makeMetaFactoryIndyCall(tree, localContext.referenceKind(), refSym, indy_args);
|
||||
result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,6 +373,9 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
} else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
|
||||
Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
|
||||
result = make.Ident(translatedSym).setType(tree.type);
|
||||
} else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
|
||||
Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
|
||||
result = make.Ident(translatedSym).setType(translatedSym.type);
|
||||
} else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
|
||||
Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
|
||||
result = make.Ident(translatedSym).setType(tree.type);
|
||||
@ -362,6 +405,16 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
|
||||
JCExpression init = translate(tree.init);
|
||||
result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
|
||||
} else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
|
||||
JCExpression init = translate(tree.init);
|
||||
VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
|
||||
result = make.VarDef(xsym, init);
|
||||
// Replace the entered symbol for this variable
|
||||
Scope sc = tree.sym.owner.members();
|
||||
if (sc != null) {
|
||||
sc.remove(tree.sym);
|
||||
sc.enter(xsym);
|
||||
}
|
||||
} else {
|
||||
super.visitVarDef(tree);
|
||||
}
|
||||
@ -451,6 +504,135 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
return trans_block;
|
||||
}
|
||||
|
||||
private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
|
||||
ListBuffer<JCCase> cases = ListBuffer.lb();
|
||||
ListBuffer<JCBreak> breaks = ListBuffer.lb();
|
||||
for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
|
||||
JCBreak br = make.Break(null);
|
||||
breaks.add(br);
|
||||
List<JCStatement> stmts = entry.getValue().append(br).toList();
|
||||
cases.add(make.Case(make.Literal(entry.getKey()), stmts));
|
||||
}
|
||||
JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
|
||||
for (JCBreak br : breaks) {
|
||||
br.target = sw;
|
||||
}
|
||||
JCBlock body = make.Block(0L, List.<JCStatement>of(
|
||||
sw,
|
||||
make.Throw(makeNewClass(
|
||||
syms.illegalArgumentExceptionType,
|
||||
List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
|
||||
JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
|
||||
names.deserializeLambda,
|
||||
make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
|
||||
List.<JCTypeParameter>nil(),
|
||||
List.of(make.VarDef(kInfo.deserParamSym, null)),
|
||||
List.<JCExpression>nil(),
|
||||
body,
|
||||
null);
|
||||
deser.sym = kInfo.deserMethodSym;
|
||||
deser.type = kInfo.deserMethodSym.type;
|
||||
//System.err.printf("DESER: '%s'\n", deser);
|
||||
return deser;
|
||||
}
|
||||
|
||||
/** Make an attributed class instance creation expression.
|
||||
* @param ctype The class type.
|
||||
* @param args The constructor arguments.
|
||||
*/
|
||||
JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
|
||||
JCNewClass tree = make.NewClass(null,
|
||||
null, make.QualIdent(ctype.tsym), args, null);
|
||||
tree.constructor = rs.resolveConstructor(
|
||||
null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
|
||||
tree.type = ctype;
|
||||
return tree;
|
||||
}
|
||||
|
||||
private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
|
||||
DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
|
||||
String functionalInterfaceClass = classSig(targetType);
|
||||
String functionalInterfaceMethodName = samSym.getSimpleName().toString();
|
||||
String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
|
||||
String implClass = classSig(refSym.owner.type);
|
||||
String implMethodName = refSym.getQualifiedName().toString();
|
||||
String implMethodSignature = methodSig(types.erasure(refSym.type));
|
||||
|
||||
JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
|
||||
ListBuffer<JCExpression> serArgs = ListBuffer.lb();
|
||||
int i = 0;
|
||||
for (Type t : indyType.getParameterTypes()) {
|
||||
List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
|
||||
List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
|
||||
serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
|
||||
++i;
|
||||
}
|
||||
JCStatement stmt = make.If(
|
||||
deserTest(deserTest(deserTest(deserTest(deserTest(
|
||||
kindTest,
|
||||
"getFunctionalInterfaceClass", functionalInterfaceClass),
|
||||
"getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
|
||||
"getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
|
||||
"getImplClass", implClass),
|
||||
"getImplMethodSignature", implMethodSignature),
|
||||
make.Return(makeIndyCall(
|
||||
pos,
|
||||
syms.lambdaMetafactory,
|
||||
names.altMetaFactory,
|
||||
staticArgs, indyType, serArgs.toList())),
|
||||
null);
|
||||
ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
|
||||
if (stmts == null) {
|
||||
stmts = ListBuffer.lb();
|
||||
kInfo.deserializeCases.put(implMethodName, stmts);
|
||||
}
|
||||
/****
|
||||
System.err.printf("+++++++++++++++++\n");
|
||||
System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
|
||||
System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
|
||||
System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
|
||||
System.err.printf("*implMethodKind: %d\n", implMethodKind);
|
||||
System.err.printf("*implClass: '%s'\n", implClass);
|
||||
System.err.printf("*implMethodName: '%s'\n", implMethodName);
|
||||
System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
|
||||
****/
|
||||
stmts.append(stmt);
|
||||
}
|
||||
|
||||
private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
|
||||
JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
|
||||
testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
|
||||
testExpr.setType(syms.booleanType);
|
||||
return testExpr;
|
||||
}
|
||||
|
||||
private JCExpression deserTest(JCExpression prev, String func, String lit) {
|
||||
MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
|
||||
Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
|
||||
JCMethodInvocation eqtest = make.Apply(
|
||||
List.<JCExpression>nil(),
|
||||
make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
|
||||
List.<JCExpression>of(make.Literal(lit)));
|
||||
eqtest.setType(syms.booleanType);
|
||||
JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
|
||||
compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
|
||||
compound.setType(syms.booleanType);
|
||||
return compound;
|
||||
}
|
||||
|
||||
private JCExpression deserGetter(String func, Type type) {
|
||||
return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
|
||||
}
|
||||
|
||||
private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
|
||||
MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
|
||||
Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
|
||||
return make.Apply(
|
||||
List.<JCExpression>nil(),
|
||||
make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
|
||||
args).setType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new synthetic method with given flags, name, type, owner
|
||||
*/
|
||||
@ -678,14 +860,14 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
* * super is used
|
||||
*/
|
||||
private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
|
||||
JCMethodDecl bridgeDecl = (new MemberReferenceBridger(tree, localContext).bridge());
|
||||
translatedMethodList = translatedMethodList.prepend(bridgeDecl);
|
||||
kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an indy method call to the meta factory
|
||||
*/
|
||||
private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, int refKind, Symbol refSym, List<JCExpression> indy_args) {
|
||||
private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
|
||||
boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
|
||||
//determine the static bsm args
|
||||
Type mtype = types.erasure(tree.descriptorType);
|
||||
MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
|
||||
@ -709,7 +891,31 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
List.<Type>nil(),
|
||||
syms.methodClass);
|
||||
|
||||
return makeIndyCall(tree, syms.lambdaMetafactory, names.metaFactory, staticArgs, indyType, indy_args);
|
||||
Name metafactoryName = needsAltMetafactory ?
|
||||
names.altMetaFactory : names.metaFactory;
|
||||
|
||||
if (needsAltMetafactory) {
|
||||
ListBuffer<Object> markers = ListBuffer.lb();
|
||||
for (Symbol t : tree.targets.tail) {
|
||||
if (t != syms.serializableType.tsym) {
|
||||
markers.append(t);
|
||||
}
|
||||
}
|
||||
int flags = isSerializable? FLAG_SERIALIZABLE : 0;
|
||||
boolean hasMarkers = markers.nonEmpty();
|
||||
flags |= hasMarkers ? FLAG_MARKERS : 0;
|
||||
staticArgs = staticArgs.append(flags);
|
||||
if (hasMarkers) {
|
||||
staticArgs = staticArgs.append(markers.length());
|
||||
staticArgs = staticArgs.appendList(markers.toList());
|
||||
}
|
||||
if (isSerializable) {
|
||||
addDeserializationCase(refKind, refSym, tree.type, samSym,
|
||||
tree, staticArgs, indyType);
|
||||
}
|
||||
}
|
||||
|
||||
return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -795,6 +1001,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
|
||||
@ -814,6 +1021,20 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
*/
|
||||
private int lambdaCount = 0;
|
||||
|
||||
/**
|
||||
* keep the count of lambda expression defined in given context (used to
|
||||
* generate unambiguous names for serializable lambdas)
|
||||
*/
|
||||
private Map<String, Integer> serializableLambdaCounts =
|
||||
new HashMap<String, Integer>();
|
||||
|
||||
/**
|
||||
* maps for fake clinit symbols to be used as owners of lambda occurring in
|
||||
* a static var init context
|
||||
*/
|
||||
private Map<ClassSymbol, Symbol> clinits =
|
||||
new HashMap<ClassSymbol, Symbol>();
|
||||
|
||||
private void analyzeClass(JCClassDecl tree) {
|
||||
frameStack = List.nil();
|
||||
scan(tree);
|
||||
@ -836,21 +1057,26 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
@Override
|
||||
public void visitClassDef(JCClassDecl tree) {
|
||||
List<Frame> prevStack = frameStack;
|
||||
Map<String, Integer> prevSerializableLambdaCount = serializableLambdaCounts;
|
||||
Map<ClassSymbol, Symbol> prevClinits = clinits;
|
||||
try {
|
||||
if (frameStack.nonEmpty() && enclosingLambda() != null) {
|
||||
serializableLambdaCounts = new HashMap<String, Integer>();
|
||||
prevClinits = new HashMap<ClassSymbol, Symbol>();
|
||||
if (directlyEnclosingLambda() != null) {
|
||||
tree.sym.owner = owner();
|
||||
LambdaTranslationContext lambdaContext = (LambdaTranslationContext)contextMap.get(enclosingLambda());
|
||||
LambdaTranslationContext lambdaContext = (LambdaTranslationContext) contextMap.get(directlyEnclosingLambda());
|
||||
Type encl = lambdaContext.enclosingType();
|
||||
if (encl.hasTag(NONE)) {
|
||||
//if the translated lambda body occurs in a static context,
|
||||
//any class declaration within it must be made static
|
||||
//@@@TODO: What about nested classes within lambda?
|
||||
tree.sym.flags_field |= STATIC;
|
||||
((ClassType)tree.sym.type).setEnclosingType(Type.noType);
|
||||
((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);
|
||||
((ClassType) tree.sym.type).setEnclosingType(encl);
|
||||
}
|
||||
}
|
||||
frameStack = frameStack.prepend(new Frame(tree));
|
||||
@ -858,8 +1084,10 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
}
|
||||
finally {
|
||||
frameStack = prevStack;
|
||||
serializableLambdaCounts = prevSerializableLambdaCount;
|
||||
clinits = prevClinits;
|
||||
}
|
||||
if (!tree.sym.isStatic() && frameStack.nonEmpty() && enclosingLambda() != null) {
|
||||
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);
|
||||
@ -868,9 +1096,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
|
||||
@Override
|
||||
public void visitIdent(JCIdent tree) {
|
||||
if (context() == null || !lambdaIdentSymbolFilter(tree.sym)) {
|
||||
super.visitIdent(tree);
|
||||
} else {
|
||||
if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
|
||||
if (tree.sym.kind == VAR &&
|
||||
tree.sym.owner.kind == MTH &&
|
||||
tree.type.constValue() == null) {
|
||||
@ -902,6 +1128,7 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitIdent(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -969,9 +1196,22 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
|
||||
@Override
|
||||
public void visitVarDef(JCVariableDecl tree) {
|
||||
if (frameStack.head.tree.hasTag(LAMBDA)) {
|
||||
((LambdaTranslationContext)context()).addSymbol(tree.sym, LOCAL_VAR);
|
||||
TranslationContext<?> context = context();
|
||||
LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
|
||||
(LambdaTranslationContext)context :
|
||||
null;
|
||||
if (ltc != null) {
|
||||
if (frameStack.head.tree.hasTag(LAMBDA)) {
|
||||
ltc.addSymbol(tree.sym, LOCAL_VAR);
|
||||
}
|
||||
// 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();
|
||||
if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
|
||||
ltc.addSymbol(tree.sym, TYPE_VAR);
|
||||
}
|
||||
}
|
||||
|
||||
List<Frame> prevStack = frameStack;
|
||||
try {
|
||||
if (tree.sym.owner.kind == MTH) {
|
||||
@ -986,7 +1226,25 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
}
|
||||
|
||||
private Name lambdaName() {
|
||||
return names.lambda.append(names.fromString("$" + lambdaCount++));
|
||||
return names.lambda.append(names.fromString("" + lambdaCount++));
|
||||
}
|
||||
|
||||
private Name serializedLambdaName(Symbol owner) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(names.lambda);
|
||||
buf.append(owner.name);
|
||||
buf.append('$');
|
||||
int methTypeHash = methodSig(owner.type).hashCode();
|
||||
buf.append(methTypeHash);
|
||||
buf.append('$');
|
||||
String temp = buf.toString();
|
||||
Integer count = serializableLambdaCounts.get(temp);
|
||||
if (count == null) {
|
||||
count = 0;
|
||||
}
|
||||
buf.append(count++);
|
||||
serializableLambdaCounts.put(temp, count);
|
||||
return names.fromString(buf.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1008,10 +1266,12 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
break;
|
||||
}
|
||||
JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
|
||||
return makeSyntheticMethod(((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC, names.empty, null, cdecl.sym);
|
||||
return initSym(cdecl.sym,
|
||||
((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
|
||||
case BLOCK:
|
||||
JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
|
||||
return makeSyntheticMethod(((JCBlock)frameStack2.head.tree).flags & STATIC | Flags.BLOCK, names.empty, null, cdecl2.sym);
|
||||
return initSym(cdecl2.sym,
|
||||
((JCBlock)frameStack2.head.tree).flags & STATIC);
|
||||
case CLASSDEF:
|
||||
return ((JCClassDecl)frameStack2.head.tree).sym;
|
||||
case METHODDEF:
|
||||
@ -1027,7 +1287,33 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
return null;
|
||||
}
|
||||
|
||||
private JCTree enclosingLambda() {
|
||||
private Symbol initSym(ClassSymbol csym, long flags) {
|
||||
boolean isStatic = (flags & STATIC) != 0;
|
||||
if (isStatic) {
|
||||
//static clinits are generated in Gen - so we need to fake them
|
||||
Symbol clinit = clinits.get(csym);
|
||||
if (clinit == null) {
|
||||
clinit = makeSyntheticMethod(STATIC,
|
||||
names.clinit,
|
||||
new MethodType(List.<Type>nil(), syms.voidType, List.<Type>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.getElementsByName(names.init)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
Assert.error("init not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
private JCTree directlyEnclosingLambda() {
|
||||
if (frameStack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<Frame> frameStack2 = frameStack;
|
||||
while (frameStack2.nonEmpty()) {
|
||||
switch (frameStack2.head.tree.getTag()) {
|
||||
@ -1044,6 +1330,28 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean inClassWithinLambda() {
|
||||
if (frameStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
List<Frame> 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
|
||||
@ -1178,6 +1486,22 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
this.depth = frameStack.size() - 1;
|
||||
this.prev = context();
|
||||
}
|
||||
|
||||
/** does this functional expression need to be created using alternate metafactory? */
|
||||
boolean needsAltMetafactory() {
|
||||
return (tree.targets.length() > 1 ||
|
||||
isSerializable());
|
||||
}
|
||||
|
||||
/** does this functional expression require serialization support? */
|
||||
boolean isSerializable() {
|
||||
for (Symbol target : tree.targets) {
|
||||
if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1203,6 +1527,9 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
/** map from class symbols to translated synthetic parameters (for captured member access) */
|
||||
Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
|
||||
|
||||
/** map from original to translated lambda locals */
|
||||
Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
|
||||
|
||||
/** the synthetic symbol for the method hoisting the translated lambda */
|
||||
Symbol translatedSym;
|
||||
|
||||
@ -1214,7 +1541,8 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
if (frame.tree.hasTag(VARDEF)) {
|
||||
self = ((JCVariableDecl)frame.tree).sym;
|
||||
}
|
||||
this.translatedSym = makeSyntheticMethod(0, lambdaName(), null, owner.enclClass());
|
||||
Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
|
||||
this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1222,10 +1550,14 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
* synthetic lambda body
|
||||
*/
|
||||
Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
|
||||
if (skind == CAPTURED_THIS) {
|
||||
return sym; // self represented
|
||||
} else {
|
||||
return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
|
||||
switch (skind) {
|
||||
case CAPTURED_THIS:
|
||||
return sym; // self represented
|
||||
case TYPE_VAR:
|
||||
// Just erase the type var
|
||||
return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner);
|
||||
default:
|
||||
return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1249,6 +1581,10 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
transMap = lambdaParams;
|
||||
preferredName = sym.name.toString();
|
||||
break;
|
||||
case TYPE_VAR:
|
||||
transMap = typeVars;
|
||||
preferredName = sym.name.toString();
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
if (!transMap.containsKey(sym)) {
|
||||
@ -1272,6 +1608,9 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
case PARAM:
|
||||
translationMap.putAll(lambdaParams);
|
||||
break;
|
||||
case TYPE_VAR:
|
||||
translationMap.putAll(typeVars);
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@ -1311,8 +1650,8 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
syntheticParams = params.toList();
|
||||
|
||||
//prepend synthetic args to translated lambda method signature
|
||||
translatedSym.type = (MethodType) types.createMethodTypeWithParameters(
|
||||
(MethodType) generatedLambdaSig(),
|
||||
translatedSym.type = types.createMethodTypeWithParameters(
|
||||
generatedLambdaSig(),
|
||||
TreeInfo.types(syntheticParams));
|
||||
}
|
||||
|
||||
@ -1389,6 +1728,60 @@ public class LambdaToMethod extends TreeTranslator {
|
||||
CAPTURED_VAR,
|
||||
CAPTURED_THIS,
|
||||
LOCAL_VAR,
|
||||
PARAM;
|
||||
PARAM,
|
||||
TYPE_VAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* ****************************************************************
|
||||
* Signature Generation
|
||||
* ****************************************************************
|
||||
*/
|
||||
|
||||
private String methodSig(Type type) {
|
||||
L2MSignatureGenerator sg = new L2MSignatureGenerator();
|
||||
sg.assembleSig(type);
|
||||
return sg.toString();
|
||||
}
|
||||
|
||||
private String classSig(Type type) {
|
||||
L2MSignatureGenerator sg = new L2MSignatureGenerator();
|
||||
sg.assembleClassSig(type);
|
||||
return sg.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signature Generation
|
||||
*/
|
||||
private class L2MSignatureGenerator extends Types.SignatureGenerator {
|
||||
|
||||
/**
|
||||
* An output buffer for type signatures.
|
||||
*/
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
L2MSignatureGenerator() {
|
||||
super(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void append(char ch) {
|
||||
sb.append(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void append(byte[] ba) {
|
||||
sb.append(new String(ba));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void append(Name name) {
|
||||
sb.append(name.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -309,7 +309,9 @@ public class ClassReader implements Completer {
|
||||
/** Add member to class unless it is synthetic.
|
||||
*/
|
||||
private void enterMember(ClassSymbol c, Symbol sym) {
|
||||
if ((sym.flags_field & (SYNTHETIC|BRIDGE)) != SYNTHETIC)
|
||||
// Synthetic members are not entered -- reason lost to history (optimization?).
|
||||
// Lambda methods must be entered because they may have inner classes (which reference them)
|
||||
if ((sym.flags_field & (SYNTHETIC|BRIDGE)) != SYNTHETIC || sym.name.startsWith(names.lambda))
|
||||
c.members_field.enter(sym);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,9 @@ import javax.tools.JavaFileObject;
|
||||
import com.sun.tools.javac.code.*;
|
||||
import com.sun.tools.javac.code.Attribute.RetentionPolicy;
|
||||
import com.sun.tools.javac.code.Attribute.TypeCompound;
|
||||
import static com.sun.tools.javac.code.BoundKind.EXTENDS;
|
||||
import static com.sun.tools.javac.code.BoundKind.SUPER;
|
||||
import static com.sun.tools.javac.code.BoundKind.UNBOUND;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.code.Type.*;
|
||||
import com.sun.tools.javac.code.Types.UniqueType;
|
||||
@ -126,10 +129,6 @@ public class ClassWriter extends ClassFile {
|
||||
*/
|
||||
ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE);
|
||||
|
||||
/** An output buffer for type signatures.
|
||||
*/
|
||||
ByteBuffer sigbuf = new ByteBuffer();
|
||||
|
||||
/** The constant pool.
|
||||
*/
|
||||
Pool pool;
|
||||
@ -158,6 +157,9 @@ public class ClassWriter extends ClassFile {
|
||||
/** Access to files. */
|
||||
private final JavaFileManager fileManager;
|
||||
|
||||
/** Sole signature generator */
|
||||
private final CWSignatureGenerator signatureGen;
|
||||
|
||||
/** The tags and constants used in compressed stackmap. */
|
||||
static final int SAME_FRAME_SIZE = 64;
|
||||
static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
|
||||
@ -185,6 +187,7 @@ public class ClassWriter extends ClassFile {
|
||||
source = Source.instance(context);
|
||||
types = Types.instance(context);
|
||||
fileManager = context.get(JavaFileManager.class);
|
||||
signatureGen = new CWSignatureGenerator(types);
|
||||
|
||||
verbose = options.isSet(VERBOSE);
|
||||
scramble = options.isSet("-scramble");
|
||||
@ -270,172 +273,81 @@ public class ClassWriter extends ClassFile {
|
||||
buf.elems[adr+3] = (byte)((x ) & 0xFF);
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* Signature Generation
|
||||
******************************************************************/
|
||||
|
||||
/** Assemble signature of given type in string buffer.
|
||||
/**
|
||||
* Signature Generation
|
||||
*/
|
||||
void assembleSig(Type type) {
|
||||
type = type.unannotatedType();
|
||||
switch (type.getTag()) {
|
||||
case BYTE:
|
||||
sigbuf.appendByte('B');
|
||||
break;
|
||||
case SHORT:
|
||||
sigbuf.appendByte('S');
|
||||
break;
|
||||
case CHAR:
|
||||
sigbuf.appendByte('C');
|
||||
break;
|
||||
case INT:
|
||||
sigbuf.appendByte('I');
|
||||
break;
|
||||
case LONG:
|
||||
sigbuf.appendByte('J');
|
||||
break;
|
||||
case FLOAT:
|
||||
sigbuf.appendByte('F');
|
||||
break;
|
||||
case DOUBLE:
|
||||
sigbuf.appendByte('D');
|
||||
break;
|
||||
case BOOLEAN:
|
||||
sigbuf.appendByte('Z');
|
||||
break;
|
||||
case VOID:
|
||||
sigbuf.appendByte('V');
|
||||
break;
|
||||
case CLASS:
|
||||
sigbuf.appendByte('L');
|
||||
assembleClassSig(type);
|
||||
sigbuf.appendByte(';');
|
||||
break;
|
||||
case ARRAY:
|
||||
ArrayType at = (ArrayType)type;
|
||||
sigbuf.appendByte('[');
|
||||
assembleSig(at.elemtype);
|
||||
break;
|
||||
case METHOD:
|
||||
MethodType mt = (MethodType)type;
|
||||
sigbuf.appendByte('(');
|
||||
assembleSig(mt.argtypes);
|
||||
sigbuf.appendByte(')');
|
||||
assembleSig(mt.restype);
|
||||
if (hasTypeVar(mt.thrown)) {
|
||||
for (List<Type> l = mt.thrown; l.nonEmpty(); l = l.tail) {
|
||||
sigbuf.appendByte('^');
|
||||
assembleSig(l.head);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WILDCARD: {
|
||||
WildcardType ta = (WildcardType) type;
|
||||
switch (ta.kind) {
|
||||
case SUPER:
|
||||
sigbuf.appendByte('-');
|
||||
assembleSig(ta.type);
|
||||
break;
|
||||
case EXTENDS:
|
||||
sigbuf.appendByte('+');
|
||||
assembleSig(ta.type);
|
||||
break;
|
||||
case UNBOUND:
|
||||
sigbuf.appendByte('*');
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(ta.kind);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPEVAR:
|
||||
sigbuf.appendByte('T');
|
||||
sigbuf.appendName(type.tsym.name);
|
||||
sigbuf.appendByte(';');
|
||||
break;
|
||||
case FORALL:
|
||||
ForAll ft = (ForAll)type;
|
||||
assembleParamsSig(ft.tvars);
|
||||
assembleSig(ft.qtype);
|
||||
break;
|
||||
case UNINITIALIZED_THIS:
|
||||
case UNINITIALIZED_OBJECT:
|
||||
// we don't yet have a spec for uninitialized types in the
|
||||
// local variable table
|
||||
assembleSig(types.erasure(((UninitializedType)type).qtype));
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("typeSig " + type.getTag());
|
||||
}
|
||||
}
|
||||
private class CWSignatureGenerator extends Types.SignatureGenerator {
|
||||
|
||||
boolean hasTypeVar(List<Type> l) {
|
||||
while (l.nonEmpty()) {
|
||||
if (l.head.hasTag(TYPEVAR)) return true;
|
||||
l = l.tail;
|
||||
/**
|
||||
* An output buffer for type signatures.
|
||||
*/
|
||||
ByteBuffer sigbuf = new ByteBuffer();
|
||||
|
||||
CWSignatureGenerator(Types types) {
|
||||
super(types);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void assembleClassSig(Type type) {
|
||||
type = type.unannotatedType();
|
||||
ClassType ct = (ClassType)type;
|
||||
ClassSymbol c = (ClassSymbol)ct.tsym;
|
||||
enterInner(c);
|
||||
Type outer = ct.getEnclosingType();
|
||||
if (outer.allparams().nonEmpty()) {
|
||||
boolean rawOuter =
|
||||
c.owner.kind == MTH || // either a local class
|
||||
c.name == names.empty; // or anonymous
|
||||
assembleClassSig(rawOuter
|
||||
? types.erasure(outer)
|
||||
: outer);
|
||||
sigbuf.appendByte('.');
|
||||
Assert.check(c.flatname.startsWith(c.owner.enclClass().flatname));
|
||||
sigbuf.appendName(rawOuter
|
||||
? c.flatname.subName(c.owner.enclClass().flatname.getByteLength()+1,c.flatname.getByteLength())
|
||||
: c.name);
|
||||
} else {
|
||||
sigbuf.appendBytes(externalize(c.flatname));
|
||||
}
|
||||
if (ct.getTypeArguments().nonEmpty()) {
|
||||
sigbuf.appendByte('<');
|
||||
assembleSig(ct.getTypeArguments());
|
||||
sigbuf.appendByte('>');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void assembleSig(List<Type> types) {
|
||||
for (List<Type> ts = types; ts.nonEmpty(); ts = ts.tail)
|
||||
assembleSig(ts.head);
|
||||
}
|
||||
|
||||
void assembleParamsSig(List<Type> typarams) {
|
||||
sigbuf.appendByte('<');
|
||||
for (List<Type> ts = typarams; ts.nonEmpty(); ts = ts.tail) {
|
||||
TypeVar tvar = (TypeVar)ts.head;
|
||||
sigbuf.appendName(tvar.tsym.name);
|
||||
List<Type> bounds = types.getBounds(tvar);
|
||||
if ((bounds.head.tsym.flags() & INTERFACE) != 0) {
|
||||
sigbuf.appendByte(':');
|
||||
}
|
||||
for (List<Type> l = bounds; l.nonEmpty(); l = l.tail) {
|
||||
sigbuf.appendByte(':');
|
||||
assembleSig(l.head);
|
||||
/**
|
||||
* Assemble signature of given type in string buffer.
|
||||
* Check for uninitialized types before calling the general case.
|
||||
*/
|
||||
@Override
|
||||
public void assembleSig(Type type) {
|
||||
type = type.unannotatedType();
|
||||
switch (type.getTag()) {
|
||||
case UNINITIALIZED_THIS:
|
||||
case UNINITIALIZED_OBJECT:
|
||||
// we don't yet have a spec for uninitialized types in the
|
||||
// local variable table
|
||||
assembleSig(types.erasure(((UninitializedType)type).qtype));
|
||||
break;
|
||||
default:
|
||||
super.assembleSig(type);
|
||||
}
|
||||
}
|
||||
sigbuf.appendByte('>');
|
||||
|
||||
@Override
|
||||
protected void append(char ch) {
|
||||
sigbuf.appendByte(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void append(byte[] ba) {
|
||||
sigbuf.appendBytes(ba);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void append(Name name) {
|
||||
sigbuf.appendName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void classReference(ClassSymbol c) {
|
||||
enterInner(c);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
sigbuf.reset();
|
||||
}
|
||||
|
||||
private Name toName() {
|
||||
return sigbuf.toName(names);
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return sigbuf.length == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return signature of given type
|
||||
/**
|
||||
* Return signature of given type
|
||||
*/
|
||||
Name typeSig(Type type) {
|
||||
Assert.check(sigbuf.length == 0);
|
||||
Assert.check(signatureGen.isEmpty());
|
||||
//- System.out.println(" ? " + type);
|
||||
assembleSig(type);
|
||||
Name n = sigbuf.toName(names);
|
||||
sigbuf.reset();
|
||||
signatureGen.assembleSig(type);
|
||||
Name n = signatureGen.toName();
|
||||
signatureGen.reset();
|
||||
//- System.out.println(" " + n);
|
||||
return n;
|
||||
}
|
||||
@ -711,7 +623,7 @@ public class ClassWriter extends ClassFile {
|
||||
(flags & (SYNTHETIC|BRIDGE)) != SYNTHETIC &&
|
||||
(flags & ANONCONSTR) == 0 &&
|
||||
(!types.isSameType(sym.type, sym.erasure(types)) ||
|
||||
hasTypeVar(sym.type.getThrownTypes()))) {
|
||||
signatureGen.hasTypeVar(sym.type.getThrownTypes()))) {
|
||||
// note that a local class with captured variables
|
||||
// will get a signature attribute
|
||||
int alenIdx = writeAttr(names.Signature);
|
||||
@ -1730,7 +1642,7 @@ public class ClassWriter extends ClassFile {
|
||||
Assert.check((c.flags() & COMPOUND) == 0);
|
||||
databuf.reset();
|
||||
poolbuf.reset();
|
||||
sigbuf.reset();
|
||||
signatureGen.reset();
|
||||
pool = c.pool;
|
||||
innerClasses = null;
|
||||
innerClassesQueue = null;
|
||||
@ -1791,12 +1703,12 @@ public class ClassWriter extends ClassFile {
|
||||
if (sigReq) {
|
||||
Assert.check(source.allowGenerics());
|
||||
int alenIdx = writeAttr(names.Signature);
|
||||
if (typarams.length() != 0) assembleParamsSig(typarams);
|
||||
assembleSig(supertype);
|
||||
if (typarams.length() != 0) signatureGen.assembleParamsSig(typarams);
|
||||
signatureGen.assembleSig(supertype);
|
||||
for (List<Type> l = interfaces; l.nonEmpty(); l = l.tail)
|
||||
assembleSig(l.head);
|
||||
databuf.appendChar(pool.put(sigbuf.toName(names)));
|
||||
sigbuf.reset();
|
||||
signatureGen.assembleSig(l.head);
|
||||
databuf.appendChar(pool.put(signatureGen.toName()));
|
||||
signatureGen.reset();
|
||||
endAttr(alenIdx);
|
||||
acount++;
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public class Names {
|
||||
public final Name clone;
|
||||
public final Name close;
|
||||
public final Name compareTo;
|
||||
public final Name deserializeLambda;
|
||||
public final Name desiredAssertionStatus;
|
||||
public final Name equals;
|
||||
public final Name error;
|
||||
@ -174,6 +175,7 @@ public class Names {
|
||||
//lambda-related
|
||||
public final Name lambda;
|
||||
public final Name metaFactory;
|
||||
public final Name altMetaFactory;
|
||||
|
||||
public final Name.Table table;
|
||||
|
||||
@ -207,6 +209,7 @@ public class Names {
|
||||
clone = fromString("clone");
|
||||
close = fromString("close");
|
||||
compareTo = fromString("compareTo");
|
||||
deserializeLambda = fromString("$deserializeLambda$");
|
||||
desiredAssertionStatus = fromString("desiredAssertionStatus");
|
||||
equals = fromString("equals");
|
||||
error = fromString("<error>");
|
||||
@ -306,8 +309,9 @@ public class Names {
|
||||
package_info = fromString("package-info");
|
||||
|
||||
//lambda-related
|
||||
lambda = fromString("lambda");
|
||||
lambda = fromString("lambda$");
|
||||
metaFactory = fromString("metaFactory");
|
||||
altMetaFactory = fromString("altMetaFactory");
|
||||
}
|
||||
|
||||
protected Name.Table createTable(Options options) {
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 8005653
|
||||
* @summary A lambda containing an inner class referencing an external type var in class parameter type
|
||||
* @author Robert Field
|
||||
* @run main LambdaInnerTypeVarArgs
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LambdaInnerTypeVarArgs {
|
||||
|
||||
static int assertionCount = 0;
|
||||
|
||||
static void assertTrue(boolean cond) {
|
||||
assertionCount++;
|
||||
if (!cond)
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
interface I {
|
||||
C doit();
|
||||
}
|
||||
|
||||
abstract class C {
|
||||
abstract Object it();
|
||||
}
|
||||
|
||||
class TV {
|
||||
C go() {
|
||||
List<String> ls = new ArrayList<>();
|
||||
ls.add("Oh");
|
||||
ls.add("my");
|
||||
return foo(ls).doit();
|
||||
}
|
||||
|
||||
<RRRRR> I foo(List<RRRRR> r) {
|
||||
return () -> new C() {
|
||||
List<RRRRR> xxxxx = r;
|
||||
@Override
|
||||
Object it() { return xxxxx; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void test1() {
|
||||
assertTrue(((List<String>)(new TV().go().it())).get(0).equals("Oh"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LambdaInnerTypeVarArgs t = new LambdaInnerTypeVarArgs();
|
||||
t.test1();
|
||||
assertTrue(assertionCount == 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 8005653
|
||||
* @summary A lambda containing an inner class referencing an external type var
|
||||
* @author Robert Field
|
||||
* @run main LambdaInnerTypeVarReflect
|
||||
*/
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LambdaInnerTypeVarReflect {
|
||||
|
||||
static int assertionCount = 0;
|
||||
|
||||
static void assertTrue(boolean cond) {
|
||||
assertionCount++;
|
||||
if (!cond)
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
interface I {
|
||||
C doit();
|
||||
}
|
||||
|
||||
abstract class C {
|
||||
abstract Object it();
|
||||
}
|
||||
|
||||
class TV {
|
||||
C go() {
|
||||
return foo("Frump").doit();
|
||||
}
|
||||
|
||||
<RRRRR> I foo(RRRRR r) {
|
||||
return () -> new C() {
|
||||
public RRRRR xxxxx = r;
|
||||
@Override
|
||||
Object it() { return xxxxx; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void test1() throws IOException {
|
||||
char[] buffer = new char[1024];
|
||||
String innerName = new TV().go().getClass().getName();
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
int exitCode = com.sun.tools.javap.Main.run(new String[] {innerName}, pw);
|
||||
assertTrue(exitCode == 0);
|
||||
|
||||
String javapOut = sw.toString();
|
||||
assertTrue(javapOut.contains(innerName));
|
||||
assertTrue(!javapOut.contains("RRRRR"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
LambdaInnerTypeVarReflect t = new LambdaInnerTypeVarReflect();
|
||||
t.test1();
|
||||
assertTrue(assertionCount == 3);
|
||||
}
|
||||
}
|
43
langtools/test/tools/javac/lambda/MethodReference61.java
Normal file
43
langtools/test/tools/javac/lambda/MethodReference61.java
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 8006763
|
||||
* @summary super in method reference used in anonymous class
|
||||
*/
|
||||
public class MethodReference61 {
|
||||
interface SAM {
|
||||
void m();
|
||||
}
|
||||
|
||||
static class MyTester {
|
||||
public void ifoo() { }
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
MyTester t = new MyTester() {
|
||||
SAM s = super::ifoo;
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user