Fri, 30 Nov 2012 15:14:36 +0000
8004101: Add checks for method reference well-formedness
Summary: Bring method reference type-checking in sync with latest EDR
Reviewed-by: jjg
1 /*
2 * Copyright (c) 2005, 2012, 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 */
26 package com.sun.tools.javac.model;
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 sun.reflect.annotation.*;
37 import javax.lang.model.type.MirroredTypeException;
38 import javax.lang.model.type.MirroredTypesException;
39 import javax.lang.model.type.TypeMirror;
41 import com.sun.tools.javac.code.*;
42 import com.sun.tools.javac.code.Symbol.*;
43 import com.sun.tools.javac.code.Type.ArrayType;
44 import com.sun.tools.javac.util.*;
47 /**
48 * A generator of dynamic proxy implementations of
49 * java.lang.annotation.Annotation.
50 *
51 * <p> The "dynamic proxy return form" of an annotation element value is
52 * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
53 *
54 * <p><b>This is NOT part of any supported API.
55 * If you write code that depends on this, you do so at your own risk.
56 * This code and its internal interfaces are subject to change or
57 * deletion without notice.</b>
58 */
60 public class AnnotationProxyMaker {
62 private final Attribute.Compound anno;
63 private final Class<? extends Annotation> annoType;
66 private AnnotationProxyMaker(Attribute.Compound anno,
67 Class<? extends Annotation> annoType) {
68 this.anno = anno;
69 this.annoType = annoType;
70 }
73 /**
74 * Returns a dynamic proxy for an annotation mirror.
75 */
76 public static <A extends Annotation> A generateAnnotation(
77 Attribute.Compound anno, Class<A> annoType) {
78 AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType);
79 return annoType.cast(apm.generateAnnotation());
80 }
83 /**
84 * Returns a dynamic proxy for an annotation mirror.
85 */
86 private Annotation generateAnnotation() {
87 return AnnotationParser.annotationForMap(annoType,
88 getAllReflectedValues());
89 }
91 /**
92 * Returns a map from element names to their values in "dynamic
93 * proxy return form". Includes all elements, whether explicit or
94 * defaulted.
95 */
96 private Map<String, Object> getAllReflectedValues() {
97 Map<String, Object> res = new LinkedHashMap<String, Object>();
99 for (Map.Entry<MethodSymbol, Attribute> entry :
100 getAllValues().entrySet()) {
101 MethodSymbol meth = entry.getKey();
102 Object value = generateValue(meth, entry.getValue());
103 if (value != null) {
104 res.put(meth.name.toString(), value);
105 } else {
106 // Ignore this element. May (properly) lead to
107 // IncompleteAnnotationException somewhere down the line.
108 }
109 }
110 return res;
111 }
113 /**
114 * Returns a map from element symbols to their values.
115 * Includes all elements, whether explicit or defaulted.
116 */
117 private Map<MethodSymbol, Attribute> getAllValues() {
118 Map<MethodSymbol, Attribute> res =
119 new LinkedHashMap<MethodSymbol, Attribute>();
121 // First find the default values.
122 ClassSymbol sym = (ClassSymbol) anno.type.tsym;
123 for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) {
124 if (e.sym.kind == Kinds.MTH) {
125 MethodSymbol m = (MethodSymbol) e.sym;
126 Attribute def = m.getDefaultValue();
127 if (def != null)
128 res.put(m, def);
129 }
130 }
131 // Next find the explicit values, possibly overriding defaults.
132 for (Pair<MethodSymbol, Attribute> p : anno.values)
133 res.put(p.fst, p.snd);
134 return res;
135 }
137 /**
138 * Converts an element value to its "dynamic proxy return form".
139 * Returns an exception proxy on some errors, but may return null if
140 * a useful exception cannot or should not be generated at this point.
141 */
142 private Object generateValue(MethodSymbol meth, Attribute attr) {
143 ValueVisitor vv = new ValueVisitor(meth);
144 return vv.getValue(attr);
145 }
148 private class ValueVisitor implements Attribute.Visitor {
150 private MethodSymbol meth; // annotation element being visited
151 private Class<?> returnClass; // return type of annotation element
152 private Object value; // value in "dynamic proxy return form"
154 ValueVisitor(MethodSymbol meth) {
155 this.meth = meth;
156 }
158 Object getValue(Attribute attr) {
159 Method method; // runtime method of annotation element
160 try {
161 method = annoType.getMethod(meth.name.toString());
162 } catch (NoSuchMethodException e) {
163 return null;
164 }
165 returnClass = method.getReturnType();
166 attr.accept(this);
167 if (!(value instanceof ExceptionProxy) &&
168 !AnnotationType.invocationHandlerReturnType(returnClass)
169 .isInstance(value)) {
170 typeMismatch(method, attr);
171 }
172 return value;
173 }
176 public void visitConstant(Attribute.Constant c) {
177 value = c.getValue();
178 }
180 public void visitClass(Attribute.Class c) {
181 value = new MirroredTypeExceptionProxy(c.classType);
182 }
184 public void visitArray(Attribute.Array a) {
185 Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName();
187 if (elemName.equals(elemName.table.names.java_lang_Class)) { // Class[]
188 // Construct a proxy for a MirroredTypesException
189 ListBuffer<TypeMirror> elems = new ListBuffer<TypeMirror>();
190 for (Attribute value : a.values) {
191 Type elem = ((Attribute.Class) value).classType;
192 elems.append(elem);
193 }
194 value = new MirroredTypesExceptionProxy(elems.toList());
196 } else {
197 int len = a.values.length;
198 Class<?> returnClassSaved = returnClass;
199 returnClass = returnClass.getComponentType();
200 try {
201 Object res = Array.newInstance(returnClass, len);
202 for (int i = 0; i < len; i++) {
203 a.values[i].accept(this);
204 if (value == null || value instanceof ExceptionProxy) {
205 return;
206 }
207 try {
208 Array.set(res, i, value);
209 } catch (IllegalArgumentException e) {
210 value = null; // indicates a type mismatch
211 return;
212 }
213 }
214 value = res;
215 } finally {
216 returnClass = returnClassSaved;
217 }
218 }
219 }
221 @SuppressWarnings({"unchecked", "rawtypes"})
222 public void visitEnum(Attribute.Enum e) {
223 if (returnClass.isEnum()) {
224 String constName = e.value.toString();
225 try {
226 value = Enum.valueOf((Class)returnClass, constName);
227 } catch (IllegalArgumentException ex) {
228 value = new EnumConstantNotPresentExceptionProxy(
229 (Class<Enum<?>>) returnClass, constName);
230 }
231 } else {
232 value = null; // indicates a type mismatch
233 }
234 }
236 public void visitCompound(Attribute.Compound c) {
237 try {
238 Class<? extends Annotation> nested =
239 returnClass.asSubclass(Annotation.class);
240 value = generateAnnotation(c, nested);
241 } catch (ClassCastException ex) {
242 value = null; // indicates a type mismatch
243 }
244 }
246 public void visitError(Attribute.Error e) {
247 value = null; // indicates a type mismatch
248 }
251 /**
252 * Sets "value" to an ExceptionProxy indicating a type mismatch.
253 */
254 private void typeMismatch(Method method, final Attribute attr) {
255 class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy {
256 static final long serialVersionUID = 269;
257 transient final Method method;
258 AnnotationTypeMismatchExceptionProxy(Method method) {
259 this.method = method;
260 }
261 public String toString() {
262 return "<error>"; // eg: @Anno(value=<error>)
263 }
264 protected RuntimeException generateException() {
265 return new AnnotationTypeMismatchException(method,
266 attr.type.toString());
267 }
268 }
269 value = new AnnotationTypeMismatchExceptionProxy(method);
270 }
271 }
274 /**
275 * ExceptionProxy for MirroredTypeException.
276 * The toString, hashCode, and equals methods foward to the underlying
277 * type.
278 */
279 private static final class MirroredTypeExceptionProxy extends ExceptionProxy {
280 static final long serialVersionUID = 269;
282 private transient TypeMirror type;
283 private final String typeString;
285 MirroredTypeExceptionProxy(TypeMirror t) {
286 type = t;
287 typeString = t.toString();
288 }
290 public String toString() {
291 return typeString;
292 }
294 public int hashCode() {
295 return (type != null ? type : typeString).hashCode();
296 }
298 public boolean equals(Object obj) {
299 return type != null &&
300 obj instanceof MirroredTypeExceptionProxy &&
301 type.equals(((MirroredTypeExceptionProxy) obj).type);
302 }
304 protected RuntimeException generateException() {
305 return new MirroredTypeException(type);
306 }
308 // Explicitly set all transient fields.
309 private void readObject(ObjectInputStream s)
310 throws IOException, ClassNotFoundException {
311 s.defaultReadObject();
312 type = null;
313 }
314 }
317 /**
318 * ExceptionProxy for MirroredTypesException.
319 * The toString, hashCode, and equals methods foward to the underlying
320 * types.
321 */
322 private static final class MirroredTypesExceptionProxy extends ExceptionProxy {
323 static final long serialVersionUID = 269;
325 private transient List<TypeMirror> types;
326 private final String typeStrings;
328 MirroredTypesExceptionProxy(List<TypeMirror> ts) {
329 types = ts;
330 typeStrings = ts.toString();
331 }
333 public String toString() {
334 return typeStrings;
335 }
337 public int hashCode() {
338 return (types != null ? types : typeStrings).hashCode();
339 }
341 public boolean equals(Object obj) {
342 return types != null &&
343 obj instanceof MirroredTypesExceptionProxy &&
344 types.equals(
345 ((MirroredTypesExceptionProxy) obj).types);
346 }
348 protected RuntimeException generateException() {
349 return new MirroredTypesException(types);
350 }
352 // Explicitly set all transient fields.
353 private void readObject(ObjectInputStream s)
354 throws IOException, ClassNotFoundException {
355 s.defaultReadObject();
356 types = null;
357 }
358 }
359 }