1 package gnu.kawa.reflect; 2 import gnu.bytecode.*; 3 import gnu.mapping.*; 4 import java.lang.reflect.Proxy; 5 import java.util.List; 6 import java.util.ArrayList; 7 import java.lang.reflect.Array; 8 import gnu.expr.*; 9 import gnu.text.SourceMessages; 10 11 /* A procedure that evaluates to an Annotation value. 12 * The parameters are using (keyword,value)-pairs, 13 * though (as in Java) a single argument implies a "value" keyword. 14 * Usually this will be constant-folded so the annotation can be 15 * written out to a class file. 16 */ 17 18 public class MakeAnnotation extends ProcedureN 19 { 20 public static final MakeAnnotation instance = new MakeAnnotation(null); 21 22 /** If null, get annotationType from first argument. */ 23 ClassType annotationType; 24 MakeAnnotation(ClassType annotationType)25 public MakeAnnotation (ClassType annotationType) 26 { 27 this.annotationType = annotationType; 28 setProperty(Procedure.validateApplyKey, 29 "gnu.kawa.reflect.MakeAnnotation:validate"); 30 } 31 make(Object annotationType)32 public static MakeAnnotation make (Object annotationType) 33 { 34 ClassType annotationCType; 35 if (annotationType instanceof ClassType) 36 annotationCType = (ClassType) annotationType; 37 else if (annotationType instanceof Class) 38 annotationCType = (ClassType) Type.make((Class) annotationType); 39 else 40 annotationCType = ClassType.make(annotationType.toString()); 41 return new MakeAnnotation(annotationCType); 42 } 43 44 static final gnu.bytecode.Method makeMethod = 45 ClassType.make("gnu.kawa.reflect.MakeAnnotation") 46 .getDeclaredMethod("make", 1); 47 public static final Procedure makeMethodProc = new PrimProcedure(makeMethod); 48 public static final QuoteExp makeMethodExp = 49 QuoteExp.getInstance(makeMethodProc); 50 makeAnnotationMaker(Expression classRef)51 public static ApplyExp makeAnnotationMaker (Expression classRef) 52 { 53 ApplyExp aexp = new ApplyExp(MakeAnnotation.makeMethodExp, new Expression[] { classRef }); 54 aexp.setFlag(ApplyExp.INLINE_IF_CONSTANT); 55 return aexp; 56 } 57 getName()58 public String getName () 59 { 60 return annotationType == null ? "make-annotation" 61 : "@" + annotationType.getName(); 62 } 63 64 validate(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)65 public static Expression validate 66 (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc) 67 { 68 Compilation comp = visitor.getCompilation(); 69 Language language = visitor.getLanguage(); 70 SourceMessages messages = visitor.getMessages(); 71 boolean mustBeConstant = visitor.processingAnnotations(); 72 MakeAnnotation aproc = (MakeAnnotation) proc; 73 Expression[] args = exp.getArgs(); 74 int nargs = args.length; 75 int i = 0; 76 77 ClassType annotationType = aproc.annotationType; 78 79 if (annotationType == null) 80 { 81 args[0] = visitor.visit(args[0], null); 82 i++; 83 Type t = visitor.getLanguage().getTypeFor(args[0], true); 84 if (t instanceof ClassType) 85 annotationType = (ClassType) t; 86 } 87 int istart = i; 88 89 AnnotationEntry aentry = null; 90 if (annotationType != null) 91 { 92 if (annotationType.implementsInterface(Type.javalangannotationAnnotationType)) 93 aentry = new AnnotationEntry(annotationType); 94 else 95 messages.error('e', annotationType.getName()+" is not an annotation type"); 96 } 97 else if (mustBeConstant) 98 messages.error('e', "annotation type is not a known class"); 99 boolean warnedMissingName = false; 100 for (; i < nargs; i++) 101 { 102 String name; 103 int ikey = i; 104 if (i == istart && nargs == istart+1) 105 name = "value"; 106 else 107 { 108 args[i] = visitor.visit(args[i], null); 109 Object keyword = args[i].valueIfConstant(); 110 if (i == nargs || ! (keyword instanceof Keyword)) 111 { 112 if (! warnedMissingName) 113 messages.error(mustBeConstant ? 'e' : 'w', 114 "missing keyword in annotation arguments"); 115 warnedMissingName = false; 116 aentry = null; 117 name = null; 118 } 119 else 120 { 121 i++; 122 name = ((Keyword) keyword).getName(); 123 } 124 } 125 Type eltype = null; 126 if (annotationType != null && name != null) 127 { 128 Method method = annotationType.getDeclaredMethod(name, Type.typeArray0); 129 if (method == null) 130 { 131 comp.error('e', "no annotation element named '"+name+'\'', args[ikey]); 132 aentry = null; 133 } 134 else 135 eltype = comp.getLanguage().getLangTypeFor(method.getReturnType()); 136 } 137 138 int ecount = messages.getErrorCount(); 139 args[i] = visitor.visit(args[i], eltype); 140 Object arg = args[i].valueIfConstant(); 141 if (messages.getErrorCount() > ecount) 142 { 143 eltype = null; 144 aentry = null; 145 } 146 else if (arg == null && mustBeConstant) 147 { 148 comp.error('e', "annotation value must be constant", args[i]); 149 eltype = null; 150 aentry = null; 151 } 152 if (aentry != null) 153 { 154 try 155 { 156 aentry.addMember(name, arg, eltype); 157 } 158 catch (Exception ex) 159 { 160 aentry = null; 161 comp.error(mustBeConstant ? 'e' : 'w', 162 "bad annotation value", 163 args[i]); 164 } 165 } 166 } 167 if (aentry != null) 168 { 169 Class aclass = annotationType.getReflectClass(); 170 return new QuoteExp(Proxy.newProxyInstance(aclass.getClassLoader(), 171 new Class[] { aclass }, aentry), 172 annotationType); 173 } 174 else 175 return exp; 176 } 177 applyN(Object[] args)178 public Object applyN (Object[] args) 179 { 180 return applyN(args, null); 181 } 182 applyN(Object[] args, SourceMessages messages)183 public Object applyN (Object[] args, SourceMessages messages) 184 { 185 int nargs = args.length; 186 int i = 0; 187 188 ClassType annotationType = this.annotationType; 189 Class aclass; 190 191 if (annotationType == null) 192 { 193 aclass = (Class) args[i++]; 194 annotationType = (ClassType) Type.make(aclass); 195 } 196 else 197 aclass = annotationType.getReflectClass(); 198 int istart = i; 199 200 AnnotationEntry aentry = new AnnotationEntry(annotationType); 201 for (; i < nargs; i++) 202 { 203 String name; 204 if (i == istart && nargs == istart+1) 205 name = "value"; 206 else 207 { 208 Object keyword = args[i]; 209 i++; 210 if (i == nargs || ! (keyword instanceof Keyword)) 211 throw new IllegalArgumentException("missing keyword in annotation arguments"); 212 name = ((Keyword) keyword).getName(); 213 } 214 Object arg = args[i]; 215 Method method = annotationType.getDeclaredMethod(name, Type.typeArray0); 216 if (method == null) 217 throw new IllegalArgumentException("no annotation element named '"+name+'\''); 218 Type eltype = method.getReturnType(); 219 aentry.addMember(name, AnnotationEntry.asAnnotationValue(arg, eltype)); 220 } 221 return Proxy.newProxyInstance(aclass.getClassLoader(), 222 new Class[] { aclass }, aentry); 223 } 224 } 225