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