Initial push for the 'defender methods' feature. The compiler recognizes and attributes the syntax described in the strawman proposal; defender methods are decorated with a special bytecode attribute called 'Defender'. It is also possible (using the -XDemitDefenderMethodAnnos flag) to have the compiler automatically generate a non-standard annotation called @DefenderMethod which contains information about the default implementation attached to the defender method declaration.
authormcimadamore
Fri Jun 18 13:02:01 2010 +0100 (2 years ago)
changeset 5809f79be8946c6
parent 5790bed895ea4d1
child 58102b8de982628
Initial push for the 'defender methods' feature. The compiler recognizes and attributes the syntax described in the strawman proposal; defender methods are decorated with a special bytecode attribute called 'Defender'. It is also possible (using the -XDemitDefenderMethodAnnos flag) to have the compiler automatically generate a non-standard annotation called @DefenderMethod which contains information about the default implementation attached to the defender method declaration.

Note: the current prototype doesn't commit to any of the implementation strategies described in the strawman; as such the code the compiler emits will not be executable (calling a defender method will result in an AbstractMethodError).
src/share/classes/com/sun/runtime/DefenderMethod.java
src/share/classes/com/sun/tools/classfile/Attribute.java
src/share/classes/com/sun/tools/classfile/ClassWriter.java
src/share/classes/com/sun/tools/classfile/Defender_attribute.java
src/share/classes/com/sun/tools/javac/code/Flags.java
src/share/classes/com/sun/tools/javac/code/Source.java
src/share/classes/com/sun/tools/javac/code/Symbol.java
src/share/classes/com/sun/tools/javac/code/Symtab.java
src/share/classes/com/sun/tools/javac/comp/Attr.java
src/share/classes/com/sun/tools/javac/comp/Check.java
src/share/classes/com/sun/tools/javac/comp/Flow.java
src/share/classes/com/sun/tools/javac/comp/Lower.java
src/share/classes/com/sun/tools/javac/comp/MemberEnter.java
src/share/classes/com/sun/tools/javac/comp/Resolve.java
src/share/classes/com/sun/tools/javac/jvm/ClassReader.java
src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
src/share/classes/com/sun/tools/javac/jvm/Gen.java
src/share/classes/com/sun/tools/javac/jvm/Target.java
src/share/classes/com/sun/tools/javac/parser/JavacParser.java
src/share/classes/com/sun/tools/javac/resources/compiler.properties
src/share/classes/com/sun/tools/javac/tree/JCTree.java
src/share/classes/com/sun/tools/javac/util/Names.java
src/share/classes/com/sun/tools/javap/AttributeWriter.java
test/tools/javac/defender/Neg01.java
test/tools/javac/defender/Neg01.out
test/tools/javac/defender/Pos01.java
test/tools/javac/defender/Pos02.java
--- a/src/share/classes/com/sun/tools/classfile/Attribute.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/classfile/Attribute.java Fri Jun 18 13:02:01 2010 +0100
@@ -43,6 +43,7 @@ public abstract class Attribute {
public static final String Code = "Code";
public static final String ConstantValue = "ConstantValue";
public static final String CompilationID = "CompilationID";
+ public static final String Defender = "Defender";
public static final String Deprecated = "Deprecated";
public static final String EnclosingMethod = "EnclosingMethod";
public static final String Exceptions = "Exceptions";
@@ -165,6 +166,7 @@ public abstract class Attribute {
R visitCode(Code_attribute attr, P p);
R visitCompilationID(CompilationID_attribute attr, P p);
R visitConstantValue(ConstantValue_attribute attr, P p);
+ R visitDefender(Defender_attribute attr, P p);
R visitDeprecated(Deprecated_attribute attr, P p);
R visitEnclosingMethod(EnclosingMethod_attribute attr, P p);
R visitExceptions(Exceptions_attribute attr, P p);
--- a/src/share/classes/com/sun/tools/classfile/ClassWriter.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/classfile/ClassWriter.java Fri Jun 18 13:02:01 2010 +0100
@@ -376,6 +376,12 @@ public class ClassWriter {
return null;
}
+ public Void visitDefender(Defender_attribute attr, ClassOutputStream out) {
+ out.writeShort(attr.class_index);
+ out.writeShort(attr.method_index);
+ return null;
+ }
+
public Void visitDeprecated(Deprecated_attribute attr, ClassOutputStream out) {
return null;
}
--- a/src/share/classes/com/sun/tools/javac/code/Flags.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Flags.java Fri Jun 18 13:02:01 2010 +0100
@@ -123,6 +123,7 @@ public class Flags {
public static final int ACC_SUPER = 0x0020;
public static final int ACC_BRIDGE = 0x0040;
public static final int ACC_VARARGS = 0x0080;
+ public static final int ACC_DEFENDER = 0x0200;
/*****************************************
* Internal compiler flags (no bits in the lower 16).
@@ -245,6 +246,11 @@ public class Flags {
* Flag that marks a lambda expression symbol that requires access to enclosing class
*/
public static final long OUTER_ACCESS = 1L<<41;
+
+ /**
+ * Flag that marks a defender method/interface
+ */
+ public static final long DEFENDER = 1L<<42;
/** Modifier masks.
*/
@@ -259,7 +265,7 @@ public class Flags {
ConstructorFlags = AccessFlags,
InterfaceMethodFlags = ABSTRACT | PUBLIC,
MethodFlags = AccessFlags | ABSTRACT | STATIC | NATIVE |
- SYNCHRONIZED | FINAL | STRICTFP;
+ SYNCHRONIZED | FINAL | STRICTFP | ACC_DEFENDER;
public static final long
LocalVarFlags = FINAL | PARAMETER;
@@ -333,7 +339,8 @@ public class Flags {
PARAMETER("parameter"),
VARARGS("varargs"),
PACKAGE("package"),
- THROW("throw");
+ THROW("throw"),
+ DEFENDER("defender");
String name;
--- a/src/share/classes/com/sun/tools/javac/code/Source.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Source.java Fri Jun 18 13:02:01 2010 +0100
@@ -126,6 +126,9 @@ public enum Source {
return compareTo(JDK1_7) >= 0;
}
public boolean allowMulticatch() {
+ return compareTo(JDK1_7) >= 0;
+ }
+ public boolean allowDefenderMethods() {
return compareTo(JDK1_7) >= 0;
}
public boolean allowEnums() {
--- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Jun 18 13:02:01 2010 +0100
@@ -1033,6 +1033,11 @@ public abstract class Symbol implements
/** The names of the parameters */
public List<Name> savedParameterNames;
+
+ /** Symbol corresponding to the method's default implementation
+ * (used for defender methods);
+ */
+ public Symbol defaultImpl = null;
/** For an attribute field accessor, its default value if any.
* The value is null if none appeared in the method
--- a/src/share/classes/com/sun/tools/javac/code/Symtab.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java Fri Jun 18 13:02:01 2010 +0100
@@ -154,6 +154,7 @@ public class Symtab {
public final Type proprietaryType;
public final Type systemType;
public final Type proxyHelper;
+ public final Type defenderMethodType;
/** The symbol representing the length field of an array.
*/
@@ -474,6 +475,7 @@ public class Symtab {
inheritedType = enterClass("java.lang.annotation.Inherited");
systemType = enterClass("java.lang.System");
proxyHelper = enterClass("com.sun.runtime.ProxyHelper");
+ defenderMethodType = enterClass("com.sun.runtime.DefenderMethod");
synthesizeEmptyInterfaceIfMissing(cloneableType);
synthesizeEmptyInterfaceIfMissing(serializableType);
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Fri Jun 18 13:02:01 2010 +0100
@@ -279,6 +279,19 @@ public class Attr extends JCTree.Visitor
return rs.resolveSelf(pos, env, env.enclClass.sym, names._this);
}
+ /** The `this' symbol corresponding to an implemented defender interface.
+ * @param env The current environment.
+ */
+ Symbol defenderThisSym(DiagnosticPosition pos, Env<AttrContext> env, Type type) {
+ if (types.asSuper(env.enclClass.type, type.tsym) == null ||
+ (type.tsym.flags() & DEFENDER) == 0) {
+ throw new AssertionError();
+ }
+ Symbol thisSym = thisSym(pos, env).clone(env.info.scope.owner);
+ thisSym.type = type;
+ return thisSym;
+ }
+
/** Attribute a parsed identifier.
* @param tree Parsed identifier name
* @param topLevel The toplevel to use
@@ -632,6 +645,7 @@ public class Attr extends JCTree.Visitor
public void visitMethodDef(JCMethodDecl tree) {
MethodSymbol m = tree.sym;
+ boolean isDefender = (m.flags() & DEFENDER) != 0;
Lint lint = env.info.lint.augment(m.attributes_field, m.flags());
Lint prevLint = chk.setLint(lint);
@@ -649,6 +663,13 @@ public class Attr extends JCTree.Visitor
Env<AttrContext> localEnv = memberEnter.methodEnv(tree, env);
localEnv.info.lint = lint;
+
+ if (isDefender) {
+ //when attributing defender method body we need a synthetic 'this' variable
+ //whose type is the type of the enclosing interface
+ VarSymbol _this = new VarSymbol(0, names._this, m.owner.type, m.owner);
+ localEnv.info.scope.enter(_this);
+ }
// Enter all type parameters into the local method scope.
for (List<JCTypeParameter> l = tree.typarams; l.nonEmpty(); l = l.tail)
@@ -701,7 +722,7 @@ public class Attr extends JCTree.Visitor
log.error(tree.pos(),
"default.allowed.in.intf.annotation.member");
}
- } else if ((owner.flags() & INTERFACE) != 0) {
+ } else if ((owner.flags() & INTERFACE) != 0 && !isDefender) {
log.error(tree.body.pos(), "intf.meth.cant.have.body");
} else if ((tree.mods.flags & ABSTRACT) != 0) {
log.error(tree.pos(), "abstract.meth.cant.have.body");
@@ -735,6 +756,18 @@ public class Attr extends JCTree.Visitor
// Attribute method body.
attribStat(tree.body, localEnv);
+
+ if (isDefender) {
+ //check that default expression is of right type
+ if (tree.body.stats.head.getTag() != JCTree.EXEC ||
+ ((JCExpressionStatement)tree.body.stats.head).expr.getTag() != JCTree.APPLY) {
+ log.error(tree.body.stats.head, "bad.defender.method.body");
+ } else {
+ JCTree defaultImpl = ((JCExpressionStatement)tree.body.stats.head).expr;
+ chk.checkType(defaultImpl, defaultImpl.type, m.type.getReturnType());
+ m.defaultImpl = TreeInfo.symbol(((JCMethodInvocation)defaultImpl).meth);
+ }
+ }
}
localEnv.info.scope.leave();
result = tree.type = m.type;
@@ -2342,7 +2375,10 @@ public class Attr extends JCTree.Visitor
return rs.resolveQualifiedMethod(
pos, env, site, name, pt.getParameterTypes(), pt.getTypeArguments());
} else if (name == names._this || name == names._super) {
- return rs.resolveSelf(pos, env, site.tsym, name);
+ return ((site.tsym.flags() & DEFENDER) != 0 &&
+ types.asSuper(env.enclClass.type, site.tsym) != null) ?
+ defenderThisSym(pos, env, site) :
+ rs.resolveSelf(pos, env, site.tsym, name);
} else if (name == names._class) {
// In this case, we have already made sure in
// visitSelect that qualifier expression is a type.
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Fri Jun 18 13:02:01 2010 +0100
@@ -1753,7 +1753,7 @@ public class Check {
undef == null && e != null;
e = e.sibling) {
if (e.sym.kind == MTH &&
- (e.sym.flags() & (ABSTRACT|IPROXY)) == ABSTRACT) {
+ (e.sym.flags() & (ABSTRACT|IPROXY|DEFENDER)) == ABSTRACT) {
MethodSymbol absmeth = (MethodSymbol)e.sym;
MethodSymbol implmeth = absmeth.implementation(impl, types, true);
if (implmeth == null || implmeth == absmeth)
--- a/src/share/classes/com/sun/tools/javac/comp/Flow.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Flow.java Fri Jun 18 13:02:01 2010 +0100
@@ -693,7 +693,8 @@ public class Flow extends TreeScanner {
}
public void visitMethodDef(JCMethodDecl tree) {
- if (tree.body == null) return;
+ if (tree.body == null ||
+ (tree.sym.flags() & DEFENDER) != 0) return;
List<Type> caughtPrev = caught;
List<Type> mthrown = tree.sym.type.getThrownTypes();
--- a/src/share/classes/com/sun/tools/javac/comp/Lower.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Jun 18 13:02:01 2010 +0100
@@ -39,6 +39,7 @@ import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.main.OptionName;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*;
@@ -82,6 +83,7 @@ public class Lower extends TreeTranslato
private final Name classDollar;
private Types types;
private boolean debugLower;
+ private boolean emitDefenderMethodAnnos;
protected Lower(Context context) {
context.put(lowerKey, this);
@@ -106,6 +108,7 @@ public class Lower extends TreeTranslato
types = Types.instance(context);
Options options = Options.instance(context);
debugLower = options.get("debuglower") != null;
+ emitDefenderMethodAnnos = options.get("emitDefenderMethodAnnos") != null;
}
/** The currently enclosing class.
@@ -1396,6 +1399,9 @@ public class Lower extends TreeTranslato
if (currentClass == c && lambdaStack.isEmpty()) {
// in this case, `this' works fine
return make.at(pos).This(c.erasure(types));
+ } else if ((c.flags() & DEFENDER) != 0 &&
+ types.asSuper(currentClass.type, c) != null) {
+ return make.at(pos).This(c.erasure(types));
} else {
// need to go via this$n
return makeOuterThis(pos, c);
@@ -2359,6 +2365,26 @@ public class Lower extends TreeTranslato
ordinalAssign.type = id1.type;
tree.body.stats = tree.body.stats.prepend(ordinalAssign);
}
+ }
+
+ if ((tree.sym.flags() & DEFENDER) != 0 &&
+ emitDefenderMethodAnnos) {
+ //add synthetic @DefenderMethod annotation
+ ListBuffer<Pair<MethodSymbol, Attribute>> values = ListBuffer.lb();
+
+ MethodSymbol _className = lookupMethod(tree.pos(), names._className, syms.defenderMethodType, List.<Type>nil());
+ String _classNameVal = tree.sym.defaultImpl.enclClass().flatName().toString();
+ values.append(new Pair<MethodSymbol, Attribute>(_className, new Attribute.Constant(syms.stringType, _classNameVal)));
+
+ MethodSymbol _methodName = lookupMethod(tree.pos(), names._methodName, syms.defenderMethodType, List.<Type>nil());
+ String _methodNameVal = tree.sym.defaultImpl.name.toString();
+ values.append(new Pair<MethodSymbol, Attribute>(_methodName, new Attribute.Constant(syms.stringType, _methodNameVal)));
+
+ MethodSymbol _methodSignature = lookupMethod(tree.pos(), names._methodSignature, syms.defenderMethodType, List.<Type>nil());
+ String _methodSignatureVal = writer.typeSig(types.erasure(tree.sym.defaultImpl.type)).toString();
+ values.append(new Pair<MethodSymbol, Attribute>(_methodSignature, new Attribute.Constant(syms.stringType, _methodSignatureVal)));
+
+ tree.sym.attributes_field = tree.sym.attributes_field.prepend(new Attribute.Compound(syms.defenderMethodType, values.toList()));
}
JCMethodDecl prevMethodDef = currentMethodDef;
@@ -3633,10 +3659,15 @@ public class Lower extends TreeTranslato
public void visitSelect(JCFieldAccess tree) {
// need to special case-access of the form C.super.x
- // these will always need an access method.
+ // these will always need an access method, unless C
+ // is a defender interface subclassed by the current class.
+ boolean isDefenderAccess =
+ (tree.selected.type.tsym.flags() & DEFENDER) != 0 &&
+ types.asSuper(currentClass.type, tree.selected.type.tsym) != null;
boolean qualifiedSuperAccess =
tree.selected.getTag() == JCTree.SELECT &&
- TreeInfo.name(tree.selected) == names._super;
+ TreeInfo.name(tree.selected) == names._super &&
+ !isDefenderAccess;
tree.selected = translate(tree.selected);
if (tree.name == names._class)
result = classOf(tree.selected);
--- a/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Fri Jun 18 13:02:01 2010 +0100
@@ -569,6 +569,12 @@ public class MemberEnter extends JCTree.
MethodSymbol m = new MethodSymbol(0, tree.name, null, enclScope.owner);
m.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, m, tree);
tree.sym = m;
+
+ //if this is a defender method, add the DEFENDER flag to the enclosing interface
+ if ((tree.mods.flags & DEFENDER) != 0) {
+ m.owner.flags_field |= DEFENDER;
+ }
+
Env<AttrContext> localEnv = methodEnv(tree, env);
// Compute the method type
--- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Fri Jun 18 13:02:01 2010 +0100
@@ -664,8 +664,10 @@ public class Resolve {
(m1.owner.flags_field & INTERFACE) != 0) &&
m2.overrides(m1, m2Owner, types, false))
return m2;
- boolean m1Abstract = (m1.flags() & ABSTRACT) != 0;
- boolean m2Abstract = (m2.flags() & ABSTRACT) != 0;
+ boolean m1Abstract = (m1.flags() & ABSTRACT) != 0 &&
+ (m1.flags() & DEFENDER) == 0;
+ boolean m2Abstract = (m2.flags() & ABSTRACT) != 0 &&
+ (m2.flags() & DEFENDER) == 0;
if (m1Abstract && !m2Abstract) return m2;
if (m2Abstract && !m1Abstract) return m1;
// both abstract or both concrete
@@ -792,23 +794,26 @@ public class Resolve {
if (name == names.init)
break;
//- System.out.println(" - " + bestSoFar);
- if (abstractok) {
- Symbol concrete = methodNotFound;
- if ((bestSoFar.flags() & ABSTRACT) == 0)
- concrete = bestSoFar;
- for (List<Type> l = types.interfaces(c.type);
- l.nonEmpty();
- l = l.tail) {
- bestSoFar = findMethod(env, site, name, argtypes,
- typeargtypes,
- l.head, abstractok, bestSoFar,
- allowBoxing, useVarargs, operator);
- }
- if (concrete != bestSoFar &&
- concrete.kind < ERR && bestSoFar.kind < ERR &&
- types.isSubSignature(concrete.type, bestSoFar.type))
- bestSoFar = concrete;
- }
+ Symbol concrete = methodNotFound;
+ if ((bestSoFar.flags() & ABSTRACT) == 0)
+ concrete = bestSoFar;
+ for (List<Type> l = types.interfaces(c.type);
+ l.nonEmpty();
+ l = l.tail) {
+ boolean abstractok2 = abstractok || (l.head.tsym.flags() & DEFENDER) != 0;
+ Symbol s2 = findMethod(env, site, name, argtypes,
+ typeargtypes,
+ l.head, abstractok2, bestSoFar,
+ allowBoxing, useVarargs, operator);
+ bestSoFar = s2.kind <= AMBIGUOUS && abstractok2 ?
+ s2 :
+ bestSoFar;
+ }
+ if (concrete != bestSoFar &&
+ concrete.kind < ERR && bestSoFar.kind < ERR &&
+ types.isSubSignature(concrete.type, bestSoFar.type))
+ bestSoFar = concrete;
+
}
return bestSoFar;
}
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Jun 18 13:02:01 2010 +0100
@@ -1850,6 +1850,9 @@ public class ClassReader implements Comp
*/
MethodSymbol readMethod() {
long flags = adjustMethodFlags(nextChar());
+ if ((flags & DEFENDER) != 0) {
+ currentOwner.flags_field |= DEFENDER;
+ }
Name name = readName(nextChar());
Type type = readType(nextChar());
if (name == names.init && currentOwner.hasOuterInstance()) {
@@ -2139,9 +2142,13 @@ public class ClassReader implements Comp
flags &= ~ACC_VARARGS;
flags |= VARARGS;
}
+ if ((flags & ACC_DEFENDER) != 0) {
+ flags &= ~ACC_DEFENDER;
+ flags |= DEFENDER;
+ }
return flags;
}
- long adjustClassFlags(long flags) {
+ long adjustClassFlags(long flags) {
return flags & ~ACC_SUPER; // SUPER and SYNCHRONIZED bits overloaded
}
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Fri Jun 18 13:02:01 2010 +0100
@@ -626,6 +626,23 @@ public class ClassWriter extends ClassFi
return 1;
}
+ /** Write the EnclosingMethod attribute if needed.
+ * Returns the number of attributes written (0 or 1).
+ */
+ int writeDefenderAttribute(MethodSymbol m) {
+ if (!target.hasDefenderAttribute() ||
+ (m.flags() & DEFENDER) == 0 ||
+ m.defaultImpl == null)
+ return 0;
+
+ int alenIdx = writeAttr(names.Defender);
+ ClassSymbol enclClass = m.enclClass();
+ databuf.appendChar(pool.put(enclClass));
+ databuf.appendChar(pool.put(nameType(m.defaultImpl)));
+ endAttr(alenIdx);
+ return 1;
+ }
+
/** Write flag attributes; return number of attributes written.
*/
int writeFlagAttrs(long flags) {
@@ -1132,6 +1149,7 @@ public class ClassWriter extends ClassFi
}
acount += writeMemberAttrs(m);
acount += writeParameterAttrs(m);
+ acount += writeDefenderAttribute(m);
endAttrs(acountIdx, acount);
}
@@ -1636,7 +1654,7 @@ public class ClassWriter extends ClassFi
List<Type> interfaces = types.interfaces(c.type);
List<Type> typarams = c.type.getTypeArguments();
- int flags = adjustFlags(c.flags());
+ int flags = adjustFlags(c.flags() & ~DEFENDER);
if ((flags & PROTECTED) != 0) flags |= PUBLIC;
flags = flags & ClassFlags & ~STRICTFP;
if ((flags & INTERFACE) == 0) flags |= ACC_SUPER;
@@ -1750,6 +1768,8 @@ public class ClassWriter extends ClassFi
result |= ACC_BRIDGE;
if ((flags & VARARGS) != 0 && target.useVarargsFlag())
result |= ACC_VARARGS;
+ if ((flags & DEFENDER) != 0 && target.useDefenderFlag())
+ result |= ACC_DEFENDER;
return result;
}
--- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java Fri Jun 18 13:02:01 2010 +0100
@@ -903,7 +903,8 @@ public class Gen extends JCTree.Visitor
nerrs++;
}
- else if (tree.body != null) {
+ else if (tree.body != null &&
+ (tree.sym.flags() & DEFENDER) == 0) {
// Create a new code structure and initialize it.
int startpcCrt = initCode(tree, env, fatcode);
--- a/src/share/classes/com/sun/tools/javac/jvm/Target.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/jvm/Target.java Fri Jun 18 13:02:01 2010 +0100
@@ -239,6 +239,9 @@ public enum Target {
public boolean useBridgeFlag() {
return compareTo(JDK1_5) >= 0;
}
+ public boolean useDefenderFlag() {
+ return compareTo(JDK1_7) >= 0;
+ }
/** Return the character to be used in constructing synthetic
* identifiers, where not specified by the JLS.
@@ -306,4 +309,11 @@ public enum Target {
public boolean hasEnclosingMethodAttribute() {
return compareTo(JDK1_5) >= 0 || this == JSR14;
}
+
+ /** In JDK 7 we introduced the 'Defender' attribute for supporting
+ * the defender methods language feature
+ */
+ public boolean hasDefenderAttribute() {
+ return compareTo(JDK1_7) >= 0;
+ }
}
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Fri Jun 18 13:02:01 2010 +0100
@@ -134,6 +134,7 @@ public class JavacParser implements Pars
this.allowAnnotations = source.allowAnnotations();
this.allowDiamond = source.allowDiamond();
this.allowMulticatch = source.allowMulticatch();
+ this.allowDefenderMethods = source.allowDefenderMethods();
this.allowTypeAnnotations = source.allowTypeAnnotations();
this.allowThrowTypeParameters = source.allowThrowTypeParameters();
this.allowLambda = source.allowLambda();
@@ -162,6 +163,10 @@ public class JavacParser implements Pars
/** Switch: Should multicatch clause be accepted?
*/
boolean allowMulticatch;
+
+ /** Switch: Should defender methods declaration be accepted?
+ */
+ boolean allowDefenderMethods;
/** Switch: Should varargs be recognized?
*/
@@ -2851,13 +2856,14 @@ public class JavacParser implements Pars
* InterfaceBodyDeclaration =
* ";"
* | ModifiersOpt Type Ident
- * ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" )
+ * ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest | DefenderMethodDeclaratorRest ";" )
*/
List<JCTree> classOrInterfaceBodyDeclaration(Name className, boolean isInterface) {
if (S.token() == SEMI) {
S.nextToken();
return List.<JCTree>of(F.at(Position.NOPOS).Block(0, List.<JCStatement>nil()));
} else {
+ boolean isExtension = false;
String dc = S.docComment();
int pos = S.pos();
JCModifiers mods = modifiersOpt();
@@ -2875,6 +2881,12 @@ public class JavacParser implements Pars
List<JCAnnotation> annosAfterParams = annotationsOpt(AnnotationKind.DEFAULT_ANNO);
Name name = S.name();
+ if (isInterface && name.equals(names._extension)) {
+ checkDefenderMethods();
+ accept(IDENTIFIER);
+ mods.flags |= Flags.DEFENDER;
+ isExtension = true;
+ }
pos = S.pos();
JCExpression type;
boolean isVoid = S.token() == VOID;
@@ -2897,14 +2909,14 @@ public class JavacParser implements Pars
log.error(pos, "invalid.meth.decl.ret.type.req");
return List.of(methodDeclaratorRest(
pos, mods, null, names.init, typarams,
- isInterface, true, dc));
+ isInterface, true, isExtension, dc));
} else {
pos = S.pos();
name = ident();
if (S.token() == LPAREN) {
return List.of(methodDeclaratorRest(
pos, mods, type, name, typarams,
- isInterface, isVoid, dc));
+ isInterface, isVoid, isExtension, dc));
} else if (!isVoid && typarams.isEmpty()) {
List<JCTree> defs =
variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
@@ -2935,6 +2947,8 @@ public class JavacParser implements Pars
* FormalParameters [Annotations] [THROWS TypeList] ";"
* ConstructorDeclaratorRest =
* "(" FormalParameterListOpt ")" [Annotations] [THROWS TypeList] MethodBody
+ * DefenderMethodDeclaratorRest =
+ * "(" FormalParameterListOpt ")" [Annotations] [THROWS TypeList] DEFAULT IDENT { '.' [TYPEARGS] IDENT }
*/
JCTree methodDeclaratorRest(int pos,
JCModifiers mods,
@@ -2942,6 +2956,7 @@ public class JavacParser implements Pars
Name name,
List<JCTypeParameter> typarams,
boolean isInterface, boolean isVoid,
+ boolean isDefender,
String dc) {
List<JCVariableDecl> params = formalParameters();
@@ -2967,7 +2982,36 @@ public class JavacParser implements Pars
}
JCBlock body = null;
JCExpression defaultValue;
- if (S.token() == LBRACE) {
+ if (isDefender) {
+ //a defender method declaration is turned into an ordinary method
+ //declaration where the method body is a standard method call to
+ //the extended method.
+ //
+ // e.g.
+ // extension T reduce(T arg) default Collections.<T>reduce;
+ //
+ // --> T reduce(T arg) { Collections.<T>reduce(this, arg); }
+ int prevMode = mode;
+ mode = EXPR;
+ accept(DEFAULT);
+ List<JCExpression> defenderMethodParams = List.<JCExpression>of(toP(F.at(pos).Ident(names._this)));
+ for (JCVariableDecl param : params) {
+ defenderMethodParams =
+ defenderMethodParams.append(toP(F.at(pos).Ident(param.name)));
+ }
+ List<JCExpression> args = typeArgumentsOpt();
+ JCExpression meth = toP(F.at(pos).Ident(ident()));
+ while (args == null && S.token() == DOT) {
+ accept(DOT);
+ args = typeArgumentsOpt(EXPR);
+ meth = toP(F.at(pos).Select(meth, ident()));
+ }
+ JCMethodInvocation defaultImpl = toP(F.at(pos).Apply(args, meth, defenderMethodParams));
+ body = toP(F.at(pos).Block(0, List.<JCStatement>of(toP(F.at(pos).Exec(defaultImpl)))));
+ defaultValue = null;
+ mode = prevMode;
+ }
+ else if (S.token() == LBRACE) {
body = block();
defaultValue = null;
} else {
@@ -3371,4 +3415,10 @@ public class JavacParser implements Pars
allowThrowTypeParameters = true;
}
}
+ void checkDefenderMethods() {
+ if (!allowDefenderMethods) {
+ log.error(S.pos(), "defender.methods.not.supported.in.source", source.name);
+ allowDefenderMethods = true;
+ }
+ }
}
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Jun 18 13:02:01 2010 +0100
@@ -129,6 +129,8 @@ compiler.err.call.to.super.not.allowed.i
call to super not allowed in enum constructor
compiler.err.no.superclass=\
{0} has no superclass
+compiler.err.bad.defender.method.body=\
+ body of defender method must be a method call
compiler.err.concrete.inheritance.conflict=\
methods {0} from {1} and {2} from {3} are inherited with the same signature
--- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java Fri Jun 18 13:02:01 2010 +0100
@@ -683,6 +683,10 @@ public abstract class JCTree implements
public JCBlock getBody() { return body; }
public JCTree getDefaultValue() { // for annotation types
return defaultValue;
+ }
+ public boolean isExtended() {
+ //return mods.getFlags().contains(Modifier.DEFENDER);
+ return false;
}
@Override
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
--- a/src/share/classes/com/sun/tools/javac/util/Names.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javac/util/Names.java Fri Jun 18 13:02:01 2010 +0100
@@ -91,6 +91,7 @@ public class Names {
public final Name InnerClasses;
public final Name Synthetic;
public final Name Bridge;
+ public final Name Defender;
public final Name Deprecated;
public final Name Enum;
public final Name _name;
@@ -157,6 +158,10 @@ public class Names {
public final Name getDeclaringClass;
public final Name ex;
public final Name finalize;
+ public final Name _extension;
+ public final Name _className;
+ public final Name _methodName;
+ public final Name _methodSignature;
public final Name.Table table;
@@ -210,6 +215,7 @@ public class Names {
InnerClasses = fromString("InnerClasses");
Synthetic = fromString("Synthetic");
Bridge = fromString("Bridge");
+ Defender = fromString("Defender");
Deprecated = fromString("Deprecated");
Enum = fromString("Enum");
_name = fromString("name");
@@ -281,6 +287,10 @@ public class Names {
getDeclaringClass = fromString("getDeclaringClass");
ex = fromString("ex");
finalize = fromString("finalize");
+ _extension = fromString("extension");
+ _className = fromString("className");
+ _methodName = fromString("methodName");
+ _methodSignature = fromString("methodSignature");
}
protected Name.Table createTable(Options options) {
--- a/src/share/classes/com/sun/tools/javap/AttributeWriter.java Thu Jun 17 13:24:27 2010 +0100
+++ b/src/share/classes/com/sun/tools/javap/AttributeWriter.java Fri Jun 18 13:02:01 2010 +0100
@@ -38,6 +38,7 @@ import com.sun.tools.classfile.ConstantP
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.ConstantValue_attribute;
import com.sun.tools.classfile.DefaultAttribute;
+import com.sun.tools.classfile.Defender_attribute;
import com.sun.tools.classfile.Deprecated_attribute;
import com.sun.tools.classfile.EnclosingMethod_attribute;
import com.sun.tools.classfile.Exceptions_attribute;
@@ -211,6 +212,16 @@ public class AttributeWriter extends Bas
println();
return null;
}
+
+ public Void visitDefender(Defender_attribute attr, Void ignore) {
+ print("Defender: #" + attr.method_index);
+ tab();
+ print("// " + getJavaClassName(attr));
+ if (attr.method_index != 0)
+ print("." + getMethodName(attr));
+ println();
+ return null;
+ }
public Void visitDeprecated(Deprecated_attribute attr, Void ignore) {
println("Deprecated: true");
@@ -236,6 +247,22 @@ public class AttributeWriter extends Bas
}
private String getMethodName(EnclosingMethod_attribute a) {
+ try {
+ return a.getMethodName(constant_pool);
+ } catch (ConstantPoolException e) {
+ return report(e);
+ }
+ }
+
+ private String getJavaClassName(Defender_attribute a) {
+ try {
+ return getJavaName(a.getClassName(constant_pool));
+ } catch (ConstantPoolException e) {
+ return report(e);
+ }
+ }
+
+ private String getMethodName(Defender_attribute a) {
try {
return a.getMethodName(constant_pool);
} catch (ConstantPoolException e) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/runtime/DefenderMethod.java Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.runtime;
+
+import java.lang.annotation.*;
+
+
+/**
+ *
+ * @author Maurizio Cimadamore
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface DefenderMethod {
+ String className();
+ String methodName();
+ String methodSignature();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/tools/classfile/Defender_attribute.java Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.classfile;
+
+import java.io.IOException;
+
+/**
+ * Bytecode support for defender methods
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class Defender_attribute extends Attribute {
+ Defender_attribute(ClassReader cr, int name_index, int length) throws IOException {
+ super(name_index, length);
+ class_index = cr.readUnsignedShort();
+ method_index = cr.readUnsignedShort();
+ }
+
+ public Defender_attribute(ConstantPool constant_pool, int class_index, int method_index)
+ throws ConstantPoolException {
+ this(constant_pool.getUTF8Index(Attribute.Defender), class_index, method_index);
+ }
+
+ public Defender_attribute(int name_index, int class_index, int method_index) {
+ super(name_index, 4);
+ this.class_index = class_index;
+ this.method_index = method_index;
+ }
+
+ public <R, D> R accept(Visitor<R, D> visitor, D data) {
+ return visitor.visitDefender(this, data);
+ }
+
+ public String getClassName(ConstantPool constant_pool) throws ConstantPoolException {
+ return constant_pool.getClassInfo(class_index).getName();
+ }
+
+ public String getMethodName(ConstantPool constant_pool) throws ConstantPoolException {
+ if (method_index == 0)
+ return "";
+ return constant_pool.getNameAndTypeInfo(method_index).getName();
+ }
+
+ public final int class_index;
+ public final int method_index;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg01.java Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 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
+ * @summary negative test for ambiguous defender methods
+ * @author Maurizio Cimadamore
+ * @compile/fail/ref=Neg01.out -XDrawDiagnostics Neg01.java
+ */
+
+class Neg01 {
+ interface IA { extension int m() default Neg01.m1; }
+ interface IB { extension int m() default Neg01.m2; }
+
+ static class A implements IA {}
+ static class B implements IB {}
+
+ static class AB implements IA, IB {}
+
+ static int m1(IA a) { return 0; }
+ static int m2(IB b) { return 0; }
+
+ static void test() {
+ new AB().m(); //ambiguous
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Neg01.out Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,2 @@
+Neg01.java:44:17: compiler.err.ref.ambiguous: m, kindname.method, m(), Neg01.IB, kindname.method, m(), Neg01.IA
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Pos01.java Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 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
+ * @summary basic test for defender methods
+ * @author Maurizio Cimadamore
+ * @compile Pos01.java
+ */
+
+import java.util.*;
+
+class Pos01 {
+
+ interface Mapper<T> {
+ T map(T in);
+ }
+
+ interface ExtendedList<T> extends List<T> {
+ extension List<T> map(Mapper<T> r)
+ default Pos01.<T>listMapper;
+ }
+
+ static class MyList<E> extends ArrayList<E> implements ExtendedList<E> {}
+
+ public static void main(String[] args) {
+ MyList<Integer> l = new MyList<Integer>();
+ l.add(1); l.add(2); l.add(3);
+ l.map(#(Integer x)(x * x));
+ }
+
+ static <T> List<T> listMapper(List<T> l, Mapper<T> mapper) {
+ MyList<T> new_list = new MyList<T>();
+ for (T el : l) {
+ new_list.add(mapper.map(el));
+ }
+ return new_list;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defender/Pos02.java Fri Jun 18 13:02:01 2010 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 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
+ * @summary test for explicit resolution of ambiguous defender methods
+ * @author Maurizio Cimadamore
+ * @compile Pos02.java
+ */
+
+class Pos02 {
+ interface IA { extension int m() default Pos02.m1; }
+ interface IB { extension int m() default Pos02.m2; }
+
+ static class A implements IA {}
+ static class B implements IB {}
+
+ static class AB implements IA, IB {
+ void test() {
+ IA.this.m();
+ IA.super.m();
+ }
+ }
+
+ static int m1(IA a) { return 0; }
+ static int m2(IB b) { return 0; }
+}