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 }