6843077: JSR 308: Annotations on types

Co-authored-by: Mahmood Ali <mali@csail.mit.edu>
Co-authored-by: Matt Papi <mpapi@csail.mit.edu>
Reviewed-by: jjg, mcimadamore, darcy
This commit is contained in:
Michael Ernst 2009-06-26 18:51:39 -07:00 committed by Jonathan Gibbons
parent 2b12b62ad4
commit 5a1465b9de
45 changed files with 1856 additions and 98 deletions

View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. # Copyright 2006-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -44,7 +44,27 @@ if [ "$LANGTOOLS_USE_BOOTCLASSPATH" != "no" ]; then
bcp="$mylib/#PROGRAM#.jar":$cp bcp="$mylib/#PROGRAM#.jar":$cp
fi fi
# javac currently assumes that assertions are enabled in the launcher # tools currently assumes that assertions are enabled in the launcher
ea=-ea:com.sun.tools ea=-ea:com.sun.tools
"#TARGET_JAVA#" ${bcp:+-Xbootclasspath/p:"$bcp"} ${ea} -jar "${mydir}"/../lib/#PROGRAM#.jar "$@" # Any parameters starting with -J are passed to the JVM.
# All other parameters become parameters of #PROGRAM#.
# Separate out -J* options for the JVM
# Note jdk as possible default to run jtreg
# Unset IFS and use newline as arg separator to preserve spaces in args
DUALCASE=1 # for MKS: make case statement case-sensitive (6709498)
saveIFS="$IFS"
nl='
'
for i in "$@" ; do
IFS=
case $i in
-J* ) javaOpts=$javaOpts$nl`echo $i | sed -e 's/^-J//'` ;;
* ) toolOpts=$toolOpts$nl$i ;;
esac
IFS="$saveIFS"
done
unset DUALCASE
eval "#TARGET_JAVA#" "${bcp:+-Xbootclasspath/p:"$bcp"}" ${ea} ${javaOpts} -jar "${mydir}"/../lib/#PROGRAM#.jar ${toolOpts}

View file

@ -53,6 +53,7 @@ public interface MethodTree extends Tree {
Tree getReturnType(); Tree getReturnType();
List<? extends TypeParameterTree> getTypeParameters(); List<? extends TypeParameterTree> getTypeParameters();
List<? extends VariableTree> getParameters(); List<? extends VariableTree> getParameters();
List<? extends AnnotationTree> getReceiverAnnotations();
List<? extends ExpressionTree> getThrows(); List<? extends ExpressionTree> getThrows();
BlockTree getBody(); BlockTree getBody();
Tree getDefaultValue(); // for annotation types Tree getDefaultValue(); // for annotation types

View file

@ -45,6 +45,9 @@ public interface Tree {
* Enumerates all kinds of trees. * Enumerates all kinds of trees.
*/ */
public enum Kind { public enum Kind {
ANNOTATED_TYPE(AnnotatedTypeTree.class),
/** /**
* Used for instances of {@link AnnotationTree}. * Used for instances of {@link AnnotationTree}.
*/ */

View file

@ -57,6 +57,7 @@ package com.sun.source.tree;
* @since 1.6 * @since 1.6
*/ */
public interface TreeVisitor<R,P> { public interface TreeVisitor<R,P> {
R visitAnnotatedType(AnnotatedTypeTree node, P p);
R visitAnnotation(AnnotationTree node, P p); R visitAnnotation(AnnotationTree node, P p);
R visitMethodInvocation(MethodInvocationTree node, P p); R visitMethodInvocation(MethodInvocationTree node, P p);
R visitAssert(AssertTree node, P p); R visitAssert(AssertTree node, P p);

View file

@ -47,4 +47,5 @@ import javax.lang.model.element.Name;
public interface TypeParameterTree extends Tree { public interface TypeParameterTree extends Tree {
Name getName(); Name getName();
List<? extends Tree> getBounds(); List<? extends Tree> getBounds();
List<? extends AnnotationTree> getAnnotations();
} }

View file

@ -244,6 +244,10 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p); return defaultAction(node, p);
} }
public R visitAnnotatedType(AnnotatedTypeTree node, P p) {
return defaultAction(node, p);
}
public R visitErroneous(ErroneousTree node, P p) { public R visitErroneous(ErroneousTree node, P p) {
return defaultAction(node, p); return defaultAction(node, p);
} }

View file

@ -120,19 +120,20 @@ public class TreePath implements Iterable<Tree> {
public Iterator<Tree> iterator() { public Iterator<Tree> iterator() {
return new Iterator<Tree>() { return new Iterator<Tree>() {
public boolean hasNext() { public boolean hasNext() {
return curr.parent != null; return next != null;
} }
public Tree next() { public Tree next() {
curr = curr.parent; Tree t = next.leaf;
return curr.leaf; next = next.parent;
return t;
} }
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
private TreePath curr; private TreePath next = TreePath.this;
}; };
} }

View file

@ -138,6 +138,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
r = scanAndReduce(node.getReturnType(), p, r); r = scanAndReduce(node.getReturnType(), p, r);
r = scanAndReduce(node.getTypeParameters(), p, r); r = scanAndReduce(node.getTypeParameters(), p, r);
r = scanAndReduce(node.getParameters(), p, r); r = scanAndReduce(node.getParameters(), p, r);
r = scanAndReduce(node.getReceiverAnnotations(), p, r);
r = scanAndReduce(node.getThrows(), p, r); r = scanAndReduce(node.getThrows(), p, r);
r = scanAndReduce(node.getBody(), p, r); r = scanAndReduce(node.getBody(), p, r);
return r; return r;
@ -354,7 +355,9 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
} }
public R visitTypeParameter(TypeParameterTree node, P p) { public R visitTypeParameter(TypeParameterTree node, P p) {
return scan(node.getBounds(), p); R r = scan(node.getAnnotations(), p);
r = scanAndReduce(node.getBounds(), p, r);
return r;
} }
public R visitWildcard(WildcardTree node, P p) { public R visitWildcard(WildcardTree node, P p) {
@ -371,6 +374,12 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
return r; return r;
} }
public R visitAnnotatedType(AnnotatedTypeTree node, P p) {
R r = scan(node.getAnnotations(), p);
r = scanAndReduce(node.getUnderlyingType(), p, r);
return r;
}
public R visitOther(Tree node, P p) { public R visitOther(Tree node, P p) {
return null; return null;
} }

View file

@ -35,6 +35,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType; import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaCompiler.CompilationTask;
import com.sun.source.tree.ClassTree; import com.sun.source.tree.ClassTree;
@ -182,7 +183,20 @@ public abstract class Trees {
/** /**
* Gets the original type from the ErrorType object. * Gets the original type from the ErrorType object.
* @param errorType The errorType for which we want to get the original type. * @param errorType The errorType for which we want to get the original type.
* @returns javax.lang.model.type.TypeMirror corresponding to the original type, replaced by the ErrorType. * @return javax.lang.model.type.TypeMirror corresponding to the original type, replaced by the ErrorType.
*/ */
public abstract TypeMirror getOriginalType(ErrorType errorType); public abstract TypeMirror getOriginalType(ErrorType errorType);
/**
* Prints a message of the specified kind at the location of the
* tree within the provided compilation unit
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
* @param t the tree to use as a position hint
* @param root the compilation unit that contains tree
*/
public abstract void printMessage(Diagnostic.Kind kind, CharSequence msg,
com.sun.source.tree.Tree t,
com.sun.source.tree.CompilationUnitTree root);
} }

View file

@ -54,6 +54,8 @@ public abstract class Attribute {
public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations"; public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations"; public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations";
public static final String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations"; public static final String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
public static final String RuntimeVisibleTypeAnnotations = "RuntimeVisibleTypeAnnotations";
public static final String RuntimeInvisibleTypeAnnotations = "RuntimeInvisibleTypeAnnotations";
public static final String Signature = "Signature"; public static final String Signature = "Signature";
public static final String SourceDebugExtension = "SourceDebugExtension"; public static final String SourceDebugExtension = "SourceDebugExtension";
public static final String SourceFile = "SourceFile"; public static final String SourceFile = "SourceFile";
@ -131,6 +133,8 @@ public abstract class Attribute {
standardAttributes.put(RuntimeInvisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations_attribute.class); standardAttributes.put(RuntimeInvisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations_attribute.class);
standardAttributes.put(RuntimeVisibleAnnotations, RuntimeVisibleAnnotations_attribute.class); standardAttributes.put(RuntimeVisibleAnnotations, RuntimeVisibleAnnotations_attribute.class);
standardAttributes.put(RuntimeVisibleParameterAnnotations, RuntimeVisibleParameterAnnotations_attribute.class); standardAttributes.put(RuntimeVisibleParameterAnnotations, RuntimeVisibleParameterAnnotations_attribute.class);
standardAttributes.put(RuntimeVisibleTypeAnnotations, RuntimeVisibleTypeAnnotations_attribute.class);
standardAttributes.put(RuntimeInvisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations_attribute.class);
standardAttributes.put(Signature, Signature_attribute.class); standardAttributes.put(Signature, Signature_attribute.class);
standardAttributes.put(SourceID, SourceID_attribute.class); standardAttributes.put(SourceID, SourceID_attribute.class);
} }
@ -184,6 +188,8 @@ public abstract class Attribute {
R visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, P p); R visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, P p);
R visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, P p); R visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, P p);
R visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, P p); R visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, P p);
R visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, P p);
R visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, P p);
R visitSignature(Signature_attribute attr, P p); R visitSignature(Signature_attribute attr, P p);
R visitSourceDebugExtension(SourceDebugExtension_attribute attr, P p); R visitSourceDebugExtension(SourceDebugExtension_attribute attr, P p);
R visitSourceFile(SourceFile_attribute attr, P p); R visitSourceFile(SourceFile_attribute attr, P p);

View file

@ -1,3 +1,4 @@
/* /*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@ -477,6 +478,16 @@ public class ClassWriter {
return null; return null;
} }
public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, ClassOutputStream out) {
annotationWriter.write(attr.annotations, out);
return null;
}
public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, ClassOutputStream out) {
annotationWriter.write(attr.annotations, out);
return null;
}
public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, ClassOutputStream out) { public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, ClassOutputStream out) {
out.writeByte(attr.parameter_annotations.length); out.writeByte(attr.parameter_annotations.length);
for (Annotation[] annos: attr.parameter_annotations) for (Annotation[] annos: attr.parameter_annotations)
@ -636,6 +647,12 @@ public class ClassWriter {
write(anno, out); write(anno, out);
} }
public void write(ExtendedAnnotation[] annos, ClassOutputStream out) {
out.writeShort(annos.length);
for (ExtendedAnnotation anno: annos)
write(anno, out);
}
public void write(Annotation anno, ClassOutputStream out) { public void write(Annotation anno, ClassOutputStream out) {
out.writeShort(anno.type_index); out.writeShort(anno.type_index);
out.writeShort(anno.element_value_pairs.length); out.writeShort(anno.element_value_pairs.length);
@ -643,6 +660,11 @@ public class ClassWriter {
write(p, out); write(p, out);
} }
public void write(ExtendedAnnotation anno, ClassOutputStream out) {
write(anno.annotation, out);
write(anno.position, out);
}
public void write(element_value_pair pair, ClassOutputStream out) { public void write(element_value_pair pair, ClassOutputStream out) {
out.writeShort(pair.element_name_index); out.writeShort(pair.element_name_index);
write(pair.value, out); write(pair.value, out);
@ -680,5 +702,95 @@ public class ClassWriter {
write(v, out); write(v, out);
return null; return null;
} }
private void write(ExtendedAnnotation.Position p, ClassOutputStream out) {
out.writeByte(p.type.targetTypeValue());
switch (p.type) {
// type case
case TYPECAST:
case TYPECAST_GENERIC_OR_ARRAY:
// object creation
case INSTANCEOF:
case INSTANCEOF_GENERIC_OR_ARRAY:
// new expression
case NEW:
case NEW_GENERIC_OR_ARRAY:
case NEW_TYPE_ARGUMENT:
case NEW_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
out.writeShort(p.offset);
break;
// local variable
case LOCAL_VARIABLE:
case LOCAL_VARIABLE_GENERIC_OR_ARRAY:
int table_length = p.lvarOffset.length;
out.writeShort(table_length);
for (int i = 0; i < table_length; ++i) {
out.writeShort(1); // for table length
out.writeShort(p.lvarOffset[i]);
out.writeShort(p.lvarLength[i]);
out.writeShort(p.lvarIndex[i]);
}
break;
// method receiver
case METHOD_RECEIVER:
// Do nothing
break;
// type parameters
case CLASS_TYPE_PARAMETER:
case METHOD_TYPE_PARAMETER:
out.writeByte(p.parameter_index);
break;
// type parameters bounds
case CLASS_TYPE_PARAMETER_BOUND:
case CLASS_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
case METHOD_TYPE_PARAMETER_BOUND:
case METHOD_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
out.writeByte(p.parameter_index);
out.writeByte(p.bound_index);
break;
// wildcards
case WILDCARD_BOUND:
case WILDCARD_BOUND_GENERIC_OR_ARRAY:
write(p.wildcard_position, out);
break;
// Class extends and implements clauses
case CLASS_EXTENDS:
case CLASS_EXTENDS_GENERIC_OR_ARRAY:
out.writeByte(p.type_index);
break;
// throws
case THROWS:
out.writeByte(p.type_index);
break;
case CLASS_LITERAL:
out.writeShort(p.offset);
break;
// method parameter: not specified
case METHOD_PARAMETER_GENERIC_OR_ARRAY:
out.writeByte(p.parameter_index);
break;
// method type argument: wasn't specified
case METHOD_TYPE_ARGUMENT:
case METHOD_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
out.writeShort(p.offset);
out.writeByte(p.type_index);
break;
// We don't need to worry abut these
case METHOD_RETURN_GENERIC_OR_ARRAY:
case FIELD_GENERIC_OR_ARRAY:
break;
case UNKNOWN:
break;
default:
throw new AssertionError("unknown type: " + p);
}
// Append location data for generics/arrays.
if (p.type.hasLocation()) {
out.writeShort(p.location.size());
for (int i : p.location)
out.writeByte((byte)i);
}
}
} }
} }

View file

@ -133,6 +133,7 @@ public class JavacTaskImpl extends JavacTask {
public Boolean call() { public Boolean call() {
if (!used.getAndSet(true)) { if (!used.getAndSet(true)) {
beginContext(); beginContext();
notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
try { try {
compilerMain.setFatalErrors(true); compilerMain.setFatalErrors(true);
result = compilerMain.compile(args, context, fileObjects, processors); result = compilerMain.compile(args, context, fileObjects, processors);
@ -143,6 +144,7 @@ public class JavacTaskImpl extends JavacTask {
args = null; args = null;
context = null; context = null;
fileObjects = null; fileObjects = null;
notYetEntered = null;
return result == 0; return result == 0;
} else { } else {
throw new IllegalStateException("multiple calls to method 'call'"); throw new IllegalStateException("multiple calls to method 'call'");

View file

@ -35,6 +35,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
@ -54,6 +55,7 @@ import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.MemberEnter; import com.sun.tools.javac.comp.MemberEnter;
import com.sun.tools.javac.comp.Resolve; import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.model.JavacElements; import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacMessager;
import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree;
@ -61,6 +63,7 @@ import com.sun.tools.javac.tree.TreeCopier;
import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Pair; import com.sun.tools.javac.util.Pair;
@ -336,4 +339,54 @@ public class JavacTrees extends Trees {
return com.sun.tools.javac.code.Type.noType; return com.sun.tools.javac.code.Type.noType;
} }
/**
* Prints a message of the specified kind at the location of the
* tree within the provided compilation unit
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
* @param t the tree to use as a position hint
* @param root the compilation unit that contains tree
*/
public void printMessage(Diagnostic.Kind kind, CharSequence msg,
com.sun.source.tree.Tree t,
com.sun.source.tree.CompilationUnitTree root) {
JavaFileObject oldSource = null;
JavaFileObject newSource = null;
JCDiagnostic.DiagnosticPosition pos = null;
newSource = root.getSourceFile();
if (newSource != null) {
oldSource = log.useSource(newSource);
pos = ((JCTree) t).pos();
}
try {
switch (kind) {
case ERROR:
boolean prev = log.multipleErrors;
try {
log.error(pos, "proc.messager", msg.toString());
} finally {
log.multipleErrors = prev;
}
break;
case WARNING:
log.warning(pos, "proc.messager", msg.toString());
break;
case MANDATORY_WARNING:
log.mandatoryWarning(pos, "proc.messager", msg.toString());
break;
default:
log.note(pos, "proc.messager", msg.toString());
}
} finally {
if (oldSource != null)
log.useSource(oldSource);
}
}
} }

View file

@ -204,6 +204,21 @@ public abstract class Attribute implements AnnotationValue {
} }
} }
public static class TypeCompound extends Compound {
public TypeAnnotationPosition position;
public TypeCompound(Compound compound,
TypeAnnotationPosition position) {
this(compound.type, compound.values, position);
}
public TypeCompound(Type type,
List<Pair<MethodSymbol, Attribute>> values,
TypeAnnotationPosition position) {
super(type, values);
this.position = position;
}
}
/** The value for an annotation element of an array type. /** The value for an annotation element of an array type.
*/ */
public static class Array extends Attribute { public static class Array extends Attribute {

View file

@ -153,6 +153,9 @@ public enum Source {
public boolean enforceMandatoryWarnings() { public boolean enforceMandatoryWarnings() {
return compareTo(JDK1_5) >= 0; return compareTo(JDK1_5) >= 0;
} }
public boolean allowTypeAnnotations() {
return compareTo(JDK1_7) >= 0;
}
public static SourceVersion toSourceVersion(Source source) { public static SourceVersion toSourceVersion(Source source) {
switch(source) { switch(source) {
case JDK1_2: case JDK1_2:

View file

@ -100,6 +100,17 @@ public abstract class Symbol implements Element {
*/ */
public Type type; public Type type;
/** The type annotations targeted to a tree directly owned by this symbol
*/
// type annotations are stored here for two purposes:
// - convenient location to store annotations for generation after erasure
// - a private interface for accessing type annotations parsed from
// classfiles
// the field is populated for the following declaration only
// class, field, variable and type parameters
//
public List<Attribute.TypeCompound> typeAnnotations;
/** The owner of this symbol. /** The owner of this symbol.
*/ */
public Symbol owner; public Symbol owner;
@ -122,6 +133,7 @@ public abstract class Symbol implements Element {
this.completer = null; this.completer = null;
this.erasure_field = null; this.erasure_field = null;
this.attributes_field = List.nil(); this.attributes_field = List.nil();
this.typeAnnotations = List.nil();
this.name = name; this.name = name;
} }

View file

@ -700,7 +700,6 @@ public class Attr extends JCTree.Visitor {
localEnv.info.scope.leave(); localEnv.info.scope.leave();
result = tree.type = m.type; result = tree.type = m.type;
chk.validateAnnotations(tree.mods.annotations, m); chk.validateAnnotations(tree.mods.annotations, m);
} }
finally { finally {
chk.setLint(prevLint); chk.setLint(prevLint);
@ -2516,10 +2515,11 @@ public class Attr extends JCTree.Visitor {
Type clazzOuter = clazztype.getEnclosingType(); Type clazzOuter = clazztype.getEnclosingType();
if (clazzOuter.tag == CLASS) { if (clazzOuter.tag == CLASS) {
Type site; Type site;
if (tree.clazz.getTag() == JCTree.IDENT) { JCExpression clazz = TreeInfo.typeIn(tree.clazz);
if (clazz.getTag() == JCTree.IDENT) {
site = env.enclClass.sym.type; site = env.enclClass.sym.type;
} else if (tree.clazz.getTag() == JCTree.SELECT) { } else if (clazz.getTag() == JCTree.SELECT) {
site = ((JCFieldAccess) tree.clazz).selected.type; site = ((JCFieldAccess) clazz).selected.type;
} else throw new AssertionError(""+tree); } else throw new AssertionError(""+tree);
if (clazzOuter.tag == CLASS && site != clazzOuter) { if (clazzOuter.tag == CLASS && site != clazzOuter) {
if (site.tag == CLASS) if (site.tag == CLASS)
@ -2628,6 +2628,10 @@ public class Attr extends JCTree.Visitor {
result = tree.type = syms.errType; result = tree.type = syms.errType;
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
result = tree.type = attribType(tree.getUnderlyingType(), env);
}
public void visitErroneous(JCErroneous tree) { public void visitErroneous(JCErroneous tree) {
if (tree.errs != null) if (tree.errs != null)
for (JCTree err : tree.errs) for (JCTree err : tree.errs)
@ -2816,6 +2820,9 @@ public class Attr extends JCTree.Visitor {
(c.flags() & ABSTRACT) == 0) { (c.flags() & ABSTRACT) == 0) {
checkSerialVersionUID(tree, c); checkSerialVersionUID(tree, c);
} }
// Check type annotations applicability rules
validateTypeAnnotations(tree);
} }
// where // where
/** check if a class is a subtype of Serializable, if that is available. */ /** check if a class is a subtype of Serializable, if that is available. */
@ -2858,4 +2865,33 @@ public class Attr extends JCTree.Visitor {
private Type capture(Type type) { private Type capture(Type type) {
return types.capture(type); return types.capture(type);
} }
private void validateTypeAnnotations(JCTree tree) {
tree.accept(typeAnnotationsValidator);
}
//where
private final JCTree.Visitor typeAnnotationsValidator =
new TreeScanner() {
public void visitAnnotation(JCAnnotation tree) {
if (tree instanceof JCTypeAnnotation) {
chk.validateTypeAnnotation((JCTypeAnnotation)tree, false);
}
super.visitAnnotation(tree);
}
public void visitTypeParameter(JCTypeParameter tree) {
chk.validateTypeAnnotations(tree.annotations, true);
// don't call super. skip type annotations
scan(tree.bounds);
}
public void visitMethodDef(JCMethodDecl tree) {
// need to check static methods
if ((tree.sym.flags() & Flags.STATIC) != 0) {
for (JCTypeAnnotation a : tree.receiverAnnotations) {
if (chk.isTypeAnnotation(a, false))
log.error(a.pos(), "annotation.type.not.applicable");
}
}
super.visitMethodDef(tree);
}
};
} }

View file

@ -916,6 +916,10 @@ public class Check {
} }
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
tree.underlyingType.accept(this);
}
/** Default visitor method: do nothing. /** Default visitor method: do nothing.
*/ */
public void visitTree(JCTree tree) { public void visitTree(JCTree tree) {
@ -1806,6 +1810,14 @@ public class Check {
validateAnnotation(a, s); validateAnnotation(a, s);
} }
/** Check the type annotations
*/
public void validateTypeAnnotations(List<JCTypeAnnotation> annotations, boolean isTypeParameter) {
if (skipAnnotations) return;
for (JCTypeAnnotation a : annotations)
validateTypeAnnotation(a, isTypeParameter);
}
/** Check an annotation of a symbol. /** Check an annotation of a symbol.
*/ */
public void validateAnnotation(JCAnnotation a, Symbol s) { public void validateAnnotation(JCAnnotation a, Symbol s) {
@ -1820,6 +1832,15 @@ public class Check {
} }
} }
public void validateTypeAnnotation(JCTypeAnnotation a, boolean isTypeParameter) {
if (a.type == null)
throw new AssertionError("annotation tree hasn't been attributed yet: " + a);
validateAnnotation(a);
if (!isTypeAnnotation(a, isTypeParameter))
log.error(a.pos(), "annotation.type.not.applicable");
}
/** Is s a method symbol that overrides a method in a superclass? */ /** Is s a method symbol that overrides a method in a superclass? */
boolean isOverrider(Symbol s) { boolean isOverrider(Symbol s) {
if (s.kind != MTH || s.isStatic()) if (s.kind != MTH || s.isStatic())
@ -1838,6 +1859,25 @@ public class Check {
return false; return false;
} }
/** Is the annotation applicable to type annotations */
boolean isTypeAnnotation(JCTypeAnnotation a, boolean isTypeParameter) {
Attribute.Compound atTarget =
a.annotationType.type.tsym.attribute(syms.annotationTargetType.tsym);
if (atTarget == null) return true;
Attribute atValue = atTarget.member(names.value);
if (!(atValue instanceof Attribute.Array)) return true; // error recovery
Attribute.Array arr = (Attribute.Array) atValue;
for (Attribute app : arr.values) {
if (!(app instanceof Attribute.Enum)) return true; // recovery
Attribute.Enum e = (Attribute.Enum) app;
if (!isTypeParameter && e.value.name == names.TYPE_USE)
return true;
else if (isTypeParameter && e.value.name == names.TYPE_PARAMETER)
return true;
}
return false;
}
/** Is the annotation applicable to the symbol? */ /** Is the annotation applicable to the symbol? */
boolean annotationApplicable(JCAnnotation a, Symbol s) { boolean annotationApplicable(JCAnnotation a, Symbol s) {
Attribute.Compound atTarget = Attribute.Compound atTarget =
@ -1874,6 +1914,13 @@ public class Check {
} }
else if (e.value.name == names.PACKAGE) else if (e.value.name == names.PACKAGE)
{ if (s.kind == PCK) return true; } { if (s.kind == PCK) return true; }
else if (e.value.name == names.TYPE_USE)
{ if (s.kind == TYP ||
s.kind == VAR ||
(s.kind == MTH && !s.isConstructor() &&
s.type.getReturnType().tag != VOID))
return true;
}
else else
return true; // recovery return true; // recovery
} }

View file

@ -1245,6 +1245,11 @@ public class Flow extends TreeScanner {
} }
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
// annotations don't get scanned
tree.underlyingType.accept(this);
}
public void visitIdent(JCIdent tree) { public void visitIdent(JCIdent tree) {
if (tree.sym.kind == VAR) if (tree.sym.kind == VAR)
checkInit(tree.pos(), (VarSymbol)tree.sym); checkInit(tree.pos(), (VarSymbol)tree.sym);
@ -1254,7 +1259,8 @@ public class Flow extends TreeScanner {
super.visitTypeCast(tree); super.visitTypeCast(tree);
if (!tree.type.isErroneous() if (!tree.type.isErroneous()
&& lint.isEnabled(Lint.LintCategory.CAST) && lint.isEnabled(Lint.LintCategory.CAST)
&& types.isSameType(tree.expr.type, tree.clazz.type)) { && types.isSameType(tree.expr.type, tree.clazz.type)
&& !(ignoreAnnotatedCasts && containsTypeAnnotation(tree.clazz))) {
log.warning(tree.pos(), "redundant.cast", tree.expr.type); log.warning(tree.pos(), "redundant.cast", tree.expr.type);
} }
} }
@ -1263,6 +1269,23 @@ public class Flow extends TreeScanner {
// Do nothing for TopLevel since each class is visited individually // Do nothing for TopLevel since each class is visited individually
} }
/**************************************************************************
* utility methods for ignoring type-annotated casts lint checking
*************************************************************************/
private static final boolean ignoreAnnotatedCasts = true;
private static class AnnotationFinder extends TreeScanner {
public boolean foundTypeAnno = false;
public void visitAnnotation(JCAnnotation tree) {
foundTypeAnno = foundTypeAnno || (tree instanceof JCTypeAnnotation);
}
}
private boolean containsTypeAnnotation(JCTree e) {
AnnotationFinder finder = new AnnotationFinder();
finder.scan(e);
return finder.foundTypeAnno;
}
/************************************************************************** /**************************************************************************
* main method * main method
*************************************************************************/ *************************************************************************/

View file

@ -2369,6 +2369,11 @@ public class Lower extends TreeTranslator {
result = tree; result = tree;
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
tree.underlyingType = translate(tree.underlyingType);
result = tree.underlyingType;
}
public void visitTypeCast(JCTypeCast tree) { public void visitTypeCast(JCTypeCast tree) {
tree.clazz = translate(tree.clazz); tree.clazz = translate(tree.clazz);
if (tree.type.isPrimitive() != tree.expr.type.isPrimitive()) if (tree.type.isPrimitive() != tree.expr.type.isPrimitive())

View file

@ -99,8 +99,8 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
types = Types.instance(context); types = Types.instance(context);
diags = JCDiagnostic.Factory.instance(context); diags = JCDiagnostic.Factory.instance(context);
target = Target.instance(context); target = Target.instance(context);
skipAnnotations = Options options = Options.instance(context);
Options.instance(context).get("skipAnnotations") != null; skipAnnotations = options.get("skipAnnotations") != null;
} }
/** A queue for classes whose members still need to be entered into the /** A queue for classes whose members still need to be entered into the
@ -906,6 +906,10 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
if (hasDeprecatedAnnotation(tree.mods.annotations)) if (hasDeprecatedAnnotation(tree.mods.annotations))
c.flags_field |= DEPRECATED; c.flags_field |= DEPRECATED;
annotateLater(tree.mods.annotations, baseEnv, c); annotateLater(tree.mods.annotations, baseEnv, c);
// class type parameters use baseEnv but everything uses env
for (JCTypeParameter tp : tree.typarams)
tp.accept(new TypeAnnotate(baseEnv));
tree.accept(new TypeAnnotate(env));
chk.checkNonCyclic(tree.pos(), c.type); chk.checkNonCyclic(tree.pos(), c.type);
@ -988,6 +992,100 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
} }
} }
// A sub-phase that "compiles" annotations in annotated types.
private class TypeAnnotate extends TreeScanner {
private Env<AttrContext> env;
public TypeAnnotate(Env<AttrContext> env) { this.env = env; }
private void enterTypeAnnotations(List<JCTypeAnnotation> annotations) {
Set<TypeSymbol> annotated = new HashSet<TypeSymbol>();
if (!skipAnnotations)
for (List<JCTypeAnnotation> al = annotations; al.nonEmpty(); al = al.tail) {
JCTypeAnnotation a = al.head;
Attribute.Compound c = annotate.enterAnnotation(a,
syms.annotationType,
env);
if (c == null) continue;
Attribute.TypeCompound tc = new Attribute.TypeCompound(c.type, c.values, a.annotation_position);
a.attribute_field = tc;
// Note: @Deprecated has no effect on local variables and parameters
if (!annotated.add(a.type.tsym))
log.error(a.pos, "duplicate.annotation");
}
}
// each class (including enclosed inner classes) should be visited
// separately through MemberEnter.complete(Symbol)
// this flag is used to prevent from visiting inner classes.
private boolean isEnclosingClass = false;
@Override
public void visitClassDef(final JCClassDecl tree) {
if (isEnclosingClass)
return;
isEnclosingClass = true;
scan(tree.mods);
// type parameter need to be visited with a separate env
// scan(tree.typarams);
scan(tree.extending);
scan(tree.implementing);
scan(tree.defs);
}
private void annotate(final JCTree tree, final List<JCTypeAnnotation> annotations) {
annotate.later(new Annotate.Annotator() {
public String toString() {
return "annotate " + annotations + " onto " + tree;
}
public void enterAnnotation() {
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
try {
enterTypeAnnotations(annotations);
// enrich type parameter symbols... easier for annotation processors
if (tree instanceof JCTypeParameter) {
JCTypeParameter typeparam = (JCTypeParameter)tree;
ListBuffer<Attribute.Compound> buf = ListBuffer.lb();
for (JCTypeAnnotation anno : annotations)
buf.add(anno.attribute_field);
typeparam.type.tsym.attributes_field = buf.toList();
}
} finally {
log.useSource(prev);
}
}
});
}
@Override
public void visitAnnotatedType(final JCAnnotatedType tree) {
annotate(tree, tree.annotations);
super.visitAnnotatedType(tree);
}
@Override
public void visitTypeParameter(final JCTypeParameter tree) {
annotate(tree, tree.annotations);
super.visitTypeParameter(tree);
}
@Override
public void visitNewArray(final JCNewArray tree) {
annotate(tree, tree.annotations);
for (List<JCTypeAnnotation> dimAnnos : tree.dimAnnotations)
annotate(tree, dimAnnos);
super.visitNewArray(tree);
}
@Override
public void visitApply(JCMethodInvocation tree) {
super.visitApply(tree);
scan(tree.typeargs);
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
annotate(tree, tree.receiverAnnotations);
super.visitMethodDef(tree);
}
}
private Env<AttrContext> baseEnv(JCClassDecl tree, Env<AttrContext> env) { private Env<AttrContext> baseEnv(JCClassDecl tree, Env<AttrContext> env) {
Scope typaramScope = new Scope(tree.sym); Scope typaramScope = new Scope(tree.sym);
if (tree.typarams != null) if (tree.typarams != null)

View file

@ -27,6 +27,8 @@ package com.sun.tools.javac.comp;
import java.util.*; import java.util.*;
import javax.lang.model.element.ElementKind;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.*;
@ -59,6 +61,8 @@ public class TransTypes extends TreeTranslator {
return instance; return instance;
} }
private boolean debugJSR308;
private Names names; private Names names;
private Log log; private Log log;
private Symtab syms; private Symtab syms;
@ -88,6 +92,7 @@ public class TransTypes extends TreeTranslator {
types = Types.instance(context); types = Types.instance(context);
make = TreeMaker.instance(context); make = TreeMaker.instance(context);
resolve = Resolve.instance(context); resolve = Resolve.instance(context);
debugJSR308 = Options.instance(context).get("TA:trans") != null;
} }
/** A hashtable mapping bridge methods to the methods they override after /** A hashtable mapping bridge methods to the methods they override after
@ -435,12 +440,15 @@ public class TransTypes extends TreeTranslator {
} }
public void visitClassDef(JCClassDecl tree) { public void visitClassDef(JCClassDecl tree) {
new TypeAnnotationPositions().scan(tree);
new TypeAnnotationLift().scan(tree);
translateClass(tree.sym); translateClass(tree.sym);
result = tree; result = tree;
} }
JCMethodDecl currentMethod = null; JCMethodDecl currentMethod = null;
public void visitMethodDef(JCMethodDecl tree) { public void visitMethodDef(JCMethodDecl tree) {
tree.sym.typeAnnotations = tree.sym.typeAnnotations;
JCMethodDecl previousMethod = currentMethod; JCMethodDecl previousMethod = currentMethod;
try { try {
currentMethod = tree; currentMethod = tree;
@ -726,8 +734,8 @@ public class TransTypes extends TreeTranslator {
/** Visitor method for parameterized types. /** Visitor method for parameterized types.
*/ */
public void visitTypeApply(JCTypeApply tree) { public void visitTypeApply(JCTypeApply tree) {
// Delete all type parameters. JCTree clazz = translate(tree.clazz, null);
result = translate(tree.clazz, null); result = clazz;
} }
/************************************************************************** /**************************************************************************
@ -793,4 +801,342 @@ public class TransTypes extends TreeTranslator {
pt = null; pt = null;
return translate(cdef, null); return translate(cdef, null);
} }
private class TypeAnnotationPositions extends TreeScanner {
private ListBuffer<JCTree> frames = ListBuffer.lb();
private void push(JCTree t) { frames = frames.prepend(t); }
private JCTree pop() { return frames.next(); }
private JCTree peek() { return frames.first(); }
private JCTree peek2() { return frames.toList().tail.head; }
@Override
public void scan(JCTree tree) {
push(tree);
super.scan(tree);
pop();
}
private TypeAnnotationPosition resolveFrame(JCTree tree, JCTree frame,
List<JCTree> path, TypeAnnotationPosition p) {
switch (frame.getKind()) {
case TYPE_CAST:
p.type = TargetType.TYPECAST;
p.pos = frame.pos;
return p;
case INSTANCE_OF:
p.type = TargetType.INSTANCEOF;
p.pos = frame.pos;
return p;
case NEW_CLASS:
p.type = TargetType.NEW;
p.pos = frame.pos;
return p;
case NEW_ARRAY:
p.type = TargetType.NEW;
p.pos = frame.pos;
return p;
case CLASS:
p.pos = frame.pos;
if (((JCClassDecl)frame).extending == tree) {
p.type = TargetType.CLASS_EXTENDS;
p.type_index = -1;
} else if (((JCClassDecl)frame).implementing.contains(tree)) {
p.type = TargetType.CLASS_EXTENDS;
p.type_index = ((JCClassDecl)frame).implementing.indexOf(tree);
} else if (((JCClassDecl)frame).typarams.contains(tree)) {
p.type = TargetType.CLASS_TYPE_PARAMETER;
p.parameter_index = ((JCClassDecl)frame).typarams.indexOf(tree);
} else
throw new AssertionError();
return p;
case METHOD: {
JCMethodDecl frameMethod = (JCMethodDecl)frame;
p.pos = frame.pos;
if (frameMethod.receiverAnnotations.contains(tree))
p.type = TargetType.METHOD_RECEIVER;
else if (frameMethod.thrown.contains(tree)) {
p.type = TargetType.THROWS;
p.type_index = frameMethod.thrown.indexOf(tree);
} else if (((JCMethodDecl)frame).restype == tree) {
p.type = TargetType.METHOD_RETURN_GENERIC_OR_ARRAY;
} else if (frameMethod.typarams.contains(tree)) {
p.type = TargetType.METHOD_TYPE_PARAMETER;
p.parameter_index = frameMethod.typarams.indexOf(tree);
} else
throw new AssertionError();
return p;
}
case MEMBER_SELECT: {
JCFieldAccess fieldFrame = (JCFieldAccess)frame;
if (fieldFrame.name == names._class) {
p.type = TargetType.CLASS_LITERAL;
if (fieldFrame.selected instanceof JCAnnotatedType) {
p.pos = TreeInfo.typeIn(fieldFrame).pos;
} else if (fieldFrame.selected instanceof JCArrayTypeTree) {
p.pos = fieldFrame.selected.pos;
}
} else
throw new AssertionError();
return p;
}
case PARAMETERIZED_TYPE: {
TypeAnnotationPosition nextP;
if (((JCTypeApply)frame).clazz == tree)
nextP = p; // generic: RAW; noop
else if (((JCTypeApply)frame).arguments.contains(tree))
p.location = p.location.prepend(
((JCTypeApply)frame).arguments.indexOf(tree));
else
throw new AssertionError();
List<JCTree> newPath = path.tail;
return resolveFrame(newPath.head, newPath.tail.head, newPath, p);
}
case ARRAY_TYPE: {
p.location = p.location.prepend(0);
List<JCTree> newPath = path.tail;
return resolveFrame(newPath.head, newPath.tail.head, newPath, p);
}
case TYPE_PARAMETER:
if (path.tail.tail.head.getTag() == JCTree.CLASSDEF) {
JCClassDecl clazz = (JCClassDecl)path.tail.tail.head;
p.type = TargetType.CLASS_TYPE_PARAMETER_BOUND;
p.parameter_index = clazz.typarams.indexOf(path.tail.head);
p.bound_index = ((JCTypeParameter)frame).bounds.indexOf(tree);
} else if (path.tail.tail.head.getTag() == JCTree.METHODDEF) {
JCMethodDecl method = (JCMethodDecl)path.tail.tail.head;
p.type = TargetType.METHOD_TYPE_PARAMETER_BOUND;
p.parameter_index = method.typarams.indexOf(path.tail.head);
p.bound_index = ((JCTypeParameter)frame).bounds.indexOf(tree);
} else
throw new AssertionError();
p.pos = frame.pos;
return p;
case VARIABLE:
VarSymbol v = ((JCVariableDecl)frame).sym;
p.pos = frame.pos;
switch (v.getKind()) {
case LOCAL_VARIABLE:
p.type = TargetType.LOCAL_VARIABLE; break;
case FIELD:
p.type = TargetType.FIELD_GENERIC_OR_ARRAY; break;
case PARAMETER:
p.type = TargetType.METHOD_PARAMETER_GENERIC_OR_ARRAY;
p.parameter_index = methodParamIndex(path, frame);
break;
default: throw new AssertionError();
}
return p;
case ANNOTATED_TYPE: {
List<JCTree> newPath = path.tail;
return resolveFrame(newPath.head, newPath.tail.head,
newPath, p);
}
case METHOD_INVOCATION: {
JCMethodInvocation invocation = (JCMethodInvocation)frame;
if (!invocation.typeargs.contains(tree))
throw new AssertionError("{" + tree + "} is not an argument in the invocation: " + invocation);
p.type = TargetType.METHOD_TYPE_ARGUMENT;
p.pos = invocation.pos;
p.type_index = invocation.typeargs.indexOf(tree);
return p;
}
case EXTENDS_WILDCARD:
case SUPER_WILDCARD: {
p.type = TargetType.WILDCARD_BOUND;
List<JCTree> newPath = path.tail;
TypeAnnotationPosition wildcard =
resolveFrame(newPath.head, newPath.tail.head, newPath,
new TypeAnnotationPosition());
if (!wildcard.location.isEmpty())
wildcard.type = wildcard.type.getGenericComplement();
p.wildcard_position = wildcard;
p.pos = frame.pos;
return p;
}
}
return p;
}
@Override
public void visitApply(JCMethodInvocation tree) {
scan(tree.meth);
scan(tree.typeargs);
scan(tree.args);
}
private void setTypeAnnotationPos(List<JCTypeAnnotation> annotations, TypeAnnotationPosition position) {
for (JCTypeAnnotation anno : annotations) {
anno.annotation_position = position;
anno.attribute_field.position = position;
}
}
@Override
public void visitNewArray(JCNewArray tree) {
findPosition(tree, tree, tree.annotations);
int dimAnnosCount = tree.dimAnnotations.size();
// handle annotations associated with dimentions
for (int i = 0; i < dimAnnosCount; ++i) {
TypeAnnotationPosition p = new TypeAnnotationPosition();
p.type = TargetType.NEW_GENERIC_OR_ARRAY;
p.pos = tree.pos;
p.location = p.location.append(i);
setTypeAnnotationPos(tree.dimAnnotations.get(i), p);
}
// handle "free" annotations
int i = dimAnnosCount == 0 ? 0 : dimAnnosCount - 1;
JCExpression elemType = tree.elemtype;
while (elemType != null) {
if (elemType.getTag() == JCTree.ANNOTATED_TYPE) {
JCAnnotatedType at = (JCAnnotatedType)elemType;
TypeAnnotationPosition p = new TypeAnnotationPosition();
p.type = TargetType.NEW_GENERIC_OR_ARRAY;
p.pos = tree.pos;
p.location = p.location.append(i);
setTypeAnnotationPos(at.annotations, p);
elemType = at.underlyingType;
} else if (elemType.getTag() == JCTree.TYPEARRAY) {
++i;
elemType = ((JCArrayTypeTree)elemType).elemtype;
} else
break;
}
// find annotations locations of initializer elements
scan(tree.elems);
}
@Override
public void visitAnnotatedType(JCAnnotatedType tree) {
findPosition(tree, peek2(), tree.annotations);
super.visitAnnotatedType(tree);
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
TypeAnnotationPosition p = new TypeAnnotationPosition();
p.type = TargetType.METHOD_RECEIVER;
setTypeAnnotationPos(tree.receiverAnnotations, p);
super.visitMethodDef(tree);
}
@Override
public void visitTypeParameter(JCTypeParameter tree) {
findPosition(tree, peek2(), tree.annotations);
super.visitTypeParameter(tree);
}
void findPosition(JCTree tree, JCTree frame, List<JCTypeAnnotation> annotations) {
if (!annotations.isEmpty()) {
TypeAnnotationPosition p =
resolveFrame(tree, frame, frames.toList(),
new TypeAnnotationPosition());
if (!p.location.isEmpty())
p.type = p.type.getGenericComplement();
setTypeAnnotationPos(annotations, p);
if (debugJSR308) {
System.out.println("trans: " + tree);
System.out.println(" target: " + p);
}
}
}
private int methodParamIndex(List<JCTree> path, JCTree param) {
List<JCTree> curr = path;
if (curr.head != param)
curr = path.tail;
JCMethodDecl method = (JCMethodDecl)curr.tail.head;
return method.params.indexOf(param);
}
}
private class TypeAnnotationLift extends TreeScanner {
List<Attribute.TypeCompound> recordedTypeAnnotations = List.nil();
boolean isInner = false;
@Override
public void visitClassDef(JCClassDecl tree) {
if (isInner) {
// tree is an inner class tree. stop now.
// TransTypes.visitClassDef makes an invocation for each class
// seperately.
return;
}
isInner = true;
List<Attribute.TypeCompound> prevTAs = recordedTypeAnnotations;
recordedTypeAnnotations = List.nil();
try {
super.visitClassDef(tree);
} finally {
tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations);
recordedTypeAnnotations = prevTAs;
}
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
List<Attribute.TypeCompound> prevTAs = recordedTypeAnnotations;
recordedTypeAnnotations = List.nil();
try {
super.visitMethodDef(tree);
} finally {
tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations);
recordedTypeAnnotations = prevTAs;
}
}
@Override
public void visitVarDef(JCVariableDecl tree) {
List<Attribute.TypeCompound> prevTAs = recordedTypeAnnotations;
recordedTypeAnnotations = List.nil();
ElementKind kind = tree.sym.getKind();
if (kind == ElementKind.LOCAL_VARIABLE && tree.mods.annotations.nonEmpty()) {
// need to lift the annotations
TypeAnnotationPosition position = new TypeAnnotationPosition();
position.pos = tree.pos;
position.type = TargetType.LOCAL_VARIABLE;
for (Attribute.Compound attribute : tree.sym.attributes_field) {
Attribute.TypeCompound tc =
new Attribute.TypeCompound(attribute.type, attribute.values, position);
recordedTypeAnnotations = recordedTypeAnnotations.append(tc);
}
}
try {
super.visitVarDef(tree);
} finally {
if (kind.isField() || kind == ElementKind.LOCAL_VARIABLE)
tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations);
recordedTypeAnnotations = kind.isField() ? prevTAs : prevTAs.appendList(recordedTypeAnnotations);
}
}
@Override
public void visitApply(JCMethodInvocation tree) {
scan(tree.meth);
scan(tree.typeargs);
scan(tree.args);
}
public void visitAnnotation(JCAnnotation tree) {
if (tree instanceof JCTypeAnnotation)
recordedTypeAnnotations = recordedTypeAnnotations.append(((JCTypeAnnotation)tree).attribute_field);
super.visitAnnotation(tree);
}
}
} }

View file

@ -47,7 +47,6 @@ import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.file.BaseFileObject; import com.sun.tools.javac.file.BaseFileObject;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*; import static com.sun.tools.javac.code.Kinds.*;
@ -187,6 +186,10 @@ public class ClassReader implements Completer {
/** The minor version number of the class file being read. */ /** The minor version number of the class file being read. */
int minorVersion; int minorVersion;
/** Switch: debug output for JSR 308-related operations.
*/
boolean debugJSR308;
/** Get the ClassReader instance for this invocation. */ /** Get the ClassReader instance for this invocation. */
public static ClassReader instance(Context context) { public static ClassReader instance(Context context) {
ClassReader instance = context.get(classReaderKey); ClassReader instance = context.get(classReaderKey);
@ -256,6 +259,7 @@ public class ClassReader implements Completer {
: null; : null;
typevars = new Scope(syms.noSymbol); typevars = new Scope(syms.noSymbol);
debugJSR308 = options.get("TA:reader") != null;
initAttributeReaders(); initAttributeReaders();
} }
@ -303,6 +307,12 @@ public class ClassReader implements Completer {
return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF)); return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
} }
/** Read a byte.
*/
byte nextByte() {
return buf[bp++];
}
/** Read an integer. /** Read an integer.
*/ */
int nextInt() { int nextInt() {
@ -1060,7 +1070,21 @@ public class ClassReader implements Completer {
if (allowVarargs) if (allowVarargs)
sym.flags_field |= VARARGS; sym.flags_field |= VARARGS;
} }
} },
// v51 attributes
new AttributeReader(names.RuntimeVisibleTypeAnnotations, V51, CLASS_OR_MEMBER_ATTRIBUTE) {
void read(Symbol sym, int attrLen) {
attachTypeAnnotations(sym);
}
},
new AttributeReader(names.RuntimeInvisibleTypeAnnotations, V51, CLASS_OR_MEMBER_ATTRIBUTE) {
void read(Symbol sym, int attrLen) {
attachTypeAnnotations(sym);
}
},
// The following attributes for a Code attribute are not currently handled // The following attributes for a Code attribute are not currently handled
// StackMapTable // StackMapTable
@ -1268,6 +1292,17 @@ public class ClassReader implements Completer {
} }
} }
void attachTypeAnnotations(final Symbol sym) {
int numAttributes = nextChar();
if (numAttributes != 0) {
ListBuffer<TypeAnnotationProxy> proxies =
ListBuffer.lb();
for (int i = 0; i < numAttributes; i++)
proxies.append(readTypeAnnotation());
annotate.later(new TypeAnnotationCompleter(sym, proxies.toList()));
}
}
/** Attach the default value for an annotation element. /** Attach the default value for an annotation element.
*/ */
void attachAnnotationDefault(final Symbol sym) { void attachAnnotationDefault(final Symbol sym) {
@ -1304,6 +1339,121 @@ public class ClassReader implements Completer {
return new CompoundAnnotationProxy(t, pairs.toList()); return new CompoundAnnotationProxy(t, pairs.toList());
} }
TypeAnnotationProxy readTypeAnnotation() {
CompoundAnnotationProxy proxy = readCompoundAnnotation();
TypeAnnotationPosition position = readPosition();
if (debugJSR308)
System.out.println("TA: reading: " + proxy + " @ " + position
+ " in " + log.currentSourceFile());
return new TypeAnnotationProxy(proxy, position);
}
TypeAnnotationPosition readPosition() {
byte tag = nextByte();
if (!TargetType.isValidTargetTypeValue(tag))
throw this.badClassFile("bad.type.annotation.value", tag);
TypeAnnotationPosition position = new TypeAnnotationPosition();
TargetType type = TargetType.fromTargetTypeValue(tag);
position.type = type;
switch (type) {
// type case
case TYPECAST:
case TYPECAST_GENERIC_OR_ARRAY:
// object creation
case INSTANCEOF:
case INSTANCEOF_GENERIC_OR_ARRAY:
// new expression
case NEW:
case NEW_GENERIC_OR_ARRAY:
position.offset = nextChar();
break;
// local variable
case LOCAL_VARIABLE:
case LOCAL_VARIABLE_GENERIC_OR_ARRAY:
int table_length = nextChar();
position.lvarOffset = new int[table_length];
position.lvarLength = new int[table_length];
position.lvarIndex = new int[table_length];
for (int i = 0; i < table_length; ++i) {
position.lvarOffset[i] = nextChar();
position.lvarLength[i] = nextChar();
position.lvarIndex[i] = nextChar();
}
break;
// method receiver
case METHOD_RECEIVER:
// Do nothing
break;
// type parameters
case CLASS_TYPE_PARAMETER:
case METHOD_TYPE_PARAMETER:
position.parameter_index = nextByte();
break;
// type parameter bounds
case CLASS_TYPE_PARAMETER_BOUND:
case CLASS_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
case METHOD_TYPE_PARAMETER_BOUND:
case METHOD_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
position.parameter_index = nextByte();
position.bound_index = nextByte();
break;
// wildcard
case WILDCARD_BOUND:
case WILDCARD_BOUND_GENERIC_OR_ARRAY:
position.wildcard_position = readPosition();
break;
// Class extends and implements clauses
case CLASS_EXTENDS:
case CLASS_EXTENDS_GENERIC_OR_ARRAY:
position.type_index = nextByte();
break;
// throws
case THROWS:
position.type_index = nextByte();
break;
case CLASS_LITERAL:
case CLASS_LITERAL_GENERIC_OR_ARRAY:
position.offset = nextChar();
break;
// method parameter: not specified
case METHOD_PARAMETER_GENERIC_OR_ARRAY:
position.parameter_index = nextByte();
break;
// method type argument: wasn't specified
case NEW_TYPE_ARGUMENT:
case NEW_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
case METHOD_TYPE_ARGUMENT:
case METHOD_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
position.offset = nextChar();
position.type_index = nextByte();
break;
// We don't need to worry abut these
case METHOD_RETURN_GENERIC_OR_ARRAY:
case FIELD_GENERIC_OR_ARRAY:
break;
case UNKNOWN:
break;
default:
throw new AssertionError("unknown type: " + position);
}
if (type.hasLocation()) {
int len = nextChar();
ListBuffer<Integer> loc = ListBuffer.lb();
for (int i = 0; i < len; i++)
loc = loc.append((int)nextByte());
position.location = loc.toList();
}
return position;
}
Attribute readAttributeValue() { Attribute readAttributeValue() {
char c = (char) buf[bp++]; char c = (char) buf[bp++];
switch (c) { switch (c) {
@ -1408,6 +1558,18 @@ public class ClassReader implements Completer {
} }
} }
/** A temporary proxy representing a type annotation.
*/
static class TypeAnnotationProxy {
final CompoundAnnotationProxy compound;
final TypeAnnotationPosition position;
public TypeAnnotationProxy(CompoundAnnotationProxy compound,
TypeAnnotationPosition position) {
this.compound = compound;
this.position = position;
}
}
class AnnotationDeproxy implements ProxyVisitor { class AnnotationDeproxy implements ProxyVisitor {
private ClassSymbol requestingOwner = currentOwner.kind == MTH private ClassSymbol requestingOwner = currentOwner.kind == MTH
? currentOwner.enclClass() : (ClassSymbol)currentOwner; ? currentOwner.enclClass() : (ClassSymbol)currentOwner;
@ -1604,6 +1766,45 @@ public class ClassReader implements Completer {
} }
} }
class TypeAnnotationCompleter extends AnnotationCompleter {
List<TypeAnnotationProxy> proxies;
TypeAnnotationCompleter(Symbol sym,
List<TypeAnnotationProxy> proxies) {
super(sym, List.<CompoundAnnotationProxy>nil());
this.proxies = proxies;
}
List<Attribute.TypeCompound> deproxyTypeCompoundList(List<TypeAnnotationProxy> proxies) {
ListBuffer<Attribute.TypeCompound> buf = ListBuffer.lb();
for (TypeAnnotationProxy proxy: proxies) {
Attribute.Compound compound = deproxyCompound(proxy.compound);
Attribute.TypeCompound typeCompound = new Attribute.TypeCompound(compound, proxy.position);
buf.add(typeCompound);
}
return buf.toList();
}
@Override
public void enterAnnotation() {
JavaFileObject previousClassFile = currentClassFile;
try {
currentClassFile = classFile;
List<Attribute.TypeCompound> newList = deproxyTypeCompoundList(proxies);
if (debugJSR308)
System.out.println("TA: reading: adding " + newList
+ " to symbol " + sym + " in " + log.currentSourceFile());
sym.typeAnnotations = ((sym.typeAnnotations == null)
? newList
: newList.prependList(sym.typeAnnotations));
} finally {
currentClassFile = previousClassFile;
}
}
}
/************************************************************************ /************************************************************************
* Reading Symbols * Reading Symbols

View file

@ -37,7 +37,6 @@ import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
import static com.sun.tools.javac.code.BoundKind.*; import static com.sun.tools.javac.code.BoundKind.*;
import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.*;
@ -62,6 +61,10 @@ public class ClassWriter extends ClassFile {
private final Options options; private final Options options;
/** Switch: debugging output for JSR 308-related operations.
*/
private boolean debugJSR308;
/** Switch: verbose output. /** Switch: verbose output.
*/ */
private boolean verbose; private boolean verbose;
@ -173,6 +176,7 @@ public class ClassWriter extends ClassFile {
types = Types.instance(context); types = Types.instance(context);
fileManager = context.get(JavaFileManager.class); fileManager = context.get(JavaFileManager.class);
debugJSR308 = options.get("TA:writer") != null;
verbose = options.get("-verbose") != null; verbose = options.get("-verbose") != null;
scramble = options.get("-scramble") != null; scramble = options.get("-scramble") != null;
scrambleAll = options.get("-scrambleAll") != null; scrambleAll = options.get("-scrambleAll") != null;
@ -668,6 +672,7 @@ public class ClassWriter extends ClassFile {
acount++; acount++;
} }
acount += writeJavaAnnotations(sym.getAnnotationMirrors()); acount += writeJavaAnnotations(sym.getAnnotationMirrors());
acount += writeTypeAnnotations(sym.typeAnnotations);
return acount; return acount;
} }
@ -762,6 +767,43 @@ public class ClassWriter extends ClassFile {
return attrCount; return attrCount;
} }
int writeTypeAnnotations(List<Attribute.TypeCompound> typeAnnos) {
if (typeAnnos.isEmpty()) return 0;
ListBuffer<Attribute.TypeCompound> visibles = ListBuffer.lb();
ListBuffer<Attribute.TypeCompound> invisibles = ListBuffer.lb();
for (Attribute.TypeCompound tc : typeAnnos) {
switch (getRetention(tc.type.tsym)) {
case SOURCE: break;
case CLASS: invisibles.append(tc); break;
case RUNTIME: visibles.append(tc); break;
default: ;// /* fail soft */ throw new AssertionError(vis);
}
}
int attrCount = 0;
if (visibles.length() != 0) {
int attrIndex = writeAttr(names.RuntimeVisibleTypeAnnotations);
databuf.appendChar(visibles.length());
for (Attribute.TypeCompound p : visibles)
writeTypeAnnotation(p);
endAttr(attrIndex);
attrCount++;
}
if (invisibles.length() != 0) {
int attrIndex = writeAttr(names.RuntimeInvisibleTypeAnnotations);
databuf.appendChar(invisibles.length());
for (Attribute.TypeCompound p : invisibles)
writeTypeAnnotation(p);
endAttr(attrIndex);
attrCount++;
}
return attrCount;
}
/** A mirror of java.lang.annotation.RetentionPolicy. */ /** A mirror of java.lang.annotation.RetentionPolicy. */
enum RetentionPolicy { enum RetentionPolicy {
SOURCE, SOURCE,
@ -862,6 +904,104 @@ public class ClassWriter extends ClassFile {
} }
} }
void writeTypeAnnotation(Attribute.TypeCompound c) {
// ignore UNKNOWN attributes - improve testing
if (debugJSR308)
System.out.println("TA: writing " + c + " at " + c.position
+ " in " + log.currentSourceFile());
writeCompoundAttribute(c);
writePosition(c.position);
}
void writePosition(TypeAnnotationPosition p) {
databuf.appendByte(p.type.targetTypeValue());
switch (p.type) {
// type case
case TYPECAST:
case TYPECAST_GENERIC_OR_ARRAY:
// object creation
case INSTANCEOF:
case INSTANCEOF_GENERIC_OR_ARRAY:
// new expression
case NEW:
case NEW_GENERIC_OR_ARRAY:
databuf.appendChar(p.offset);
break;
// local variable
case LOCAL_VARIABLE:
case LOCAL_VARIABLE_GENERIC_OR_ARRAY:
databuf.appendChar(p.lvarOffset.length); // for table length
for (int i = 0; i < p.lvarOffset.length; ++i) {
databuf.appendChar(p.lvarOffset[i]);
databuf.appendChar(p.lvarLength[i]);
databuf.appendChar(p.lvarIndex[i]);
}
break;
// method receiver
case METHOD_RECEIVER:
// Do nothing
break;
// type parameters
case CLASS_TYPE_PARAMETER:
case METHOD_TYPE_PARAMETER:
databuf.appendByte(p.parameter_index);
break;
// type parameters bounds
case CLASS_TYPE_PARAMETER_BOUND:
case CLASS_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
case METHOD_TYPE_PARAMETER_BOUND:
case METHOD_TYPE_PARAMETER_BOUND_GENERIC_OR_ARRAY:
databuf.appendByte(p.parameter_index);
databuf.appendByte(p.bound_index);
break;
// wildcards
case WILDCARD_BOUND:
case WILDCARD_BOUND_GENERIC_OR_ARRAY:
writePosition(p.wildcard_position);
break;
// Class extends and implements clauses
case CLASS_EXTENDS:
case CLASS_EXTENDS_GENERIC_OR_ARRAY:
databuf.appendByte(p.type_index);
break;
// throws
case THROWS:
databuf.appendByte(p.type_index);
break;
case CLASS_LITERAL:
case CLASS_LITERAL_GENERIC_OR_ARRAY:
databuf.appendChar(p.offset);
break;
// method parameter: not specified
case METHOD_PARAMETER_GENERIC_OR_ARRAY:
databuf.appendByte(p.parameter_index);
break;
// method type argument: wasn't specified
case NEW_TYPE_ARGUMENT:
case NEW_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
case METHOD_TYPE_ARGUMENT:
case METHOD_TYPE_ARGUMENT_GENERIC_OR_ARRAY:
databuf.appendChar(p.offset);
databuf.appendByte(p.type_index);
break;
// We don't need to worry abut these
case METHOD_RETURN_GENERIC_OR_ARRAY:
case FIELD_GENERIC_OR_ARRAY:
break;
case UNKNOWN:
break;
default:
throw new AssertionError("unknown position: " + p);
}
// Append location data for generics/arrays.
if (p.type.hasLocation()) {
databuf.appendChar(p.location.size());
for (int i : p.location)
databuf.appendByte((byte)i);
}
}
/********************************************************************** /**********************************************************************
* Writing Objects * Writing Objects
**********************************************************************/ **********************************************************************/
@ -1569,6 +1709,7 @@ public class ClassWriter extends ClassFile {
acount += writeFlagAttrs(c.flags()); acount += writeFlagAttrs(c.flags());
acount += writeJavaAnnotations(c.getAnnotationMirrors()); acount += writeJavaAnnotations(c.getAnnotationMirrors());
acount += writeTypeAnnotations(c.typeAnnotations);
acount += writeEnclosingMethodAttribute(c); acount += writeEnclosingMethodAttribute(c);
poolbuf.appendInt(JAVA_MAGIC); poolbuf.appendInt(JAVA_MAGIC);

View file

@ -1913,11 +1913,27 @@ public class Code {
v.length = length; v.length = length;
putVar(v); putVar(v);
} }
fillLocalVarPosition(v);
} }
} }
state.defined.excl(adr); state.defined.excl(adr);
} }
private void fillLocalVarPosition(LocalVar lv) {
if (lv == null || lv.sym == null
|| lv.sym.typeAnnotations == null)
return;
for (Attribute.TypeCompound ta : lv.sym.typeAnnotations) {
TypeAnnotationPosition p = ta.position;
while (p != null) {
p.lvarOffset[0] = (int)lv.start_pc;
p.lvarLength[0] = (int)lv.length;
p.lvarIndex[0] = (int)lv.reg;
p = p.wildcard_position;
}
}
}
/** Put a live variable range into the buffer to be output to the /** Put a live variable range into the buffer to be output to the
* class file. * class file.
*/ */

View file

@ -26,6 +26,8 @@
package com.sun.tools.javac.jvm; package com.sun.tools.javac.jvm;
import java.util.*; import java.util.*;
import javax.lang.model.element.ElementKind;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.List;
@ -939,7 +941,6 @@ public class Gen extends JCTree.Visitor {
startpcCrt, startpcCrt,
code.curPc()); code.curPc());
// End the scope of all local variables in variable info.
code.endScopes(0); code.endScopes(0);
// If we exceeded limits, panic // If we exceeded limits, panic
@ -1439,7 +1440,6 @@ public class Gen extends JCTree.Visitor {
// Resolve all breaks. // Resolve all breaks.
code.resolve(exitChain); code.resolve(exitChain);
// End the scopes of all try-local variables in variable info.
code.endScopes(limit); code.endScopes(limit);
} }
@ -1672,6 +1672,7 @@ public class Gen extends JCTree.Visitor {
*************************************************************************/ *************************************************************************/
public void visitApply(JCMethodInvocation tree) { public void visitApply(JCMethodInvocation tree) {
setTypeAnnotationPositions(tree.pos);
// Generate code for method. // Generate code for method.
Item m = genExpr(tree.meth, methodType); Item m = genExpr(tree.meth, methodType);
// Generate code for all arguments, where the expected types are // Generate code for all arguments, where the expected types are
@ -1707,10 +1708,45 @@ public class Gen extends JCTree.Visitor {
result = items.makeStackItem(pt); result = items.makeStackItem(pt);
} }
private void setTypeAnnotationPositions(int treePos) {
MethodSymbol meth = code.meth;
for (Attribute.TypeCompound ta : meth.typeAnnotations) {
if (ta.position.pos == treePos) {
ta.position.offset = code.cp;
ta.position.lvarOffset[0] = code.cp;
}
}
if (code.meth.getKind() != ElementKind.CONSTRUCTOR
&& code.meth.getKind() != ElementKind.STATIC_INIT)
return;
for (Attribute.TypeCompound ta : meth.owner.typeAnnotations) {
if (ta.position.pos == treePos) {
ta.position.offset = code.cp;
ta.position.lvarOffset[0] = code.cp;
}
}
ClassSymbol clazz = meth.enclClass();
for (Symbol s : new com.sun.tools.javac.model.FilteredMemberList(clazz.members())) {
if (!s.getKind().isField())
continue;
for (Attribute.TypeCompound ta : s.typeAnnotations) {
if (ta.position.pos == treePos) {
ta.position.offset = code.cp;
ta.position.lvarOffset[0] = code.cp;
}
}
}
}
public void visitNewClass(JCNewClass tree) { public void visitNewClass(JCNewClass tree) {
// Enclosing instances or anonymous classes should have been eliminated // Enclosing instances or anonymous classes should have been eliminated
// by now. // by now.
assert tree.encl == null && tree.def == null; assert tree.encl == null && tree.def == null;
setTypeAnnotationPositions(tree.pos);
code.emitop2(new_, makeRef(tree.pos(), tree.type)); code.emitop2(new_, makeRef(tree.pos(), tree.type));
code.emitop0(dup); code.emitop0(dup);
@ -1725,6 +1761,8 @@ public class Gen extends JCTree.Visitor {
} }
public void visitNewArray(JCNewArray tree) { public void visitNewArray(JCNewArray tree) {
setTypeAnnotationPositions(tree.pos);
if (tree.elems != null) { if (tree.elems != null) {
Type elemtype = types.elemtype(tree.type); Type elemtype = types.elemtype(tree.type);
loadIntConst(tree.elems.length()); loadIntConst(tree.elems.length());
@ -2053,6 +2091,7 @@ public class Gen extends JCTree.Visitor {
} }
public void visitTypeCast(JCTypeCast tree) { public void visitTypeCast(JCTypeCast tree) {
setTypeAnnotationPositions(tree.pos);
result = genExpr(tree.expr, tree.clazz.type).load(); result = genExpr(tree.expr, tree.clazz.type).load();
// Additional code is only needed if we cast to a reference type // Additional code is only needed if we cast to a reference type
// which is not statically a supertype of the expression's type. // which is not statically a supertype of the expression's type.
@ -2069,6 +2108,8 @@ public class Gen extends JCTree.Visitor {
} }
public void visitTypeTest(JCInstanceOf tree) { public void visitTypeTest(JCInstanceOf tree) {
setTypeAnnotationPositions(tree.pos);
genExpr(tree.expr, tree.expr.type).load(); genExpr(tree.expr, tree.expr.type).load();
code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type));
result = items.makeStackItem(syms.booleanType); result = items.makeStackItem(syms.booleanType);
@ -2110,6 +2151,7 @@ public class Gen extends JCTree.Visitor {
if (tree.name == names._class) { if (tree.name == names._class) {
assert target.hasClassLiterals(); assert target.hasClassLiterals();
setTypeAnnotationPositions(tree.pos);
code.emitop2(ldc2, makeRef(tree.pos(), tree.selected.type)); code.emitop2(ldc2, makeRef(tree.pos(), tree.selected.type));
result = items.makeStackItem(pt); result = items.makeStackItem(pt);
return; return;

View file

@ -75,6 +75,42 @@ public class JavacParser implements Parser {
/** The name table. */ /** The name table. */
private Names names; private Names names;
// Because of javac's limited lookahead, some contexts are ambiguous in
// the presence of type annotations even though they are not ambiguous
// in the absence of type annotations. Consider this code:
// void m(String [] m) { }
// void m(String ... m) { }
// After parsing "String", javac calls bracketsOpt which immediately
// returns if the next character is not '['. Similarly, javac can see
// if the next token is ... and in that case parse an ellipsis. But in
// the presence of type annotations:
// void m(String @A [] m) { }
// void m(String @A ... m) { }
// no finite lookahead is enough to determine whether to read array
// levels or an ellipsis. Furthermore, if you call bracketsOpt, then
// bracketsOpt first reads all the leading annotations and only then
// discovers that it needs to fail. bracketsOpt needs a way to push
// back the extra annotations that it read. (But, bracketsOpt should
// not *always* be allowed to push back extra annotations that it finds
// -- in most contexts, any such extra annotation is an error.
// Another similar case occurs with arrays and receiver annotations:
// String b() @Array [] @Receiver { }
// String b() @Receiver { }
//
// The following two variables permit type annotations that have
// already been read to be stored for later use. Alternate
// implementations are possible but would cause much larger changes to
// the parser.
/** Type annotations that have already been read but have not yet been used. **/
private List<JCTypeAnnotation> typeAnnotationsPushedBack = null;
/**
* If the parser notices extra annotations, then it either immediately
* issues an error (if this variable is false) or places the extra
* annotations in variable typeAnnotationsPushedBack (if this variable
* is true).
*/
private boolean permitTypeAnnotationsPushBack = false;
/** Construct a parser from a given scanner, tree factory and log. /** Construct a parser from a given scanner, tree factory and log.
*/ */
protected JavacParser(ParserFactory fac, protected JavacParser(ParserFactory fac,
@ -95,13 +131,19 @@ public class JavacParser implements Parser {
this.allowForeach = source.allowForeach(); this.allowForeach = source.allowForeach();
this.allowStaticImport = source.allowStaticImport(); this.allowStaticImport = source.allowStaticImport();
this.allowAnnotations = source.allowAnnotations(); this.allowAnnotations = source.allowAnnotations();
this.allowTypeAnnotations = source.allowTypeAnnotations();
this.keepDocComments = keepDocComments; this.keepDocComments = keepDocComments;
if (keepDocComments) if (keepDocComments)
docComments = new HashMap<JCTree,String>(); docComments = new HashMap<JCTree,String>();
this.keepLineMap = keepLineMap; this.keepLineMap = keepLineMap;
this.errorTree = F.Erroneous(); this.errorTree = F.Erroneous();
this.debugJSR308 = fac.options.get("TA:parser") != null;
} }
/** Switch: debug output for type-annotations operations
*/
boolean debugJSR308;
/** Switch: Should generics be recognized? /** Switch: Should generics be recognized?
*/ */
boolean allowGenerics; boolean allowGenerics;
@ -130,6 +172,10 @@ public class JavacParser implements Parser {
*/ */
boolean allowAnnotations; boolean allowAnnotations;
/** Switch: should we recognize type annotations?
*/
boolean allowTypeAnnotations;
/** Switch: should we keep docComments? /** Switch: should we keep docComments?
*/ */
boolean keepDocComments; boolean keepDocComments;
@ -558,7 +604,33 @@ public class JavacParser implements Parser {
return term(EXPR); return term(EXPR);
} }
/**
* parses (optional) type annotations followed by a type. If the
* annotations are present before the type and are not consumed during array
* parsing, this method returns a {@link JCAnnotatedType} consisting of
* these annotations and the underlying type. Otherwise, it returns the
* underlying type.
*
* <p>
*
* Note that this method sets {@code mode} to {@code TYPE} first, before
* parsing annotations.
*/
public JCExpression parseType() { public JCExpression parseType() {
List<JCTypeAnnotation> annotations = typeAnnotationsOpt();
return parseType(annotations);
}
public JCExpression parseType(List<JCTypeAnnotation> annotations) {
JCExpression result = unannotatedType();
if (!annotations.isEmpty())
result = F.AnnotatedType(annotations, result);
return result;
}
public JCExpression unannotatedType() {
return term(TYPE); return term(TYPE);
} }
@ -792,8 +864,8 @@ public class JavacParser implements Parser {
* | [TypeArguments] THIS [Arguments] * | [TypeArguments] THIS [Arguments]
* | [TypeArguments] SUPER SuperSuffix * | [TypeArguments] SUPER SuperSuffix
* | NEW [TypeArguments] Creator * | NEW [TypeArguments] Creator
* | Ident { "." Ident } * | [Annotations] Ident { "." Ident }
* [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" ) * [ [Annotations] "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
* | Arguments * | Arguments
* | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator ) * | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator )
* ] * ]
@ -942,23 +1014,62 @@ public class JavacParser implements Parser {
typeArgs = null; typeArgs = null;
} else return illegal(); } else return illegal();
break; break;
case MONKEYS_AT:
// only annotated targetting class literals or cast types are valid
List<JCTypeAnnotation> typeAnnos = typeAnnotationsOpt();
if (typeAnnos.isEmpty()) {
// else there would be no '@'
throw new AssertionError("type annos is empty");
}
JCExpression expr = term3();
// Type annotations: If term3 just parsed a non-type, expect a
// class literal (and issue a syntax error if there is no class
// literal). Otherwise, create a JCAnnotatedType.
if ((mode & TYPE) == 0) {
if (expr.getTag() != JCTree.SELECT)
return illegal(typeAnnos.head.pos);
JCFieldAccess sel = (JCFieldAccess)expr;
if (sel.name != names._class)
return illegal();
else {
sel.selected = F.AnnotatedType(typeAnnos, sel.selected);
t = expr;
}
} else {
// type annotation targeting a cast
t = toP(F.at(S.pos()).AnnotatedType(typeAnnos, expr));
}
break;
case IDENTIFIER: case ASSERT: case ENUM: case IDENTIFIER: case ASSERT: case ENUM:
if (typeArgs != null) return illegal(); if (typeArgs != null) return illegal();
t = toP(F.at(S.pos()).Ident(ident())); t = toP(F.at(S.pos()).Ident(ident()));
loop: while (true) { loop: while (true) {
pos = S.pos(); pos = S.pos();
final List<JCTypeAnnotation> annos = typeAnnotationsOpt();
// need to report an error later if LBRACKET is for array
// index access rather than array creation level
if (!annos.isEmpty() && S.token() != LBRACKET && S.token() != ELLIPSIS)
return illegal(annos.head.pos);
switch (S.token()) { switch (S.token()) {
case LBRACKET: case LBRACKET:
S.nextToken(); S.nextToken();
if (S.token() == RBRACKET) { if (S.token() == RBRACKET) {
S.nextToken(); S.nextToken();
t = bracketsOpt(t);
t = bracketsOpt(t, annos);
t = toP(F.at(pos).TypeArray(t)); t = toP(F.at(pos).TypeArray(t));
t = bracketsSuffix(t); t = bracketsSuffix(t);
} else { } else {
if ((mode & EXPR) != 0) { if ((mode & EXPR) != 0) {
mode = EXPR; mode = EXPR;
JCExpression t1 = term(); JCExpression t1 = term();
if (!annos.isEmpty()) t = illegal(annos.head.pos);
t = to(F.at(pos).Indexed(t, t1)); t = to(F.at(pos).Indexed(t, t1));
} }
accept(RBRACKET); accept(RBRACKET);
@ -1011,6 +1122,10 @@ public class JavacParser implements Parser {
// typeArgs saved for next loop iteration. // typeArgs saved for next loop iteration.
t = toP(F.at(pos).Select(t, ident())); t = toP(F.at(pos).Select(t, ident()));
break; break;
case ELLIPSIS:
assert this.permitTypeAnnotationsPushBack;
typeAnnotationsPushedBack = annos;
break loop;
default: default:
break loop; break loop;
} }
@ -1049,14 +1164,18 @@ public class JavacParser implements Parser {
if (typeArgs != null) illegal(); if (typeArgs != null) illegal();
while (true) { while (true) {
int pos1 = S.pos(); int pos1 = S.pos();
final List<JCTypeAnnotation> annos = typeAnnotationsOpt();
if (S.token() == LBRACKET) { if (S.token() == LBRACKET) {
S.nextToken(); S.nextToken();
if ((mode & TYPE) != 0) { if ((mode & TYPE) != 0) {
int oldmode = mode; int oldmode = mode;
mode = TYPE; mode = TYPE;
if (S.token() == RBRACKET) { if (S.token() == RBRACKET) {
S.nextToken(); S.nextToken();
t = bracketsOpt(t); t = bracketsOpt(t, annos);
t = toP(F.at(pos1).TypeArray(t)); t = toP(F.at(pos1).TypeArray(t));
return t; return t;
} }
@ -1091,6 +1210,13 @@ public class JavacParser implements Parser {
typeArgs = null; typeArgs = null;
} }
} else { } else {
if (!annos.isEmpty()) {
illegal(0);
if (permitTypeAnnotationsPushBack)
typeAnnotationsPushedBack = annos;
else
return illegal(annos.head.pos);
}
break; break;
} }
} }
@ -1100,6 +1226,7 @@ public class JavacParser implements Parser {
S.token() == PLUSPLUS ? JCTree.POSTINC : JCTree.POSTDEC, t)); S.token() == PLUSPLUS ? JCTree.POSTINC : JCTree.POSTDEC, t));
S.nextToken(); S.nextToken();
} }
return toP(t); return toP(t);
} }
@ -1232,22 +1359,24 @@ public class JavacParser implements Parser {
} }
/** TypeArgument = Type /** TypeArgument = Type
* | "?" * | [Annotations] "?"
* | "?" EXTENDS Type {"&" Type} * | [Annotations] "?" EXTENDS Type {"&" Type}
* | "?" SUPER Type * | [Annotations] "?" SUPER Type
*/ */
JCExpression typeArgument() { JCExpression typeArgument() {
if (S.token() != QUES) return parseType(); List<JCTypeAnnotation> annotations = typeAnnotationsOpt();
if (S.token() != QUES) return parseType(annotations);
int pos = S.pos(); int pos = S.pos();
S.nextToken(); S.nextToken();
JCExpression result;
if (S.token() == EXTENDS) { if (S.token() == EXTENDS) {
TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.EXTENDS)); TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.EXTENDS));
S.nextToken(); S.nextToken();
return F.at(pos).Wildcard(t, parseType()); result = F.at(pos).Wildcard(t, parseType());
} else if (S.token() == SUPER) { } else if (S.token() == SUPER) {
TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.SUPER)); TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.SUPER));
S.nextToken(); S.nextToken();
return F.at(pos).Wildcard(t, parseType()); result = F.at(pos).Wildcard(t, parseType());
} else if (S.token() == IDENTIFIER) { } else if (S.token() == IDENTIFIER) {
//error recovery //error recovery
reportSyntaxError(S.prevEndPos(), "expected3", reportSyntaxError(S.prevEndPos(), "expected3",
@ -1255,11 +1384,14 @@ public class JavacParser implements Parser {
TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND); TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
JCExpression wc = toP(F.at(pos).Wildcard(t, null)); JCExpression wc = toP(F.at(pos).Wildcard(t, null));
JCIdent id = toP(F.at(S.pos()).Ident(ident())); JCIdent id = toP(F.at(S.pos()).Ident(ident()));
return F.at(pos).Erroneous(List.<JCTree>of(wc, id)); result = F.at(pos).Erroneous(List.<JCTree>of(wc, id));
} else { } else {
TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND); TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
return toP(F.at(pos).Wildcard(t, null)); result = toP(F.at(pos).Wildcard(t, null));
} }
if (!annotations.isEmpty())
result = toP(F.at(annotations.head.pos).AnnotatedType(annotations,result));
return result;
} }
JCTypeApply typeArguments(JCExpression t) { JCTypeApply typeArguments(JCExpression t) {
@ -1268,21 +1400,47 @@ public class JavacParser implements Parser {
return toP(F.at(pos).TypeApply(t, args)); return toP(F.at(pos).TypeApply(t, args));
} }
/** BracketsOpt = {"[" "]"} /**
* BracketsOpt = { [Annotations] "[" "]" }
*
* <p>
*
* <code>annotations</code> is the list of annotations targeting
* the expression <code>t</code>.
*/ */
private JCExpression bracketsOpt(JCExpression t) { private JCExpression bracketsOpt(JCExpression t,
List<JCTypeAnnotation> annotations) {
List<JCTypeAnnotation> nextLevelAnnotations = typeAnnotationsOpt();
if (S.token() == LBRACKET) { if (S.token() == LBRACKET) {
int pos = S.pos(); int pos = S.pos();
S.nextToken(); S.nextToken();
t = bracketsOptCont(t, pos);
F.at(pos); JCExpression orig = t;
t = bracketsOptCont(t, pos, nextLevelAnnotations);
} else if (!nextLevelAnnotations.isEmpty()) {
if (permitTypeAnnotationsPushBack) {
this.typeAnnotationsPushedBack = nextLevelAnnotations;
} else
return illegal(nextLevelAnnotations.head.pos);
} }
int apos = S.pos();
if (!annotations.isEmpty())
t = F.at(apos).AnnotatedType(annotations, t);
return t; return t;
} }
private JCArrayTypeTree bracketsOptCont(JCExpression t, int pos) { /** BracketsOpt = {"[" TypeAnnotations "]"}
*/
private JCExpression bracketsOpt(JCExpression t) {
return bracketsOpt(t, List.<JCTypeAnnotation>nil());
}
private JCArrayTypeTree bracketsOptCont(JCExpression t, int pos,
List<JCTypeAnnotation> annotations) {
accept(RBRACKET); accept(RBRACKET);
t = bracketsOpt(t); t = bracketsOpt(t, annotations);
return toP(F.at(pos).TypeArray(t)); return toP(F.at(pos).TypeArray(t));
} }
@ -1316,18 +1474,29 @@ public class JavacParser implements Parser {
return t; return t;
} }
/** Creator = Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest ) /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
*/ */
JCExpression creator(int newpos, List<JCExpression> typeArgs) { JCExpression creator(int newpos, List<JCExpression> typeArgs) {
List<JCTypeAnnotation> newAnnotations = typeAnnotationsOpt();
switch (S.token()) { switch (S.token()) {
case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT: case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
case DOUBLE: case BOOLEAN: case DOUBLE: case BOOLEAN:
if (typeArgs == null) if (typeArgs == null) {
return arrayCreatorRest(newpos, basicType()); if (newAnnotations.isEmpty())
return arrayCreatorRest(newpos, basicType());
else
return arrayCreatorRest(newpos, F.AnnotatedType(newAnnotations, basicType()));
}
break; break;
default: default:
} }
JCExpression t = qualident(); JCExpression t = qualident();
// handle type annotations for non primitive arrays
if (!newAnnotations.isEmpty())
t = F.AnnotatedType(newAnnotations, t);
int oldmode = mode; int oldmode = mode;
mode = TYPE; mode = TYPE;
if (S.token() == LT) { if (S.token() == LT) {
@ -1344,7 +1513,7 @@ public class JavacParser implements Parser {
} }
} }
mode = oldmode; mode = oldmode;
if (S.token() == LBRACKET) { if (S.token() == LBRACKET || S.token() == MONKEYS_AT) {
JCExpression e = arrayCreatorRest(newpos, t); JCExpression e = arrayCreatorRest(newpos, t);
if (typeArgs != null) { if (typeArgs != null) {
int pos = newpos; int pos = newpos;
@ -1360,7 +1529,12 @@ public class JavacParser implements Parser {
} }
return e; return e;
} else if (S.token() == LPAREN) { } else if (S.token() == LPAREN) {
return classCreatorRest(newpos, null, typeArgs, t); JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t);
if (newClass.def != null) {
assert newClass.def.mods.annotations.isEmpty();
newClass.def.mods.annotations = List.convert(JCAnnotation.class, newAnnotations);
}
return newClass;
} else { } else {
reportSyntaxError(S.pos(), "expected2", reportSyntaxError(S.pos(), "expected2",
LPAREN, LBRACKET); LPAREN, LBRACKET);
@ -1380,40 +1554,73 @@ public class JavacParser implements Parser {
return classCreatorRest(newpos, encl, typeArgs, t); return classCreatorRest(newpos, encl, typeArgs, t);
} }
/** ArrayCreatorRest = "[" ( "]" BracketsOpt ArrayInitializer /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
* | Expression "]" {"[" Expression "]"} BracketsOpt ) * | Expression "]" {[Annotations] "[" Expression "]"} BracketsOpt )
*/ */
JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) { JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) {
List<JCTypeAnnotation> topAnnos = List.nil();
if (elemtype.getTag() == JCTree.ANNOTATED_TYPE) {
JCAnnotatedType atype = (JCAnnotatedType) elemtype;
topAnnos = atype.annotations;
elemtype = atype.underlyingType;
}
List<JCTypeAnnotation> annos = typeAnnotationsOpt();
accept(LBRACKET); accept(LBRACKET);
if (S.token() == RBRACKET) { if (S.token() == RBRACKET) {
accept(RBRACKET); accept(RBRACKET);
elemtype = bracketsOpt(elemtype);
elemtype = bracketsOpt(elemtype, annos);
if (S.token() == LBRACE) { if (S.token() == LBRACE) {
return arrayInitializer(newpos, elemtype); JCNewArray na = (JCNewArray)arrayInitializer(newpos, elemtype);
na.annotations = topAnnos;
return na;
} else { } else {
return syntaxError(S.pos(), "array.dimension.missing"); return syntaxError(S.pos(), "array.dimension.missing");
} }
} else { } else {
ListBuffer<JCExpression> dims = new ListBuffer<JCExpression>(); ListBuffer<JCExpression> dims = new ListBuffer<JCExpression>();
// maintain array dimension type annotations
ListBuffer<List<JCTypeAnnotation>> dimAnnotations = ListBuffer.lb();
dimAnnotations.append(annos);
dims.append(parseExpression()); dims.append(parseExpression());
accept(RBRACKET); accept(RBRACKET);
while (S.token() == LBRACKET) { while (S.token() == LBRACKET
|| (S.token() == MONKEYS_AT)) {
List<JCTypeAnnotation> maybeDimAnnos = typeAnnotationsOpt();
int pos = S.pos(); int pos = S.pos();
S.nextToken(); S.nextToken();
if (S.token() == RBRACKET) { if (S.token() == RBRACKET) {
elemtype = bracketsOptCont(elemtype, pos); elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos);
} else { } else {
dims.append(parseExpression()); if (S.token() == RBRACKET) { // no dimension
accept(RBRACKET); elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos);
} else {
dimAnnotations.append(maybeDimAnnos);
dims.append(parseExpression());
accept(RBRACKET);
}
} }
} }
return toP(F.at(newpos).NewArray(elemtype, dims.toList(), null));
JCNewArray na = toP(F.at(newpos).NewArray(elemtype, dims.toList(), null));
na.annotations = topAnnos;
na.dimAnnotations = dimAnnotations.toList();
return na;
} }
} }
/** ClassCreatorRest = Arguments [ClassBody] /** ClassCreatorRest = Arguments [ClassBody]
*/ */
JCExpression classCreatorRest(int newpos, JCNewClass classCreatorRest(int newpos,
JCExpression encl, JCExpression encl,
List<JCExpression> typeArgs, List<JCExpression> typeArgs,
JCExpression t) JCExpression t)
@ -1860,17 +2067,32 @@ public class JavacParser implements Parser {
new ListBuffer<JCExpressionStatement>()).toList(); new ListBuffer<JCExpressionStatement>()).toList();
} }
enum AnnotationKind { DEFAULT_ANNO, TYPE_ANNO };
/** AnnotationsOpt = { '@' Annotation } /** AnnotationsOpt = { '@' Annotation }
*/ */
List<JCAnnotation> annotationsOpt() { List<JCAnnotation> annotationsOpt(AnnotationKind kind) {
if (S.token() != MONKEYS_AT) return List.nil(); // optimization if (S.token() != MONKEYS_AT) return List.nil(); // optimization
ListBuffer<JCAnnotation> buf = new ListBuffer<JCAnnotation>(); ListBuffer<JCAnnotation> buf = new ListBuffer<JCAnnotation>();
int prevmode = mode;
while (S.token() == MONKEYS_AT) { while (S.token() == MONKEYS_AT) {
int pos = S.pos(); int pos = S.pos();
S.nextToken(); S.nextToken();
buf.append(annotation(pos)); buf.append(annotation(pos, kind));
} }
return buf.toList(); lastmode = mode;
mode = prevmode;
List<JCAnnotation> annotations = buf.toList();
if (debugJSR308 && kind == AnnotationKind.TYPE_ANNO)
System.out.println("TA: parsing " + annotations
+ " in " + log.currentSourceFile());
return annotations;
}
List<JCTypeAnnotation> typeAnnotationsOpt() {
List<JCAnnotation> annotations = annotationsOpt(AnnotationKind.TYPE_ANNO);
return List.convert(JCTypeAnnotation.class, annotations);
} }
/** ModifiersOpt = { Modifier } /** ModifiersOpt = { Modifier }
@ -1915,7 +2137,7 @@ public class JavacParser implements Parser {
if (flag == Flags.ANNOTATION) { if (flag == Flags.ANNOTATION) {
checkAnnotations(); checkAnnotations();
if (S.token() != INTERFACE) { if (S.token() != INTERFACE) {
JCAnnotation ann = annotation(lastPos); JCAnnotation ann = annotation(lastPos, AnnotationKind.DEFAULT_ANNO);
// if first modifier is an annotation, set pos to annotation's. // if first modifier is an annotation, set pos to annotation's.
if (flags == 0 && annotations.isEmpty()) if (flags == 0 && annotations.isEmpty())
pos = ann.pos; pos = ann.pos;
@ -1946,12 +2168,18 @@ public class JavacParser implements Parser {
/** Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] /** Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ]
* @param pos position of "@" token * @param pos position of "@" token
*/ */
JCAnnotation annotation(int pos) { JCAnnotation annotation(int pos, AnnotationKind kind) {
// accept(AT); // AT consumed by caller // accept(AT); // AT consumed by caller
checkAnnotations(); checkAnnotations();
if (kind == AnnotationKind.TYPE_ANNO)
checkTypeAnnotations();
JCTree ident = qualident(); JCTree ident = qualident();
List<JCExpression> fieldValues = annotationFieldValuesOpt(); List<JCExpression> fieldValues = annotationFieldValuesOpt();
JCAnnotation ann = F.at(pos).Annotation(ident, fieldValues); JCAnnotation ann;
if (kind == AnnotationKind.DEFAULT_ANNO)
ann = F.at(pos).Annotation(ident, fieldValues);
else
ann = F.at(pos).TypeAnnotation(ident, fieldValues);
storeEnd(ann, S.prevEndPos()); storeEnd(ann, S.prevEndPos());
return ann; return ann;
} }
@ -2003,7 +2231,7 @@ public class JavacParser implements Parser {
case MONKEYS_AT: case MONKEYS_AT:
pos = S.pos(); pos = S.pos();
S.nextToken(); S.nextToken();
return annotation(pos); return annotation(pos, AnnotationKind.DEFAULT_ANNO);
case LBRACE: case LBRACE:
pos = S.pos(); pos = S.pos();
accept(LBRACE); accept(LBRACE);
@ -2357,7 +2585,7 @@ public class JavacParser implements Parser {
S.resetDeprecatedFlag(); S.resetDeprecatedFlag();
} }
int pos = S.pos(); int pos = S.pos();
List<JCAnnotation> annotations = annotationsOpt(); List<JCAnnotation> annotations = annotationsOpt(AnnotationKind.DEFAULT_ANNO);
JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations); JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations);
List<JCExpression> typeArgs = typeArgumentsOpt(); List<JCExpression> typeArgs = typeArgumentsOpt();
int identPos = S.pos(); int identPos = S.pos();
@ -2460,16 +2688,23 @@ public class JavacParser implements Parser {
if (typarams.length() > 0 && mods.pos == Position.NOPOS) { if (typarams.length() > 0 && mods.pos == Position.NOPOS) {
mods.pos = pos; mods.pos = pos;
} }
List<JCAnnotation> annosAfterParams = annotationsOpt(AnnotationKind.DEFAULT_ANNO);
Token token = S.token(); Token token = S.token();
Name name = S.name(); Name name = S.name();
pos = S.pos(); pos = S.pos();
JCExpression type; JCExpression type;
boolean isVoid = S.token() == VOID; boolean isVoid = S.token() == VOID;
if (isVoid) { if (isVoid) {
if (annosAfterParams.nonEmpty())
illegal(annosAfterParams.head.pos);
type = to(F.at(pos).TypeIdent(TypeTags.VOID)); type = to(F.at(pos).TypeIdent(TypeTags.VOID));
S.nextToken(); S.nextToken();
} else { } else {
type = parseType(); mods.annotations = mods.annotations.appendList(annosAfterParams);
// method returns types are un-annotated types
type = unannotatedType();
} }
if (S.token() == LPAREN && !isInterface && type.getTag() == JCTree.IDENT) { if (S.token() == LPAREN && !isInterface && type.getTag() == JCTree.IDENT) {
if (isInterface || name != className) if (isInterface || name != className)
@ -2505,15 +2740,15 @@ public class JavacParser implements Parser {
} }
/** MethodDeclaratorRest = /** MethodDeclaratorRest =
* FormalParameters BracketsOpt [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";") * FormalParameters BracketsOpt [Annotations] [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";")
* VoidMethodDeclaratorRest = * VoidMethodDeclaratorRest =
* FormalParameters [Throws TypeList] ( MethodBody | ";") * FormalParameters [Annotations] [Throws TypeList] ( MethodBody | ";")
* InterfaceMethodDeclaratorRest = * InterfaceMethodDeclaratorRest =
* FormalParameters BracketsOpt [THROWS TypeList] ";" * FormalParameters BracketsOpt [Annotations] [THROWS TypeList] ";"
* VoidInterfaceMethodDeclaratorRest = * VoidInterfaceMethodDeclaratorRest =
* FormalParameters [THROWS TypeList] ";" * FormalParameters [Annotations] [THROWS TypeList] ";"
* ConstructorDeclaratorRest = * ConstructorDeclaratorRest =
* "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody * "(" FormalParameterListOpt ")" [Annotations] [THROWS TypeList] MethodBody
*/ */
JCTree methodDeclaratorRest(int pos, JCTree methodDeclaratorRest(int pos,
JCModifiers mods, JCModifiers mods,
@ -2523,7 +2758,22 @@ public class JavacParser implements Parser {
boolean isInterface, boolean isVoid, boolean isInterface, boolean isVoid,
String dc) { String dc) {
List<JCVariableDecl> params = formalParameters(); List<JCVariableDecl> params = formalParameters();
if (!isVoid) type = bracketsOpt(type);
List<JCTypeAnnotation> receiverAnnotations;
if (!isVoid) {
// need to distinguish between receiver anno and array anno
// look at typeAnnotationsPushedBack comment
this.permitTypeAnnotationsPushBack = true;
type = methodReturnArrayRest(type);
this.permitTypeAnnotationsPushBack = false;
if (typeAnnotationsPushedBack == null)
receiverAnnotations = List.nil();
else
receiverAnnotations = typeAnnotationsPushedBack;
typeAnnotationsPushedBack = null;
} else
receiverAnnotations = typeAnnotationsOpt();
List<JCExpression> thrown = List.nil(); List<JCExpression> thrown = List.nil();
if (S.token() == THROWS) { if (S.token() == THROWS) {
S.nextToken(); S.nextToken();
@ -2552,20 +2802,51 @@ public class JavacParser implements Parser {
} }
JCMethodDecl result = JCMethodDecl result =
toP(F.at(pos).MethodDef(mods, name, type, typarams, toP(F.at(pos).MethodDef(mods, name, type, typarams,
params, thrown, params, receiverAnnotations, thrown,
body, defaultValue)); body, defaultValue));
attach(result, dc); attach(result, dc);
return result; return result;
} }
/** QualidentList = Qualident {"," Qualident} /** Parses the array levels after the format parameters list, and append
* them to the return type, while preseving the order of type annotations
*/
private JCExpression methodReturnArrayRest(JCExpression type) {
if (type.getTag() != JCTree.TYPEARRAY)
return bracketsOpt(type);
JCArrayTypeTree baseArray = (JCArrayTypeTree)type;
while (TreeInfo.typeIn(baseArray.elemtype) instanceof JCArrayTypeTree)
baseArray = (JCArrayTypeTree)TreeInfo.typeIn(baseArray.elemtype);
if (baseArray.elemtype.getTag() == JCTree.ANNOTATED_TYPE) {
JCAnnotatedType at = (JCAnnotatedType)baseArray.elemtype;
at.underlyingType = bracketsOpt(at.underlyingType);
} else {
baseArray.elemtype = bracketsOpt(baseArray.elemtype);
}
return type;
}
/** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
*/ */
List<JCExpression> qualidentList() { List<JCExpression> qualidentList() {
ListBuffer<JCExpression> ts = new ListBuffer<JCExpression>(); ListBuffer<JCExpression> ts = new ListBuffer<JCExpression>();
ts.append(qualident());
List<JCTypeAnnotation> typeAnnos = typeAnnotationsOpt();
if (!typeAnnos.isEmpty())
ts.append(F.AnnotatedType(typeAnnos, qualident()));
else
ts.append(qualident());
while (S.token() == COMMA) { while (S.token() == COMMA) {
S.nextToken(); S.nextToken();
ts.append(qualident());
typeAnnos = typeAnnotationsOpt();
if (!typeAnnos.isEmpty())
ts.append(F.AnnotatedType(typeAnnos, qualident()));
else
ts.append(qualident());
} }
return ts.toList(); return ts.toList();
} }
@ -2589,12 +2870,13 @@ public class JavacParser implements Parser {
} }
} }
/** TypeParameter = TypeVariable [TypeParameterBound] /** TypeParameter = [Annotations] TypeVariable [TypeParameterBound]
* TypeParameterBound = EXTENDS Type {"&" Type} * TypeParameterBound = EXTENDS Type {"&" Type}
* TypeVariable = Ident * TypeVariable = Ident
*/ */
JCTypeParameter typeParameter() { JCTypeParameter typeParameter() {
int pos = S.pos(); int pos = S.pos();
List<JCTypeAnnotation> annos = typeAnnotationsOpt();
Name name = ident(); Name name = ident();
ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>(); ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
if (S.token() == EXTENDS) { if (S.token() == EXTENDS) {
@ -2605,7 +2887,7 @@ public class JavacParser implements Parser {
bounds.append(parseType()); bounds.append(parseType());
} }
} }
return toP(F.at(pos).TypeParameter(name, bounds.toList())); return toP(F.at(pos).TypeParameter(name, bounds.toList(), annos));
} }
/** FormalParameters = "(" [ FormalParameterList ] ")" /** FormalParameters = "(" [ FormalParameterList ] ")"
@ -2639,12 +2921,31 @@ public class JavacParser implements Parser {
*/ */
JCVariableDecl formalParameter() { JCVariableDecl formalParameter() {
JCModifiers mods = optFinal(Flags.PARAMETER); JCModifiers mods = optFinal(Flags.PARAMETER);
// need to distinguish between vararg annos and array annos
// look at typeAnnotaitonsPushedBack comment
this.permitTypeAnnotationsPushBack = true;
JCExpression type = parseType(); JCExpression type = parseType();
this.permitTypeAnnotationsPushBack = false;
if (S.token() == ELLIPSIS) { if (S.token() == ELLIPSIS) {
List<JCTypeAnnotation> varargsAnnos = typeAnnotationsPushedBack;
typeAnnotationsPushedBack = null;
checkVarargs(); checkVarargs();
mods.flags |= Flags.VARARGS; mods.flags |= Flags.VARARGS;
// insert var arg type annotations
if (varargsAnnos != null && varargsAnnos.nonEmpty())
type = F.at(S.pos()).AnnotatedType(varargsAnnos, type);
type = to(F.at(S.pos()).TypeArray(type)); type = to(F.at(S.pos()).TypeArray(type));
S.nextToken(); S.nextToken();
} else {
// if not a var arg, then typeAnnotationsPushedBack should be null
if (typeAnnotationsPushedBack != null
&& !typeAnnotationsPushedBack.isEmpty()) {
reportSyntaxError(typeAnnotationsPushedBack.head.pos,
"illegal.start.of.type");
}
typeAnnotationsPushedBack = null;
} }
return variableDeclaratorId(mods, type); return variableDeclaratorId(mods, type);
} }
@ -2829,4 +3130,10 @@ public class JavacParser implements Parser {
allowAnnotations = true; allowAnnotations = true;
} }
} }
void checkTypeAnnotations() {
if (!allowTypeAnnotations) {
log.error(S.pos(), "type.annotations.not.supported.in.source", source.name);
allowTypeAnnotations = true;
}
}
} }

View file

@ -921,6 +921,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
} else { // Final compilation } else { // Final compilation
compiler.close(false); compiler.close(false);
currentContext = contextForNextRound(currentContext, true); currentContext = contextForNextRound(currentContext, true);
this.context = currentContext;
updateProcessingState(currentContext, true);
compiler = JavaCompiler.instance(currentContext); compiler = JavaCompiler.instance(currentContext);
if (true) { if (true) {
@ -1213,6 +1215,10 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
node.sym = null; node.sym = null;
super.visitIdent(node); super.visitIdent(node);
} }
public void visitApply(JCMethodInvocation node) {
scan(node.typeargs);
super.visitApply(node);
}
}; };

View file

@ -105,6 +105,9 @@ public class JavacRoundEnvironment implements RoundEnvironment {
* elements are {@linkplain #getSpecifiedTypeElements specified * elements are {@linkplain #getSpecifiedTypeElements specified
* types} and any types nested within them. * types} and any types nested within them.
* *
* <p>This method will not return type annotations, which annotate
* types, not elements.
*
* @param a annotation type being requested * @param a annotation type being requested
* @return the elements annotated with the given annotation type, * @return the elements annotated with the given annotation type,
* or an empty set if there are none * or an empty set if there are none

View file

@ -882,6 +882,8 @@ compiler.misc.bad.const.pool.tag.at=\
bad constant pool tag: {0} at {1} bad constant pool tag: {0} at {1}
compiler.misc.bad.signature=\ compiler.misc.bad.signature=\
bad signature: {0} bad signature: {0}
compiler.misc.bad.type.annotation.value=\
bad type annotation target type value: {0}
compiler.misc.class.file.wrong.class=\ compiler.misc.class.file.wrong.class=\
class file contains wrong class: {0} class file contains wrong class: {0}
compiler.misc.class.file.not.found=\ compiler.misc.class.file.not.found=\
@ -1162,6 +1164,10 @@ compiler.err.annotations.not.supported.in.source=\
annotations are not supported in -source {0}\n\ annotations are not supported in -source {0}\n\
(use -source 5 or higher to enable annotations) (use -source 5 or higher to enable annotations)
compiler.err.type.annotations.not.supported.in.source=\
type annotations are not supported in -source {0}\n\
(use -source 7 or higher to enable type annotations)
compiler.err.foreach.not.supported.in.source=\ compiler.err.foreach.not.supported.in.source=\
for-each loops are not supported in -source {0}\n\ for-each loops are not supported in -source {0}\n\
(use -source 5 or higher to enable for-each loops) (use -source 5 or higher to enable for-each loops)

View file

@ -256,9 +256,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/ */
public static final int MODIFIERS = ANNOTATION + 1; public static final int MODIFIERS = ANNOTATION + 1;
public static final int ANNOTATED_TYPE = MODIFIERS + 1;
/** Error trees, of type Erroneous. /** Error trees, of type Erroneous.
*/ */
public static final int ERRONEOUS = MODIFIERS + 1; public static final int ERRONEOUS = ANNOTATED_TYPE + 1;
/** Unary operators, of type Unary. /** Unary operators, of type Unary.
*/ */
@ -622,6 +624,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public JCExpression restype; public JCExpression restype;
public List<JCTypeParameter> typarams; public List<JCTypeParameter> typarams;
public List<JCVariableDecl> params; public List<JCVariableDecl> params;
public List<JCTypeAnnotation> receiverAnnotations;
public List<JCExpression> thrown; public List<JCExpression> thrown;
public JCBlock body; public JCBlock body;
public JCExpression defaultValue; // for annotation types public JCExpression defaultValue; // for annotation types
@ -631,6 +634,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCExpression restype, JCExpression restype,
List<JCTypeParameter> typarams, List<JCTypeParameter> typarams,
List<JCVariableDecl> params, List<JCVariableDecl> params,
List<JCTypeAnnotation> receiver,
List<JCExpression> thrown, List<JCExpression> thrown,
JCBlock body, JCBlock body,
JCExpression defaultValue, JCExpression defaultValue,
@ -641,6 +645,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
this.restype = restype; this.restype = restype;
this.typarams = typarams; this.typarams = typarams;
this.params = params; this.params = params;
this.receiverAnnotations = (receiver != null ? receiver : List.<JCTypeAnnotation>nil());
this.thrown = thrown; this.thrown = thrown;
this.body = body; this.body = body;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
@ -659,6 +664,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public List<JCVariableDecl> getParameters() { public List<JCVariableDecl> getParameters() {
return params; return params;
} }
public List<JCTypeAnnotation> getReceiverAnnotations() { return receiverAnnotations; }
public List<JCExpression> getThrows() { public List<JCExpression> getThrows() {
return thrown; return thrown;
} }
@ -1371,6 +1377,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public static class JCNewArray extends JCExpression implements NewArrayTree { public static class JCNewArray extends JCExpression implements NewArrayTree {
public JCExpression elemtype; public JCExpression elemtype;
public List<JCExpression> dims; public List<JCExpression> dims;
public List<JCTypeAnnotation> annotations;
public List<List<JCTypeAnnotation>> dimAnnotations;
public List<JCExpression> elems; public List<JCExpression> elems;
protected JCNewArray(JCExpression elemtype, protected JCNewArray(JCExpression elemtype,
List<JCExpression> dims, List<JCExpression> dims,
@ -1378,6 +1386,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
{ {
this.elemtype = elemtype; this.elemtype = elemtype;
this.dims = dims; this.dims = dims;
this.annotations = List.nil();
this.dimAnnotations = List.nil();
this.elems = elems; this.elems = elems;
} }
@Override @Override
@ -1860,9 +1870,11 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public static class JCTypeParameter extends JCTree implements TypeParameterTree { public static class JCTypeParameter extends JCTree implements TypeParameterTree {
public Name name; public Name name;
public List<JCExpression> bounds; public List<JCExpression> bounds;
protected JCTypeParameter(Name name, List<JCExpression> bounds) { public List<JCTypeAnnotation> annotations;
protected JCTypeParameter(Name name, List<JCExpression> bounds, List<JCTypeAnnotation> annotations) {
this.name = name; this.name = name;
this.bounds = bounds; this.bounds = bounds;
this.annotations = annotations;
} }
@Override @Override
public void accept(Visitor v) { v.visitTypeParameter(this); } public void accept(Visitor v) { v.visitTypeParameter(this); }
@ -1872,6 +1884,9 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public List<JCExpression> getBounds() { public List<JCExpression> getBounds() {
return bounds; return bounds;
} }
public List<JCTypeAnnotation> getAnnotations() {
return annotations;
}
@Override @Override
public <R,D> R accept(TreeVisitor<R,D> v, D d) { public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitTypeParameter(this, d); return v.visitTypeParameter(this, d);
@ -1962,6 +1977,16 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
} }
} }
public static class JCTypeAnnotation extends JCAnnotation {
public TypeAnnotationPosition annotation_position;
public Attribute.TypeCompound attribute_field;
protected JCTypeAnnotation(JCTree annotationType, List<JCExpression> args) {
super(annotationType, args);
this.annotation_position = new TypeAnnotationPosition();
}
}
public static class JCModifiers extends JCTree implements com.sun.source.tree.ModifiersTree { public static class JCModifiers extends JCTree implements com.sun.source.tree.ModifiersTree {
public long flags; public long flags;
public List<JCAnnotation> annotations; public List<JCAnnotation> annotations;
@ -1989,6 +2014,33 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
} }
} }
public static class JCAnnotatedType extends JCExpression implements com.sun.source.tree.AnnotatedTypeTree {
public List<JCTypeAnnotation> annotations;
public JCExpression underlyingType;
protected JCAnnotatedType(List<JCTypeAnnotation> annotations, JCExpression underlyingType) {
this.annotations = annotations;
this.underlyingType = underlyingType;
}
@Override
public void accept(Visitor v) { v.visitAnnotatedType(this); }
public Kind getKind() { return Kind.ANNOTATED_TYPE; }
public List<JCTypeAnnotation> getAnnotations() {
return annotations;
}
public JCExpression getUnderlyingType() {
return underlyingType;
}
@Override
public <R,D> R accept(TreeVisitor<R,D> v, D d) {
return v.visitAnnotatedType(this, d);
}
@Override
public int getTag() {
return ANNOTATED_TYPE;
}
}
public static class JCErroneous extends JCExpression public static class JCErroneous extends JCExpression
implements com.sun.source.tree.ErroneousTree { implements com.sun.source.tree.ErroneousTree {
public List<? extends JCTree> errs; public List<? extends JCTree> errs;
@ -2056,6 +2108,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCExpression restype, JCExpression restype,
List<JCTypeParameter> typarams, List<JCTypeParameter> typarams,
List<JCVariableDecl> params, List<JCVariableDecl> params,
List<JCTypeAnnotation> receiver,
List<JCExpression> thrown, List<JCExpression> thrown,
JCBlock body, JCBlock body,
JCExpression defaultValue); JCExpression defaultValue);
@ -2172,6 +2225,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitTypeBoundKind(TypeBoundKind that) { visitTree(that); } public void visitTypeBoundKind(TypeBoundKind that) { visitTree(that); }
public void visitAnnotation(JCAnnotation that) { visitTree(that); } public void visitAnnotation(JCAnnotation that) { visitTree(that); }
public void visitModifiers(JCModifiers that) { visitTree(that); } public void visitModifiers(JCModifiers that) { visitTree(that); }
public void visitAnnotatedType(JCAnnotatedType that) { visitTree(that); }
public void visitErroneous(JCErroneous that) { visitTree(that); } public void visitErroneous(JCErroneous that) { visitTree(that); }
public void visitLetExpr(LetExpr that) { visitTree(that); } public void visitLetExpr(LetExpr that) { visitTree(that); }

View file

@ -224,6 +224,15 @@ public class Pretty extends JCTree.Visitor {
} }
} }
public void printTypeAnnotations(List<JCTypeAnnotation> trees) throws IOException {
if (trees.nonEmpty())
print(" ");
for (List<JCTypeAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
printExpr(l.head);
print(" ");
}
}
/** Print documentation comment, if it exists /** Print documentation comment, if it exists
* @param tree The tree for which a documentation comment should be printed. * @param tree The tree for which a documentation comment should be printed.
*/ */
@ -850,21 +859,33 @@ public class Pretty extends JCTree.Visitor {
try { try {
if (tree.elemtype != null) { if (tree.elemtype != null) {
print("new "); print("new ");
printTypeAnnotations(tree.annotations);
JCTree elem = tree.elemtype; JCTree elem = tree.elemtype;
if (elem instanceof JCArrayTypeTree) printBaseElementType(elem);
printBaseElementType((JCArrayTypeTree) elem); boolean isElemAnnoType = elem instanceof JCAnnotatedType;
else int i = 0;
printExpr(elem); List<List<JCTypeAnnotation>> da = tree.dimAnnotations;
for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) { for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
if (da.size() > i) {
printTypeAnnotations(da.get(i));
}
print("["); print("[");
i++;
printExpr(l.head); printExpr(l.head);
print("]"); print("]");
} }
if (tree.elems != null) {
if (isElemAnnoType) {
printTypeAnnotations(((JCAnnotatedType)tree.elemtype).annotations);
}
print("[]");
}
if (isElemAnnoType)
elem = ((JCAnnotatedType)elem).underlyingType;
if (elem instanceof JCArrayTypeTree) if (elem instanceof JCArrayTypeTree)
printBrackets((JCArrayTypeTree) elem); printBrackets((JCArrayTypeTree) elem);
} }
if (tree.elems != null) { if (tree.elems != null) {
if (tree.elemtype != null) print("[]");
print("{"); print("{");
printExprs(tree.elems); printExprs(tree.elems);
print("}"); print("}");
@ -1112,14 +1133,21 @@ public class Pretty extends JCTree.Visitor {
} }
// Prints the inner element type of a nested array // Prints the inner element type of a nested array
private void printBaseElementType(JCArrayTypeTree tree) throws IOException { private void printBaseElementType(JCTree tree) throws IOException {
JCTree elem = tree.elemtype; switch (tree.getTag()) {
while (elem instanceof JCWildcard) case JCTree.TYPEARRAY:
elem = ((JCWildcard) elem).inner; printBaseElementType(((JCArrayTypeTree)tree).elemtype);
if (elem instanceof JCArrayTypeTree) return;
printBaseElementType((JCArrayTypeTree) elem); case JCTree.WILDCARD:
else printBaseElementType(((JCWildcard)tree).inner);
printExpr(elem); return;
case JCTree.ANNOTATED_TYPE:
printBaseElementType(((JCAnnotatedType)tree).underlyingType);
return;
default:
printExpr(tree);
return;
}
} }
// prints the brackets of a nested array in reverse order // prints the brackets of a nested array in reverse order
@ -1127,8 +1155,13 @@ public class Pretty extends JCTree.Visitor {
JCTree elem; JCTree elem;
while (true) { while (true) {
elem = tree.elemtype; elem = tree.elemtype;
if (elem.getTag() == JCTree.ANNOTATED_TYPE) {
JCAnnotatedType atype = (JCAnnotatedType) elem;
printTypeAnnotations(atype.annotations);
elem = atype.underlyingType;
}
print("[]"); print("[]");
if (!(elem instanceof JCArrayTypeTree)) break; if (elem.getTag() != JCTree.TYPEARRAY) break;
tree = (JCArrayTypeTree) elem; tree = (JCArrayTypeTree) elem;
} }
} }
@ -1213,6 +1246,15 @@ public class Pretty extends JCTree.Visitor {
} }
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
try {
printTypeAnnotations(tree.annotations);
printExpr(tree.underlyingType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitTree(JCTree tree) { public void visitTree(JCTree tree) {
try { try {
print("(UNKNOWN: " + tree + ")"); print("(UNKNOWN: " + tree + ")");
@ -1221,4 +1263,5 @@ public class Pretty extends JCTree.Visitor {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
} }
} }

View file

@ -71,6 +71,13 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
return lb.toList(); return lb.toList();
} }
public JCTree visitAnnotatedType(AnnotatedTypeTree node, P p) {
JCAnnotatedType t = (JCAnnotatedType) node;
List<JCTypeAnnotation> annotations = copy(t.annotations, p);
JCExpression underlyingType = copy(t.underlyingType, p);
return M.at(t.pos).AnnotatedType(annotations, underlyingType);
}
public JCTree visitAnnotation(AnnotationTree node, P p) { public JCTree visitAnnotation(AnnotationTree node, P p) {
JCAnnotation t = (JCAnnotation) node; JCAnnotation t = (JCAnnotation) node;
JCTree annotationType = copy(t.annotationType, p); JCTree annotationType = copy(t.annotationType, p);
@ -233,10 +240,11 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
JCExpression restype = copy(t.restype, p); JCExpression restype = copy(t.restype, p);
List<JCTypeParameter> typarams = copy(t.typarams, p); List<JCTypeParameter> typarams = copy(t.typarams, p);
List<JCVariableDecl> params = copy(t.params, p); List<JCVariableDecl> params = copy(t.params, p);
List<JCTypeAnnotation> receiver = copy(t.receiverAnnotations, p);
List<JCExpression> thrown = copy(t.thrown, p); List<JCExpression> thrown = copy(t.thrown, p);
JCBlock body = copy(t.body, p); JCBlock body = copy(t.body, p);
JCExpression defaultValue = copy(t.defaultValue, p); JCExpression defaultValue = copy(t.defaultValue, p);
return M.at(t.pos).MethodDef(mods, t.name, restype, typarams, params, thrown, body, defaultValue); return M.at(t.pos).MethodDef(mods, t.name, restype, typarams, params, receiver, thrown, body, defaultValue);
} }
public JCTree visitMethodInvocation(MethodInvocationTree node, P p) { public JCTree visitMethodInvocation(MethodInvocationTree node, P p) {
@ -357,8 +365,9 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
public JCTree visitTypeParameter(TypeParameterTree node, P p) { public JCTree visitTypeParameter(TypeParameterTree node, P p) {
JCTypeParameter t = (JCTypeParameter) node; JCTypeParameter t = (JCTypeParameter) node;
List<JCTypeAnnotation> annos = copy(t.annotations, p);
List<JCExpression> bounds = copy(t.bounds, p); List<JCExpression> bounds = copy(t.bounds, p);
return M.at(t.pos).TypeParameter(t.name, t.bounds); return M.at(t.pos).TypeParameter(t.name, bounds, annos);
} }
public JCTree visitInstanceOf(InstanceOfTree node, P p) { public JCTree visitInstanceOf(InstanceOfTree node, P p) {

View file

@ -298,6 +298,8 @@ public class TreeInfo {
case(JCTree.POSTINC): case(JCTree.POSTINC):
case(JCTree.POSTDEC): case(JCTree.POSTDEC):
return getStartPos(((JCUnary) tree).arg); return getStartPos(((JCUnary) tree).arg);
case(JCTree.ANNOTATED_TYPE):
return getStartPos(((JCAnnotatedType) tree).underlyingType);
case(JCTree.VARDEF): { case(JCTree.VARDEF): {
JCVariableDecl node = (JCVariableDecl)tree; JCVariableDecl node = (JCVariableDecl)tree;
if (node.mods.pos != Position.NOPOS) { if (node.mods.pos != Position.NOPOS) {
@ -859,4 +861,25 @@ public class TreeInfo {
return null; return null;
} }
} }
/**
* Returns the underlying type of the tree if it is annotated type,
* or the tree itself otherwise
*/
public static JCExpression typeIn(JCExpression tree) {
switch (tree.getTag()) {
case JCTree.ANNOTATED_TYPE:
return ((JCAnnotatedType)tree).underlyingType;
case JCTree.IDENT: /* simple names */
case JCTree.TYPEIDENT: /* primitive name */
case JCTree.SELECT: /* qualified name */
case JCTree.TYPEARRAY: /* array types */
case JCTree.WILDCARD: /* wild cards */
case JCTree.TYPEPARAMETER: /* type parameters */
case JCTree.TYPEAPPLY: /* parameterized types */
return tree;
default:
throw new AssertionError("Unexpected type tree: " + tree);
}
}
} }

View file

@ -168,6 +168,20 @@ public class TreeMaker implements JCTree.Factory {
List<JCVariableDecl> params, List<JCVariableDecl> params,
List<JCExpression> thrown, List<JCExpression> thrown,
JCBlock body, JCBlock body,
JCExpression defaultValue) {
return MethodDef(
mods, name, restype, typarams, params,
null, thrown, body, defaultValue);
}
public JCMethodDecl MethodDef(JCModifiers mods,
Name name,
JCExpression restype,
List<JCTypeParameter> typarams,
List<JCVariableDecl> params,
List<JCTypeAnnotation> receiver,
List<JCExpression> thrown,
JCBlock body,
JCExpression defaultValue) JCExpression defaultValue)
{ {
JCMethodDecl tree = new JCMethodDecl(mods, JCMethodDecl tree = new JCMethodDecl(mods,
@ -175,6 +189,7 @@ public class TreeMaker implements JCTree.Factory {
restype, restype,
typarams, typarams,
params, params,
receiver,
thrown, thrown,
body, body,
defaultValue, defaultValue,
@ -430,7 +445,11 @@ public class TreeMaker implements JCTree.Factory {
} }
public JCTypeParameter TypeParameter(Name name, List<JCExpression> bounds) { public JCTypeParameter TypeParameter(Name name, List<JCExpression> bounds) {
JCTypeParameter tree = new JCTypeParameter(name, bounds); return TypeParameter(name, bounds, List.<JCTypeAnnotation>nil());
}
public JCTypeParameter TypeParameter(Name name, List<JCExpression> bounds, List<JCTypeAnnotation> annos) {
JCTypeParameter tree = new JCTypeParameter(name, bounds, annos);
tree.pos = pos; tree.pos = pos;
return tree; return tree;
} }
@ -453,6 +472,12 @@ public class TreeMaker implements JCTree.Factory {
return tree; return tree;
} }
public JCTypeAnnotation TypeAnnotation(JCTree annotationType, List<JCExpression> args) {
JCTypeAnnotation tree = new JCTypeAnnotation(annotationType, args);
tree.pos = pos;
return tree;
}
public JCModifiers Modifiers(long flags, List<JCAnnotation> annotations) { public JCModifiers Modifiers(long flags, List<JCAnnotation> annotations) {
JCModifiers tree = new JCModifiers(flags, annotations); JCModifiers tree = new JCModifiers(flags, annotations);
boolean noFlags = (flags & Flags.StandardFlags) == 0; boolean noFlags = (flags & Flags.StandardFlags) == 0;
@ -464,6 +489,12 @@ public class TreeMaker implements JCTree.Factory {
return Modifiers(flags, List.<JCAnnotation>nil()); return Modifiers(flags, List.<JCAnnotation>nil());
} }
public JCAnnotatedType AnnotatedType(List<JCTypeAnnotation> annotations, JCExpression underlyingType) {
JCAnnotatedType tree = new JCAnnotatedType(annotations, underlyingType);
tree.pos = pos;
return tree;
}
public JCErroneous Erroneous() { public JCErroneous Erroneous() {
return Erroneous(List.<JCTree>nil()); return Erroneous(List.<JCTree>nil());
} }
@ -772,6 +803,7 @@ public class TreeMaker implements JCTree.Factory {
Type(mtype.getReturnType()), Type(mtype.getReturnType()),
TypeParams(mtype.getTypeArguments()), TypeParams(mtype.getTypeArguments()),
Params(mtype.getParameterTypes(), m), Params(mtype.getParameterTypes(), m),
null,
Types(mtype.getThrownTypes()), Types(mtype.getThrownTypes()),
body, body,
null, null,

View file

@ -85,6 +85,7 @@ public class TreeScanner extends Visitor {
scan(tree.restype); scan(tree.restype);
scan(tree.typarams); scan(tree.typarams);
scan(tree.params); scan(tree.params);
scan(tree.receiverAnnotations);
scan(tree.thrown); scan(tree.thrown);
scan(tree.defaultValue); scan(tree.defaultValue);
scan(tree.body); scan(tree.body);
@ -204,8 +205,11 @@ public class TreeScanner extends Visitor {
} }
public void visitNewArray(JCNewArray tree) { public void visitNewArray(JCNewArray tree) {
scan(tree.annotations);
scan(tree.elemtype); scan(tree.elemtype);
scan(tree.dims); scan(tree.dims);
for (List<JCTypeAnnotation> annos : tree.dimAnnotations)
scan(annos);
scan(tree.elems); scan(tree.elems);
} }
@ -270,6 +274,7 @@ public class TreeScanner extends Visitor {
} }
public void visitTypeParameter(JCTypeParameter tree) { public void visitTypeParameter(JCTypeParameter tree) {
scan(tree.annotations);
scan(tree.bounds); scan(tree.bounds);
} }
@ -293,6 +298,11 @@ public class TreeScanner extends Visitor {
scan(tree.args); scan(tree.args);
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
scan(tree.annotations);
scan(tree.underlyingType);
}
public void visitErroneous(JCErroneous tree) { public void visitErroneous(JCErroneous tree) {
} }

View file

@ -282,6 +282,11 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitNewArray(JCNewArray tree) { public void visitNewArray(JCNewArray tree) {
tree.annotations = translate(tree.annotations);
List<List<JCTypeAnnotation>> dimAnnos = List.nil();
for (List<JCTypeAnnotation> origDimAnnos : tree.dimAnnotations)
dimAnnos = dimAnnos.append(translate(origDimAnnos));
tree.dimAnnotations = dimAnnos;
tree.elemtype = translate(tree.elemtype); tree.elemtype = translate(tree.elemtype);
tree.dims = translate(tree.dims); tree.dims = translate(tree.dims);
tree.elems = translate(tree.elems); tree.elems = translate(tree.elems);
@ -363,6 +368,7 @@ public class TreeTranslator extends JCTree.Visitor {
} }
public void visitTypeParameter(JCTypeParameter tree) { public void visitTypeParameter(JCTypeParameter tree) {
tree.annotations = translate(tree.annotations);
tree.bounds = translate(tree.bounds); tree.bounds = translate(tree.bounds);
result = tree; result = tree;
} }
@ -400,6 +406,12 @@ public class TreeTranslator extends JCTree.Visitor {
result = tree; result = tree;
} }
public void visitAnnotatedType(JCAnnotatedType tree) {
tree.annotations = translate(tree.annotations);
tree.underlyingType = translate(tree.underlyingType);
result = tree;
}
public void visitTree(JCTree tree) { public void visitTree(JCTree tree) {
throw new AssertionError(tree); throw new AssertionError(tree);
} }

View file

@ -99,6 +99,8 @@ public class Names {
public final Name Annotation; public final Name Annotation;
public final Name RuntimeVisibleAnnotations; public final Name RuntimeVisibleAnnotations;
public final Name RuntimeInvisibleAnnotations; public final Name RuntimeInvisibleAnnotations;
public final Name RuntimeVisibleTypeAnnotations;
public final Name RuntimeInvisibleTypeAnnotations;
public final Name RuntimeVisibleParameterAnnotations; public final Name RuntimeVisibleParameterAnnotations;
public final Name RuntimeInvisibleParameterAnnotations; public final Name RuntimeInvisibleParameterAnnotations;
public final Name Value; public final Name Value;
@ -115,6 +117,8 @@ public class Names {
public final Name getClass; public final Name getClass;
public final Name invoke; public final Name invoke;
public final Name TYPE; public final Name TYPE;
public final Name TYPE_USE;
public final Name TYPE_PARAMETER;
public final Name FIELD; public final Name FIELD;
public final Name METHOD; public final Name METHOD;
public final Name PARAMETER; public final Name PARAMETER;
@ -205,6 +209,8 @@ public class Names {
Annotation = fromString("Annotation"); Annotation = fromString("Annotation");
RuntimeVisibleAnnotations = fromString("RuntimeVisibleAnnotations"); RuntimeVisibleAnnotations = fromString("RuntimeVisibleAnnotations");
RuntimeInvisibleAnnotations = fromString("RuntimeInvisibleAnnotations"); RuntimeInvisibleAnnotations = fromString("RuntimeInvisibleAnnotations");
RuntimeVisibleTypeAnnotations = fromString("RuntimeVisibleTypeAnnotations");
RuntimeInvisibleTypeAnnotations = fromString("RuntimeInvisibleTypeAnnotations");
RuntimeVisibleParameterAnnotations = fromString("RuntimeVisibleParameterAnnotations"); RuntimeVisibleParameterAnnotations = fromString("RuntimeVisibleParameterAnnotations");
RuntimeInvisibleParameterAnnotations = fromString("RuntimeInvisibleParameterAnnotations"); RuntimeInvisibleParameterAnnotations = fromString("RuntimeInvisibleParameterAnnotations");
Value = fromString("Value"); Value = fromString("Value");
@ -224,6 +230,8 @@ public class Names {
invoke = fromString("invoke"); invoke = fromString("invoke");
TYPE = fromString("TYPE"); TYPE = fromString("TYPE");
TYPE_USE = fromString("TYPE_USE");
TYPE_PARAMETER = fromString("TYPE_PARAMETER");
FIELD = fromString("FIELD"); FIELD = fromString("FIELD");
METHOD = fromString("METHOD"); METHOD = fromString("METHOD");
PARAMETER = fromString("PARAMETER"); PARAMETER = fromString("PARAMETER");

View file

@ -26,6 +26,7 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Annotation; import com.sun.tools.classfile.Annotation;
import com.sun.tools.classfile.ExtendedAnnotation;
import com.sun.tools.classfile.Annotation.Annotation_element_value; import com.sun.tools.classfile.Annotation.Annotation_element_value;
import com.sun.tools.classfile.Annotation.Array_element_value; import com.sun.tools.classfile.Annotation.Array_element_value;
import com.sun.tools.classfile.Annotation.Class_element_value; import com.sun.tools.classfile.Annotation.Class_element_value;
@ -62,6 +63,12 @@ public class AnnotationWriter extends BasicWriter {
print(")"); print(")");
} }
public void write(ExtendedAnnotation annot) {
write(annot.annotation);
print('@');
print(annot.position.toString());
}
public void write(Annotation.element_value_pair pair) { public void write(Annotation.element_value_pair pair) {
print("#" + pair.element_name_index + ":"); print("#" + pair.element_name_index + ":");
write(pair.value); write(pair.value);

View file

@ -51,8 +51,10 @@ import com.sun.tools.classfile.ModuleMemberTable_attribute;
import com.sun.tools.classfile.Module_attribute; import com.sun.tools.classfile.Module_attribute;
import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute; import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute; import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.Signature_attribute; import com.sun.tools.classfile.Signature_attribute;
import com.sun.tools.classfile.SourceDebugExtension_attribute; import com.sun.tools.classfile.SourceDebugExtension_attribute;
import com.sun.tools.classfile.SourceFile_attribute; import com.sun.tools.classfile.SourceFile_attribute;
@ -434,6 +436,26 @@ public class AttributeWriter extends BasicWriter
return null; return null;
} }
public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, Void ignore) {
println(" RuntimeVisibleTypeAnnotations: ");
for (int i = 0; i < attr.annotations.length; i++) {
print(" " + i + ": ");
annotationWriter.write(attr.annotations[i]);
println();
}
return null;
}
public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, Void ignore) {
println(" RuntimeInvisibleTypeAnnotations: ");
for (int i = 0; i < attr.annotations.length; i++) {
print(" " + i + ": ");
annotationWriter.write(attr.annotations[i]);
println();
}
return null;
}
public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, Void ignore) { public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, Void ignore) {
println(" RuntimeVisibleParameterAnnotations: "); println(" RuntimeVisibleParameterAnnotations: ");
for (int param = 0; param < attr.parameter_annotations.length; param++) { for (int param = 0; param < attr.parameter_annotations.length; param++) {

View file

@ -97,7 +97,7 @@ public class T6341866 {
processorServices.delete(); processorServices.delete();
List<String> opts = new ArrayList<String>(); List<String> opts = new ArrayList<String>();
opts.addAll(Arrays.asList("-d", ".", "-sourcepath", testSrc, "-classpath", testClasses)); opts.addAll(Arrays.asList("-d", ".", "-sourcepath", testSrc, "-classpath", testClasses, "-source", "1.6"));
if (implicitType.opt != null) if (implicitType.opt != null)
opts.add(implicitType.opt); opts.add(implicitType.opt);

View file

@ -54,6 +54,7 @@ public class T6348499 {
fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, "A.java"))); fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, "A.java")));
Iterable<String> opts = Arrays.asList("-proc:only", Iterable<String> opts = Arrays.asList("-proc:only",
"-processor", "A", "-processor", "A",
"-source", "1.6",
"-processorpath", testClasses); "-processorpath", testClasses);
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
JavacTask task = tool.getTask(out, fm, dl, opts, null, files); JavacTask task = tool.getTask(out, fm, dl, opts, null, files);

View file

@ -55,6 +55,7 @@ public class T6414633 {
fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, A.class.getName()+".java"))); fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, A.class.getName()+".java")));
String[] opts = { "-proc:only", String[] opts = { "-proc:only",
"-processor", A.class.getName(), "-processor", A.class.getName(),
"-source", "1.6",
"-classpath", testClasses }; "-classpath", testClasses };
JavacTask task = tool.getTask(null, fm, dl, Arrays.asList(opts), null, files); JavacTask task = tool.getTask(null, fm, dl, Arrays.asList(opts), null, files);
task.call(); task.call();

View file

@ -63,6 +63,7 @@ public class T6430209 {
new File(testSrc, "test0.java"), new File(testSrc, "test1.java"))); new File(testSrc, "test0.java"), new File(testSrc, "test1.java")));
Iterable<String> opts = Arrays.asList("-proc:only", Iterable<String> opts = Arrays.asList("-proc:only",
"-processor", "b6341534", "-processor", "b6341534",
"-source", "1.6",
"-processorpath", testClasses); "-processorpath", testClasses);
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
JavacTask task = tool.getTask(out, fm, dl, opts, null, files); JavacTask task = tool.getTask(out, fm, dl, opts, null, files);

View file

@ -49,7 +49,8 @@ public class T6439826 extends AbstractProcessor {
StandardJavaFileManager fm = tool.getStandardFileManager(dl, null, null); StandardJavaFileManager fm = tool.getStandardFileManager(dl, null, null);
Iterable<? extends JavaFileObject> files = Iterable<? extends JavaFileObject> files =
fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, T6439826.class.getName()+".java"))); fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrc, T6439826.class.getName()+".java")));
Iterable<String> opts = Arrays.asList("-proc:only", Iterable<String> opts = Arrays.asList("-source","1.6",
"-proc:only",
"-processor", "T6439826", "-processor", "T6439826",
"-processorpath", testClasses); "-processorpath", testClasses);
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();