1 /*
   2  * Copyright (c) 2003, 2016, 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 sun.reflect.annotation;
  27 
  28 import jdk.internal.vm.annotation.Stable;
  29 
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.io.ObjectStreamField;
  33 import java.io.Serializable;
  34 import java.lang.annotation.Annotation;
  35 import java.lang.annotation.AnnotationFormatError;
  36 import java.lang.annotation.IncompleteAnnotationException;
  37 import java.lang.invoke.VarHandle;
  38 import java.lang.reflect.Array;
  39 import java.lang.reflect.InvocationHandler;
  40 import java.lang.reflect.InvocationTargetException;
  41 import java.lang.reflect.Method;
  42 import java.lang.reflect.Proxy;
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.LinkedHashMap;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.Objects;
  49 import java.util.stream.Collectors;
  50 import java.util.stream.DoubleStream;
  51 import java.util.stream.IntStream;
  52 import java.util.stream.LongStream;
  53 import java.util.stream.Stream;
  54 
  55 /**
  56  * InvocationHandler for dynamic proxy implementation of Annotation.
  57  *
  58  * @author  Josh Bloch
  59  * @since   1.5
  60  */
  61 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
  62     private static final long serialVersionUID = 6182022883658399397L;
  63 
  64     /**
  65      * Serialized pseudo-fields, provided for serialization compatibility.
  66      * @serialField type The annotation type.
  67      * @serialField memberValues The member values as LinkedHashMap.
  68      */
  69     private static final ObjectStreamField[] serialPersistentFields = {
  70         new ObjectStreamField("type", Class.class),
  71         new ObjectStreamField("memberValues", Map.class)
  72     };
  73 
  74     @Stable
  75     private transient Class<? extends Annotation> type;
  76     @Stable
  77     private transient Object[] valuesTable;
  78     @Stable
  79     private transient int[] keysIndex;
  80 
  81     AnnotationInvocationHandler(Class<? extends Annotation> type,
  82                                 Map<String, Object> memberValues) {
  83 
  84         // there will never be an extraneous value in the given map since
  85         // parser already skips values that are not members of the annotation type
  86         // and so does deserialization (see readObject)
  87         assert AnnotationType.getInstance(type).members().keySet()
  88                              .containsAll(memberValues.keySet());
  89 
  90         this.type = type;
  91         this.valuesTable = toLinearProbeHashTable(
  92             memberValues,
  93             this.keysIndex = new int[memberValues.size()]
  94         );
  95 
  96         // analogue to "freeze" action for final fields
  97         VarHandle.releaseFence();
  98     }
  99 
 100     public Object invoke(Object proxy, Method method, Object[] args) {
 101 
 102         String memberName = method.getName(); // guaranteed interned String
 103         Class<?> dtype = method.getDeclaringClass();
 104 
 105         if (dtype == Object.class ||  // equals/hashCode/toString
 106             dtype == Annotation.class // annotationType
 107             ) {
 108             if (memberName == "equals") return equalsImpl(proxy, args[0]);
 109             if (memberName == "hashCode") return hashCodeImpl();
 110             if (memberName == "annotationType") return type;
 111             if (memberName == "toString") return toStringImpl();
 112             throw new AssertionError("Invalid method: " + method);
 113         }
 114 
 115         assert dtype == type;
 116         assert method.getParameterCount() == 0;
 117 
 118         return getValue(memberName);
 119     }
 120 
 121     /**
 122      * Returns a value for given interned member name.
 123      *
 124      * @param internedName an interned String containing the name of the member
 125      *                     to retrieve.
 126      * @throws IncompleteAnnotationException if invoked for annotation not
 127      *                                       containing given name.
 128      */
 129     Object getValue(String internedName) {
 130         assert internedName.intern() == internedName;
 131 
 132         Object result = getValue(valuesTable, internedName);
 133 
 134         if (result == null)
 135             throw new IncompleteAnnotationException(type, internedName);
 136 
 137         if (result instanceof ExceptionProxy)
 138             throw ((ExceptionProxy) result).generateException();
 139 
 140         if (result.getClass().isArray() && Array.getLength(result) != 0)
 141             result = cloneArray(result);
 142 
 143         return result;
 144     }
 145 
 146     /**
 147      * This method, which clones its array argument, would not be necessary
 148      * if Cloneable had a public clone method.
 149      */
 150     private Object cloneArray(Object array) {
 151         Class<?> type = array.getClass();
 152 
 153         if (type == byte[].class) {
 154             byte[] byteArray = (byte[])array;
 155             return byteArray.clone();
 156         }
 157         if (type == char[].class) {
 158             char[] charArray = (char[])array;
 159             return charArray.clone();
 160         }
 161         if (type == double[].class) {
 162             double[] doubleArray = (double[])array;
 163             return doubleArray.clone();
 164         }
 165         if (type == float[].class) {
 166             float[] floatArray = (float[])array;
 167             return floatArray.clone();
 168         }
 169         if (type == int[].class) {
 170             int[] intArray = (int[])array;
 171             return intArray.clone();
 172         }
 173         if (type == long[].class) {
 174             long[] longArray = (long[])array;
 175             return longArray.clone();
 176         }
 177         if (type == short[].class) {
 178             short[] shortArray = (short[])array;
 179             return shortArray.clone();
 180         }
 181         if (type == boolean[].class) {
 182             boolean[] booleanArray = (boolean[])array;
 183             return booleanArray.clone();
 184         }
 185 
 186         Object[] objectArray = (Object[])array;
 187         return objectArray.clone();
 188     }
 189 
 190 
 191     /**
 192      * Implementation of dynamicProxy.toString()
 193      */
 194     private String toStringImpl() {
 195         StringBuilder result = new StringBuilder(128);
 196         result.append('@');
 197         result.append(type.getName());
 198         result.append('(');
 199         boolean firstMember = true;
 200         for (int i : keysIndex) {
 201             if (firstMember)
 202                 firstMember = false;
 203             else
 204                 result.append(", ");
 205 
 206             result.append(valuesTable[i]);
 207             result.append('=');
 208             result.append(memberValueToString(valuesTable[i + 1]));
 209         }
 210         result.append(')');
 211         return result.toString();
 212     }
 213 
 214     /**
 215      * Translates a member value (in "dynamic proxy return form") into a string.
 216      */
 217     private static String memberValueToString(Object value) {
 218         Class<?> type = value.getClass();
 219         if (!type.isArray()) {
 220             // primitive value, string, class, enum const, or annotation
 221             if (type == Class.class)
 222                 return toSourceString((Class<?>) value);
 223             else if (type == String.class)
 224                 return  toSourceString((String) value);
 225             if (type == Character.class)
 226                 return toSourceString((char) value);
 227             else if (type == Double.class)
 228                 return  toSourceString((double) value);
 229             else if (type == Float.class)
 230                 return  toSourceString((float) value);
 231             else if (type == Long.class)
 232                 return  toSourceString((long) value);
 233             else
 234                 return value.toString();
 235         } else {
 236             Stream<String> stringStream;
 237             if (type == byte[].class)
 238                 stringStream = convert((byte[]) value);
 239             else if (type == char[].class)
 240                 stringStream = convert((char[]) value);
 241             else if (type == double[].class)
 242                 stringStream = DoubleStream.of((double[]) value)
 243                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 244             else if (type == float[].class)
 245                 stringStream = convert((float[]) value);
 246             else if (type == int[].class)
 247                 stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);
 248             else if (type == long[].class) {
 249                 stringStream = LongStream.of((long[]) value)
 250                     .mapToObj(AnnotationInvocationHandler::toSourceString);
 251             } else if (type == short[].class)
 252                 stringStream = convert((short[]) value);
 253             else if (type == boolean[].class)
 254                 stringStream = convert((boolean[]) value);
 255             else if (type == Class[].class)
 256                 stringStream =
 257                     Arrays.stream((Class<?>[]) value).
 258                     map(AnnotationInvocationHandler::toSourceString);
 259             else if (type == String[].class)
 260                 stringStream =
 261                     Arrays.stream((String[])value).
 262                     map(AnnotationInvocationHandler::toSourceString);
 263             else
 264                 stringStream = Arrays.stream((Object[])value).map(Objects::toString);
 265 
 266             return stringStreamToString(stringStream);
 267         }
 268     }
 269 
 270     /**
 271      * Translates a Class value to a form suitable for use in the
 272      * string representation of an annotation.
 273      */
 274     private static String toSourceString(Class<?> clazz) {
 275         Class<?> finalComponent = clazz;
 276         StringBuilder arrayBackets = new StringBuilder();
 277 
 278         while(finalComponent.isArray()) {
 279             finalComponent = finalComponent.getComponentType();
 280             arrayBackets.append("[]");
 281         }
 282 
 283         return finalComponent.getName() + arrayBackets.toString() + ".class" ;
 284     }
 285 
 286     private static String toSourceString(float f) {
 287         if (Float.isFinite(f))
 288             return Float.toString(f) + "f" ;
 289         else {
 290             if (Float.isInfinite(f)) {
 291                 return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";
 292             } else
 293                 return "0.0f/0.0f";
 294         }
 295     }
 296 
 297     private static String toSourceString(double d) {
 298         if (Double.isFinite(d))
 299             return Double.toString(d);
 300         else {
 301             if (Double.isInfinite(d)) {
 302                 return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";
 303             } else
 304                 return "0.0/0.0";
 305         }
 306     }
 307 
 308     private static String toSourceString(char c) {
 309         StringBuilder sb = new StringBuilder(4);
 310         sb.append('\'');
 311         if (c == '\'')
 312             sb.append("\\'");
 313         else
 314             sb.append(c);
 315         return sb.append('\'')
 316                 .toString();
 317     }
 318 
 319     private static String toSourceString(long ell) {
 320         String str = String.valueOf(ell);
 321         return (ell < Integer.MIN_VALUE || ell > Integer.MAX_VALUE)
 322                 ? (str + 'L') : str;
 323     }
 324 
 325     /**
 326      * Return a string suitable for use in the string representation
 327      * of an annotation.
 328      */
 329     private static String toSourceString(String s) {
 330         StringBuilder sb = new StringBuilder();
 331         sb.append('"');
 332         // Escape embedded quote characters, if present, but don't do
 333         // anything more heroic.
 334         sb.append(s.replace("\"", "\\\""));
 335         sb.append('"');
 336         return sb.toString();
 337     }
 338 
 339     private static Stream<String> convert(byte[] values) {
 340         List<String> list = new ArrayList<>(values.length);
 341         for (byte b : values)
 342             list.add(Byte.toString(b));
 343         return list.stream();
 344     }
 345 
 346     private static Stream<String> convert(char[] values) {
 347         List<String> list = new ArrayList<>(values.length);
 348         for (char c : values)
 349             list.add(toSourceString(c));
 350         return list.stream();
 351     }
 352 
 353     private static Stream<String> convert(float[] values) {
 354         List<String> list = new ArrayList<>(values.length);
 355         for (float f : values) {
 356             list.add(toSourceString(f));
 357         }
 358         return list.stream();
 359     }
 360 
 361     private static Stream<String> convert(short[] values) {
 362         List<String> list = new ArrayList<>(values.length);
 363         for (short s : values)
 364             list.add(Short.toString(s));
 365         return list.stream();
 366     }
 367 
 368     private static Stream<String> convert(boolean[] values) {
 369         List<String> list = new ArrayList<>(values.length);
 370         for (boolean b : values)
 371             list.add(Boolean.toString(b));
 372         return list.stream();
 373     }
 374 
 375     private static String stringStreamToString(Stream<String> stream) {
 376         return stream.collect(Collectors.joining(", ", "{", "}"));
 377     }
 378 
 379     /**
 380      * Implementation of dynamicProxy.equals(Object o)
 381      */
 382     private Boolean equalsImpl(Object proxy, Object o) {
 383         if (o == proxy)
 384             return true;
 385 
 386         if (!type.isInstance(o))
 387             return false;
 388 
 389         AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 390 
 391         if (hisHandler != null) { // One Of Us
 392             if (valuesTable.length != hisHandler.valuesTable.length) {
 393                 return false;
 394             }
 395             for (int i = 0; i < valuesTable.length; i+=2) {
 396                 String k = (String) valuesTable[i];
 397                 if (k != null) {
 398                     Object value = valuesTable[i+1];
 399                     Object hisValue = getValue(hisHandler.valuesTable, k);
 400                     if (!memberValueEquals(value, hisValue)) {
 401                         return false;
 402                     }
 403                 }
 404             }
 405         } else {
 406             for (Method accMember : AnnotationType.getInstance(type)
 407                                                   .accessibleMembers().values()) {
 408                 String k = accMember.getName(); // guaranteed interned string
 409                 Object value = getValue(valuesTable, k);
 410                 Object hisValue;
 411                 try {
 412                     hisValue = accMember.invoke(o);
 413                 } catch (InvocationTargetException ex) {
 414                     if (ex.getTargetException() instanceof IncompleteAnnotationException) {
 415                         hisValue = null;
 416                     } else {
 417                         return false;
 418                     }
 419                 } catch (IllegalAccessException ex) {
 420                     throw new AssertionError(ex);
 421                 }
 422                 if (!memberValueEquals(value, hisValue)) {
 423                     return false;
 424                 }
 425             }
 426         }
 427         return true;
 428     }
 429 
 430     /**
 431      * Returns an object's invocation handler if that object is a dynamic
 432      * proxy with a handler of type AnnotationInvocationHandler.
 433      * Returns null otherwise.
 434      */
 435     static AnnotationInvocationHandler asOneOfUs(Object o) {
 436         if (Proxy.isProxyClass(o.getClass())) {
 437             InvocationHandler handler = Proxy.getInvocationHandler(o);
 438             if (handler instanceof AnnotationInvocationHandler)
 439                 return (AnnotationInvocationHandler) handler;
 440         }
 441         return null;
 442     }
 443 
 444     /**
 445      * Returns true iff the two member values in "dynamic proxy return form"
 446      * are equal using the appropriate equality function depending on the
 447      * member type.  The two values will be of the same type unless one of
 448      * the containing annotations is ill-formed.  If one of the containing
 449      * annotations is ill-formed, this method will return false unless the
 450      * two members are identical object references.
 451      */
 452     private static boolean memberValueEquals(Object v1, Object v2) {
 453         if (v1 == v2) {
 454             return true;
 455         }
 456         if (v1 == null || v2 == null) {
 457             return false;
 458         }
 459 
 460         Class<?> type = v1.getClass();
 461 
 462         // Check for primitive, string, class, enum const, annotation,
 463         // or ExceptionProxy
 464         if (!type.isArray())
 465             return v1.equals(v2);
 466 
 467         // Check for array of string, class, enum const, annotation,
 468         // or ExceptionProxy
 469         if (v1 instanceof Object[] && v2 instanceof Object[])
 470             return Arrays.equals((Object[]) v1, (Object[]) v2);
 471 
 472         // Check for ill formed annotation(s)
 473         if (v2.getClass() != type)
 474             return false;
 475 
 476         // Deal with array of primitives
 477         if (type == byte[].class)
 478             return Arrays.equals((byte[]) v1, (byte[]) v2);
 479         if (type == char[].class)
 480             return Arrays.equals((char[]) v1, (char[]) v2);
 481         if (type == double[].class)
 482             return Arrays.equals((double[]) v1, (double[]) v2);
 483         if (type == float[].class)
 484             return Arrays.equals((float[]) v1, (float[]) v2);
 485         if (type == int[].class)
 486             return Arrays.equals((int[]) v1, (int[]) v2);
 487         if (type == long[].class)
 488             return Arrays.equals((long[]) v1, (long[]) v2);
 489         if (type == short[].class)
 490             return Arrays.equals((short[]) v1, (short[]) v2);
 491         assert type == boolean[].class;
 492         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 493     }
 494 
 495     /**
 496      * Implementation of dynamicProxy.hashCode()
 497      */
 498     private int hashCodeImpl() {
 499         int result = 0;
 500         for (int i = 0; i < valuesTable.length; i+=2) {
 501             Object k = valuesTable[i];
 502             if (k != null) {
 503                 result += (127 * k.hashCode()) ^
 504                           memberValueHashCode(valuesTable[i+1]);
 505             }
 506         }
 507         return result;
 508     }
 509 
 510     /**
 511      * Computes hashCode of a member value (in "dynamic proxy return form")
 512      */
 513     private static int memberValueHashCode(Object value) {
 514         Class<?> type = value.getClass();
 515         if (!type.isArray())    // primitive, string, class, enum const,
 516                                 // or annotation
 517             return value.hashCode();
 518 
 519         if (type == byte[].class)
 520             return Arrays.hashCode((byte[]) value);
 521         if (type == char[].class)
 522             return Arrays.hashCode((char[]) value);
 523         if (type == double[].class)
 524             return Arrays.hashCode((double[]) value);
 525         if (type == float[].class)
 526             return Arrays.hashCode((float[]) value);
 527         if (type == int[].class)
 528             return Arrays.hashCode((int[]) value);
 529         if (type == long[].class)
 530             return Arrays.hashCode((long[]) value);
 531         if (type == short[].class)
 532             return Arrays.hashCode((short[]) value);
 533         if (type == boolean[].class)
 534             return Arrays.hashCode((boolean[]) value);
 535         return Arrays.hashCode((Object[]) value);
 536     }
 537 
 538     /**
 539      * Creates an open-addressing liner-probe hash table to hold given size
 540      * entries.
 541      */
 542     private static Object[] newLinearProbeHashTable(int size) {
 543         // capacity = smallest power of 2 greater than or equal to size * 3 / 2
 544         int cap = Integer.highestOneBit(3 * Math.max(1, size));
 545         // table length = 2 * capacity (2 slots per (key, value) entry)
 546         return new Object[2 * cap];
 547     }
 548 
 549      /**
 550      * Creates an open-addressing liner-probe hash table from given valuesMap
 551      * and fills the given keysIndex array with a sequence of indices into the
 552      * created table that point to keys in valuesMap iteration order.
 553      */
 554     private static Object[] toLinearProbeHashTable(Map<String, Object> valuesMap,
 555                                                    int[] keysIndex) {
 556         assert valuesMap.size() == keysIndex.length;
 557         Object[] table = newLinearProbeHashTable(valuesMap.size());
 558         int size = 0;
 559         for (Map.Entry<String, Object> e : valuesMap.entrySet()) {
 560             size = putValue(table, keysIndex, size, e.getKey(), e.getValue());
 561         }
 562         return table;
 563     }
 564 
 565     /**
 566      * Inserts new (key, val) entry into given open-addressing liner-probe hash
 567      * table, and adds new key index into given keysIndex at given size position.
 568      * The assumption is that the table does not already contain given key.
 569      * @return incremented size.
 570      */
 571     private static int putValue(Object[] table, int[] keysIndex, int size,
 572                                 String key, Object val) {
 573         // keys are interned strings to speed-up lookup since we already
 574         // have an interned string to match in lookup (from Method.getName())
 575         key = key.intern();
 576         int i = firstKeyIndex(key, table.length);
 577         while (table[i] != null) {
 578             assert table[i] != key;
 579             i = nextKeyIndex(i, table.length);
 580         }
 581         keysIndex[size] = i;
 582         table[i] = key;
 583         table[i + 1] = val;
 584         return size + 1;
 585     }
 586 
 587     /**
 588      * Creates a LinkedHashMap from given open-addressing liner-probe hash table
 589      * and given keysIndex array holding a sequence of indices into table
 590      * in order of insertion into returned LinkedHashMap.
 591      */
 592     private static LinkedHashMap<String, Object> toLinkedHashMap(Object[] table,
 593                                                                  int[] keysIndex) {
 594         LinkedHashMap<String, Object> map = new LinkedHashMap<>(keysIndex.length + 1, 1.0f);
 595         for (int i : keysIndex) {
 596             map.put((String) table[i], table[i + 1]);
 597         }
 598         return map;
 599     }
 600 
 601     /**
 602      * Looks-up a value in an open-addressing liner-probe hash table by given
 603      * interned String key.
 604      */
 605     private static Object getValue(Object[] table, String internedKey) {
 606         int i = firstKeyIndex(internedKey, table.length);
 607         Object k;
 608         while ((k = table[i]) != null) {
 609             if (k == internedKey) {
 610                 return table[i+1];
 611             } else {
 612                 i = nextKeyIndex(i, table.length);
 613             }
 614         }
 615         return null;
 616     }
 617 
 618     /**
 619      * Returns index for Object x.
 620      */
 621     private static int firstKeyIndex(Object x, int length) {
 622         int h = System.identityHashCode(x);
 623         // Multiply by -127, and left-shift to use least bit as part of hash
 624         return ((h << 1) - (h << 8)) & (length - 1);
 625     }
 626 
 627     /**
 628      * Circularly traverses table of power of 2 size length.
 629      */
 630     private static int nextKeyIndex(int i, int length) {
 631         return (i + 2) & (length - 1);
 632     }
 633 
 634     private void writeObject(java.io.ObjectOutputStream s)
 635         throws java.io.IOException {
 636         ObjectOutputStream.PutField fields = s.putFields();
 637         fields.put("type", type);
 638         fields.put("memberValues", toLinkedHashMap(valuesTable, keysIndex));
 639         s.writeFields();
 640     }
 641 
 642     private void readObject(java.io.ObjectInputStream s)
 643         throws java.io.IOException, ClassNotFoundException {
 644         ObjectInputStream.GetField fields = s.readFields();
 645 
 646         @SuppressWarnings("unchecked")
 647         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 648         @SuppressWarnings("unchecked")
 649         Map<String, Object> memberValues = (Map<String, Object>)fields.get("memberValues", null);
 650 
 651         // Check to make sure that types have not evolved incompatibly
 652         AnnotationType annotationType;
 653         try {
 654             annotationType = AnnotationType.getInstance(t);
 655         } catch(AnnotationFormatError e) {
 656             // Class is no longer an annotation type; time to punch out
 657             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 658         }
 659 
 660         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 661 
 662         // just keep values that have a corresponding member, remove the rest...
 663         memberValues.keySet().retainAll(memberTypes.keySet());
 664 
 665         Object[] table = newLinearProbeHashTable(memberValues.size());
 666         int[] keysIndex = new int[memberValues.size()];
 667         int size = 0;
 668 
 669         // If there are annotation members without values, that
 670         // situation is handled by the invoke method.
 671         for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
 672             String name = memberValue.getKey();
 673             Object value = null;
 674             Class<?> memberType = memberTypes.get(name);
 675             if (memberType != null) {  // i.e. member still exists
 676                 value = memberValue.getValue();
 677                 if (!(memberType.isInstance(value) ||
 678                       value instanceof ExceptionProxy)) {
 679                     value = new AnnotationTypeMismatchExceptionProxy(
 680                             value.getClass() + "[" + value + "]").setMember(
 681                                 annotationType.members().get(name));
 682                 }
 683             }
 684             size = putValue(table, keysIndex, size, name, value);
 685         }
 686 
 687         this.type = t;
 688         this.valuesTable = table;
 689         this.keysIndex = keysIndex;
 690     }
 691 }