MakeAnnotation.java 7.1 KB

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