1 /* 2 * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.model; 27 28 import java.io.IOException; 29 import java.io.ObjectInputStream; 30 import java.lang.annotation.*; 31 import java.lang.reflect.Array; 32 import java.lang.reflect.Method; 33 import java.util.LinkedHashMap; 34 import java.util.Map; 35 import java.util.stream.Collectors; 36 import sun.reflect.annotation.*; 37 38 import javax.lang.model.type.MirroredTypeException; 39 import javax.lang.model.type.MirroredTypesException; 40 import javax.lang.model.type.TypeMirror; 41 42 import com.sun.tools.javac.code.*; 43 import com.sun.tools.javac.code.Symbol.*; 44 import com.sun.tools.javac.code.Type.ArrayType; 45 import com.sun.tools.javac.util.*; 46 47 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 48 import static com.sun.tools.javac.code.Kinds.Kind.*; 49 50 /** 51 * A generator of dynamic proxy implementations of 52 * java.lang.annotation.Annotation. 53 * 54 * <p> The "dynamic proxy return form" of an annotation element value is 55 * the form used by sun.reflect.annotation.AnnotationInvocationHandler. 56 * 57 * <p><b>This is NOT part of any supported API. 58 * If you write code that depends on this, you do so at your own risk. 59 * This code and its internal interfaces are subject to change or 60 * deletion without notice.</b> 61 */ 62 63 public class AnnotationProxyMaker { 64 65 private final Attribute.Compound anno; 66 private final Class<? extends Annotation> annoType; 67 68 69 private AnnotationProxyMaker(Attribute.Compound anno, 70 Class<? extends Annotation> annoType) { 71 this.anno = anno; 72 this.annoType = annoType; 73 } 74 75 76 /** 77 * Returns a dynamic proxy for an annotation mirror. 78 */ 79 public static <A extends Annotation> A generateAnnotation( 80 Attribute.Compound anno, Class<A> annoType) { 81 AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType); 82 return annoType.cast(apm.generateAnnotation()); 83 } 84 85 86 /** 87 * Returns a dynamic proxy for an annotation mirror. 88 */ 89 private Annotation generateAnnotation() { 90 return AnnotationParser.annotationForMap(annoType, 91 getAllReflectedValues()); 92 } 93 94 /** 95 * Returns a map from element names to their values in "dynamic 96 * proxy return form". Includes all elements, whether explicit or 97 * defaulted. 98 */ 99 private Map<String, Object> getAllReflectedValues() { 100 Map<String, Object> res = new LinkedHashMap<>(); 101 102 for (Map.Entry<MethodSymbol, Attribute> entry : 103 getAllValues().entrySet()) { 104 MethodSymbol meth = entry.getKey(); 105 Object value = generateValue(meth, entry.getValue()); 106 if (value != null) { 107 res.put(meth.name.toString(), value); 108 } else { 109 // Ignore this element. May (properly) lead to 110 // IncompleteAnnotationException somewhere down the line. 111 } 112 } 113 return res; 114 } 115 116 /** 117 * Returns a map from element symbols to their values. 118 * Includes all elements, whether explicit or defaulted. 119 */ 120 private Map<MethodSymbol, Attribute> getAllValues() { 121 Map<MethodSymbol, Attribute> res = new LinkedHashMap<>(); 122 123 // First find the default values. 124 ClassSymbol sym = (ClassSymbol) anno.type.tsym; 125 for (Symbol s : sym.members().getSymbols(NON_RECURSIVE)) { 126 if (s.kind == MTH) { 127 MethodSymbol m = (MethodSymbol) s; 128 Attribute def = m.getDefaultValue(); 129 if (def != null) 130 res.put(m, def); 131 } 132 } 133 // Next find the explicit values, possibly overriding defaults. 134 for (Pair<MethodSymbol, Attribute> p : anno.values) 135 res.put(p.fst, p.snd); 136 return res; 137 } 138 139 /** 140 * Converts an element value to its "dynamic proxy return form". 141 * Returns an exception proxy on some errors, but may return null if 142 * a useful exception cannot or should not be generated at this point. 143 */ 144 private Object generateValue(MethodSymbol meth, Attribute attr) { 145 ValueVisitor vv = new ValueVisitor(meth); 146 return vv.getValue(attr); 147 } 148 149 150 private class ValueVisitor implements Attribute.Visitor { 151 152 private MethodSymbol meth; // annotation element being visited 153 private Class<?> returnClass; // return type of annotation element 154 private Object value; // value in "dynamic proxy return form" 155 156 ValueVisitor(MethodSymbol meth) { 157 this.meth = meth; 158 } 159 160 Object getValue(Attribute attr) { 161 Method method; // runtime method of annotation element 162 try { 163 method = annoType.getMethod(meth.name.toString()); 164 } catch (NoSuchMethodException e) { 165 return null; 166 } 167 returnClass = method.getReturnType(); 168 attr.accept(this); 169 if (!(value instanceof ExceptionProxy) && 170 !AnnotationType.invocationHandlerReturnType(returnClass) 171 .isInstance(value)) { 172 typeMismatch(method, attr); 173 } 174 return value; 175 } 176 177 178 public void visitConstant(Attribute.Constant c) { 179 value = c.getValue(); 180 } 181 182 public void visitClass(Attribute.Class c) { 183 value = new MirroredTypeExceptionProxy(c.classType); 184 } 185 186 public void visitArray(Attribute.Array a) { 187 Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName(); 188 189 if (elemName.equals(elemName.table.names.java_lang_Class)) { // Class[] 190 // Construct a proxy for a MirroredTypesException 191 ListBuffer<TypeMirror> elems = new ListBuffer<>(); 192 for (Attribute value : a.values) { 193 Type elem = ((Attribute.Class) value).classType; 194 elems.append(elem); 195 } 196 value = new MirroredTypesExceptionProxy(elems.toList()); 197 198 } else { 199 int len = a.values.length; 200 Class<?> returnClassSaved = returnClass; 201 returnClass = returnClass.getComponentType(); 202 try { 203 Object res = Array.newInstance(returnClass, len); 204 for (int i = 0; i < len; i++) { 205 a.values[i].accept(this); 206 if (value == null || value instanceof ExceptionProxy) { 207 return; 208 } 209 try { 210 Array.set(res, i, value); 211 } catch (IllegalArgumentException e) { 212 value = null; // indicates a type mismatch 213 return; 214 } 215 } 216 value = res; 217 } finally { 218 returnClass = returnClassSaved; 219 } 220 } 221 } 222 223 @SuppressWarnings({"unchecked", "rawtypes"}) 224 public void visitEnum(Attribute.Enum e) { 225 if (returnClass.isEnum()) { 226 String constName = e.value.toString(); 227 try { 228 value = Enum.valueOf((Class)returnClass, constName); 229 } catch (IllegalArgumentException ex) { 230 value = new EnumConstantNotPresentExceptionProxy( 231 (Class<Enum<?>>) returnClass, constName); 232 } 233 } else { 234 value = null; // indicates a type mismatch 235 } 236 } 237 238 public void visitCompound(Attribute.Compound c) { 239 try { 240 Class<? extends Annotation> nested = 241 returnClass.asSubclass(Annotation.class); 242 value = generateAnnotation(c, nested); 243 } catch (ClassCastException ex) { 244 value = null; // indicates a type mismatch 245 } 246 } 247 248 public void visitError(Attribute.Error e) { 249 if (e instanceof Attribute.UnresolvedClass) 250 value = new MirroredTypeExceptionProxy(((Attribute.UnresolvedClass)e).classType); 251 else 252 value = null; // indicates a type mismatch 253 } 254 255 256 /** 257 * Sets "value" to an ExceptionProxy indicating a type mismatch. 258 */ 259 private void typeMismatch(Method method, final Attribute attr) { 260 class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy { 261 static final long serialVersionUID = 269; 262 transient final Method method; 263 AnnotationTypeMismatchExceptionProxy(Method method) { 264 this.method = method; 265 } 266 public String toString() { 267 return "<error>"; // eg: @Anno(value=<error>) 268 } 269 protected RuntimeException generateException() { 270 return new AnnotationTypeMismatchException(method, 271 attr.type.toString()); 272 } 273 } 274 value = new AnnotationTypeMismatchExceptionProxy(method); 275 } 276 } 277 278 279 /** 280 * ExceptionProxy for MirroredTypeException. 281 * The toString, hashCode, and equals methods forward to the underlying 282 * type. 283 */ 284 private static final class MirroredTypeExceptionProxy extends ExceptionProxy { 285 static final long serialVersionUID = 269; 286 287 private transient TypeMirror type; 288 private final String typeString; 289 290 MirroredTypeExceptionProxy(TypeMirror t) { 291 type = t; 292 typeString = t.toString(); 293 } 294 295 public String toString() { 296 return typeString + ".class"; 297 } 298 299 public int hashCode() { 300 return (type != null ? type : typeString).hashCode(); 301 } 302 303 public boolean equals(Object obj) { 304 return type != null && 305 obj instanceof MirroredTypeExceptionProxy && 306 type.equals(((MirroredTypeExceptionProxy) obj).type); 307 } 308 309 protected RuntimeException generateException() { 310 return new MirroredTypeException(type); 311 } 312 313 // Explicitly set all transient fields. 314 private void readObject(ObjectInputStream s) 315 throws IOException, ClassNotFoundException { 316 s.defaultReadObject(); 317 type = null; 318 } 319 } 320 321 322 /** 323 * ExceptionProxy for MirroredTypesException. 324 * The toString, hashCode, and equals methods foward to the underlying 325 * types. 326 */ 327 private static final class MirroredTypesExceptionProxy extends ExceptionProxy { 328 static final long serialVersionUID = 269; 329 330 private transient List<TypeMirror> types; 331 private final String typeStrings; 332 333 MirroredTypesExceptionProxy(List<TypeMirror> ts) { 334 types = ts; 335 typeStrings = ts.toString(); 336 } 337 338 public String toString() { 339 return types.stream() 340 .map(t -> t.toString() + ".class") 341 .collect(Collectors.joining(", ", "{", "}")); 342 } 343 344 public int hashCode() { 345 return (types != null ? types : typeStrings).hashCode(); 346 } 347 348 public boolean equals(Object obj) { 349 return types != null && 350 obj instanceof MirroredTypesExceptionProxy && 351 types.equals( 352 ((MirroredTypesExceptionProxy) obj).types); 353 } 354 355 protected RuntimeException generateException() { 356 return new MirroredTypesException(types); 357 } 358 359 // Explicitly set all transient fields. 360 private void readObject(ObjectInputStream s) 361 throws IOException, ClassNotFoundException { 362 s.defaultReadObject(); 363 types = null; 364 } 365 } 366 }