1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.io; 19 20 import java.lang.ref.SoftReference; 21 import java.lang.reflect.Constructor; 22 import java.lang.reflect.Field; 23 import java.lang.reflect.Method; 24 import java.lang.reflect.Modifier; 25 import java.lang.reflect.Proxy; 26 import java.nio.ByteOrder; 27 import java.security.MessageDigest; 28 import java.security.NoSuchAlgorithmException; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Comparator; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.WeakHashMap; 35 import libcore.io.Memory; 36 import libcore.util.EmptyArray; 37 38 /** 39 * Represents a descriptor for identifying a class during serialization and 40 * deserialization. Information contained in the descriptor includes the name 41 * and SUID of the class as well as field names and types. Information inherited 42 * from the superclasses is also taken into account. 43 * 44 * @see ObjectOutputStream 45 * @see ObjectInputStream 46 * @see java.lang.Class 47 */ 48 public class ObjectStreamClass implements Serializable { 49 50 // No need to compute the SUID for ObjectStreamClass, just use the value 51 // below 52 private static final long serialVersionUID = -6120832682080437368L; 53 54 // Name of the field that contains the SUID value (if present) 55 private static final String UID_FIELD_NAME = "serialVersionUID"; 56 57 static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1; 58 59 private static final int CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL | 60 Modifier.INTERFACE | Modifier.ABSTRACT; 61 62 private static final int FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 63 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | 64 Modifier.TRANSIENT; 65 66 private static final int METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 67 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED | 68 Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT; 69 70 private static final Class<?>[] READ_PARAM_TYPES = new Class[] { ObjectInputStream.class }; 71 private static final Class<?>[] WRITE_PARAM_TYPES = new Class[] { ObjectOutputStream.class }; 72 73 /** 74 * Constant indicating that the class has no Serializable fields. 75 */ 76 public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0]; 77 78 /* 79 * used to fetch field serialPersistentFields and checking its type 80 */ 81 static final Class<?> ARRAY_OF_FIELDS; 82 83 static { 84 try { 85 ARRAY_OF_FIELDS = Class.forName("[Ljava.io.ObjectStreamField;"); 86 } catch (ClassNotFoundException e) { 87 // This should not happen 88 throw new AssertionError(e); 89 } 90 } 91 92 private static final String CLINIT_NAME = "<clinit>"; 93 94 private static final int CLINIT_MODIFIERS = Modifier.STATIC; 95 96 private static final String CLINIT_SIGNATURE = "()V"; 97 98 // Used to determine if an object is Serializable or Externalizable 99 private static final Class<Serializable> SERIALIZABLE = Serializable.class; 100 101 private static final Class<Externalizable> EXTERNALIZABLE = Externalizable.class; 102 103 // Used to test if the object is a String or a class. 104 static final Class<String> STRINGCLASS = String.class; 105 106 static final Class<?> CLASSCLASS = Class.class; 107 108 static final Class<ObjectStreamClass> OBJECTSTREAMCLASSCLASS = ObjectStreamClass.class; 109 110 private transient Method methodWriteReplace; 111 112 private transient Method methodReadResolve; 113 114 private transient Method methodWriteObject; 115 116 private transient Method methodReadObject; 117 118 private transient Method methodReadObjectNoData; 119 120 /** 121 * Indicates whether the class properties resolved 122 * 123 * @see #resolveProperties() 124 */ 125 private transient boolean arePropertiesResolved; 126 127 /** 128 * Cached class properties 129 * 130 * @see #resolveProperties() 131 * @see #isSerializable() 132 * @see #isExternalizable() 133 * @see #isProxy() 134 * @see #isEnum() 135 */ 136 private transient boolean isSerializable; 137 private transient boolean isExternalizable; 138 private transient boolean isProxy; 139 private transient boolean isEnum; 140 141 // ClassDesc // 142 143 // Name of the class this descriptor represents 144 private transient String className; 145 146 // Corresponding loaded class with the name above 147 private transient Class<?> resolvedClass; 148 149 private transient Class<?> resolvedConstructorClass; 150 private transient long resolvedConstructorMethodId; 151 152 // Serial version UID of the class the descriptor represents 153 private transient long svUID; 154 155 // ClassDescInfo // 156 157 // Any combination of SC_WRITE_METHOD, SC_SERIALIZABLE and SC_EXTERNALIZABLE 158 // (see ObjectStreamConstants) 159 private transient byte flags; 160 161 // Descriptor for the superclass of the class associated with this 162 // descriptor 163 private transient ObjectStreamClass superclass; 164 165 // Array of ObjectStreamField (see below) describing the fields of this 166 // class 167 private transient ObjectStreamField[] fields; 168 169 // Array of ObjectStreamField describing the serialized fields of this class 170 private transient ObjectStreamField[] loadFields; 171 172 // ObjectStreamField doesn't override hashCode or equals, so this is equivalent to an 173 // IdentityHashMap, which is fine for our purposes. 174 private transient HashMap<ObjectStreamField, Field> reflectionFields = 175 new HashMap<ObjectStreamField, Field>(); 176 177 // MethodID for deserialization constructor 178 private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED; 179 setConstructor(long newConstructor)180 void setConstructor(long newConstructor) { 181 constructor = newConstructor; 182 } 183 getConstructor()184 long getConstructor() { 185 return constructor; 186 } 187 188 /** 189 * Returns the {@link Field} referred to by {@link ObjectStreamField} for the class described by 190 * this {@link ObjectStreamClass}. A {@code null} value is returned if the local definition of 191 * the field does not meet the criteria for a serializable / deserializable field, i.e. the 192 * field must be non-static and non-transient. Caching of each field lookup is performed. The 193 * first time a field is returned it is made accessible with a call to 194 * {@link Field#setAccessible(boolean)}. 195 */ checkAndGetReflectionField(ObjectStreamField osf)196 Field checkAndGetReflectionField(ObjectStreamField osf) { 197 synchronized (reflectionFields) { 198 Field field = reflectionFields.get(osf); 199 // null might indicate a cache miss or a hit and a non-serializable field so we 200 // check for a mapping. 201 if (field != null || reflectionFields.containsKey(osf)) { 202 return field; 203 } 204 } 205 206 Field field; 207 try { 208 Class<?> declaringClass = forClass(); 209 field = declaringClass.getDeclaredField(osf.getName()); 210 211 int modifiers = field.getModifiers(); 212 if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { 213 // No serialization or deserialization of transient or static fields! 214 // See http://b/4471249 and http://b/17202597. 215 field = null; 216 } else { 217 field.setAccessible(true); 218 } 219 } catch (NoSuchFieldException ex) { 220 // The caller messed up. We'll return null and won't try to resolve this again. 221 field = null; 222 } 223 224 synchronized (reflectionFields) { 225 reflectionFields.put(osf, field); 226 } 227 return field; 228 } 229 230 /* 231 * If an ObjectStreamClass describes an Externalizable class, it (the 232 * descriptor) should not have field descriptors (ObjectStreamField) at all. 233 * The ObjectStreamClass that gets saved should simply have no field info. 234 * This is a footnote in page 1511 (class Serializable) of "The Java Class 235 * Libraries, Second Edition, Vol. I". 236 */ 237 238 /** 239 * Constructs a new instance of this class. 240 */ ObjectStreamClass()241 ObjectStreamClass() { 242 } 243 244 /** 245 * Compute class descriptor for a given class <code>cl</code>. 246 * 247 * @param cl 248 * a java.langClass for which to compute the corresponding 249 * descriptor 250 * @return the computer class descriptor 251 */ createClassDesc(Class<?> cl)252 private static ObjectStreamClass createClassDesc(Class<?> cl) { 253 254 ObjectStreamClass result = new ObjectStreamClass(); 255 256 boolean isArray = cl.isArray(); 257 boolean serializable = isSerializable(cl); 258 boolean externalizable = isExternalizable(cl); 259 260 result.isSerializable = serializable; 261 result.isExternalizable = externalizable; 262 263 // Now we fill in the values 264 result.setName(cl.getName()); 265 result.setClass(cl); 266 Class<?> superclass = cl.getSuperclass(); 267 if (superclass != null) { 268 result.setSuperclass(lookup(superclass)); 269 } 270 271 Field[] declaredFields = null; 272 273 // Compute the SUID 274 if (serializable || externalizable) { 275 if (result.isEnum() || result.isProxy()) { 276 result.setSerialVersionUID(0L); 277 } else { 278 declaredFields = cl.getDeclaredFields(); 279 result.setSerialVersionUID(computeSerialVersionUID(cl, declaredFields)); 280 } 281 } 282 283 // Serializables need field descriptors 284 if (serializable && !isArray) { 285 if (declaredFields == null) { 286 declaredFields = cl.getDeclaredFields(); 287 } 288 result.buildFieldDescriptors(declaredFields); 289 } else { 290 // Externalizables or arrays do not need FieldDesc info 291 result.setFields(NO_FIELDS); 292 } 293 294 // Copy all fields to loadFields - they should be read by default in 295 // ObjectInputStream.defaultReadObject() method 296 ObjectStreamField[] fields = result.getFields(); 297 298 if (fields != null) { 299 ObjectStreamField[] loadFields = new ObjectStreamField[fields.length]; 300 301 for (int i = 0; i < fields.length; ++i) { 302 loadFields[i] = new ObjectStreamField(fields[i].getName(), 303 fields[i].getType(), fields[i].isUnshared()); 304 305 // resolve type string to init typeString field in 306 // ObjectStreamField 307 loadFields[i].getTypeString(); 308 } 309 result.setLoadFields(loadFields); 310 } 311 312 byte flags = 0; 313 if (externalizable) { 314 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; 315 flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default 316 } else if (serializable) { 317 flags |= ObjectStreamConstants.SC_SERIALIZABLE; 318 } 319 result.methodWriteReplace = findMethod(cl, "writeReplace"); 320 result.methodReadResolve = findMethod(cl, "readResolve"); 321 result.methodWriteObject = findPrivateMethod(cl, "writeObject", WRITE_PARAM_TYPES); 322 result.methodReadObject = findPrivateMethod(cl, "readObject", READ_PARAM_TYPES); 323 result.methodReadObjectNoData = findPrivateMethod(cl, "readObjectNoData", EmptyArray.CLASS); 324 if (result.hasMethodWriteObject()) { 325 flags |= ObjectStreamConstants.SC_WRITE_METHOD; 326 } 327 result.setFlags(flags); 328 329 return result; 330 } 331 332 /** 333 * Builds the collection of field descriptors for the receiver 334 * 335 * @param declaredFields 336 * collection of java.lang.reflect.Field for which to compute 337 * field descriptors 338 */ buildFieldDescriptors(Field[] declaredFields)339 void buildFieldDescriptors(Field[] declaredFields) { 340 // We could find the field ourselves in the collection, but calling 341 // reflect is easier. Optimize if needed. 342 final Field f = ObjectStreamClass.fieldSerialPersistentFields(this.forClass()); 343 // If we could not find the emulated fields, we'll have to compute 344 // dumpable fields from reflect fields 345 boolean useReflectFields = f == null; // Assume we will compute the 346 // fields to dump based on the 347 // reflect fields 348 349 ObjectStreamField[] _fields = null; 350 if (!useReflectFields) { 351 // The user declared a collection of emulated fields. Use them. 352 // We have to be able to fetch its value, even if it is private 353 f.setAccessible(true); 354 try { 355 // static field, pass null 356 _fields = (ObjectStreamField[]) f.get(null); 357 } catch (IllegalAccessException ex) { 358 throw new AssertionError(ex); 359 } 360 } else { 361 // Compute collection of dumpable fields based on reflect fields 362 List<ObjectStreamField> serializableFields = 363 new ArrayList<ObjectStreamField>(declaredFields.length); 364 // Filter, we are only interested in fields that are serializable 365 for (Field declaredField : declaredFields) { 366 int modifiers = declaredField.getModifiers(); 367 if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) { 368 ObjectStreamField field = new ObjectStreamField(declaredField.getName(), 369 declaredField.getType()); 370 serializableFields.add(field); 371 } 372 } 373 374 if (serializableFields.size() == 0) { 375 _fields = NO_FIELDS; // If no serializable fields, share the 376 // special value so that users can test 377 } else { 378 _fields = serializableFields.toArray(new ObjectStreamField[serializableFields.size()]); 379 } 380 } 381 Arrays.sort(_fields); 382 // assign offsets 383 int primOffset = 0, objectOffset = 0; 384 for (int i = 0; i < _fields.length; i++) { 385 Class<?> type = _fields[i].getType(); 386 if (type.isPrimitive()) { 387 _fields[i].offset = primOffset; 388 primOffset += primitiveSize(type); 389 } else { 390 _fields[i].offset = objectOffset++; 391 } 392 } 393 fields = _fields; 394 } 395 396 /** 397 * Compute and return the Serial Version UID of the class {@code cl}. 398 * The value is computed based on the class name, superclass chain, field 399 * names, method names, modifiers, etc. 400 * 401 * @param cl 402 * a java.lang.Class for which to compute the SUID 403 * @param fields 404 * cl.getDeclaredFields(), pre-computed by the caller 405 * @return the value of SUID of this class 406 */ computeSerialVersionUID(Class<?> cl, Field[] fields)407 private static long computeSerialVersionUID(Class<?> cl, Field[] fields) { 408 /* 409 * First we should try to fetch the static slot 'static final long 410 * serialVersionUID'. If it is defined, return it. If not defined, we 411 * really need to compute SUID using SHAOutputStream 412 */ 413 for (int i = 0; i < fields.length; i++) { 414 final Field field = fields[i]; 415 if (field.getType() == long.class) { 416 int modifiers = field.getModifiers(); 417 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { 418 if (UID_FIELD_NAME.equals(field.getName())) { 419 /* 420 * We need to be able to see it even if we have no 421 * visibility. That is why we set accessible first (new 422 * API in reflect 1.2) 423 */ 424 field.setAccessible(true); 425 try { 426 // Static field, parameter is ignored 427 return field.getLong(null); 428 } catch (IllegalAccessException iae) { 429 throw new RuntimeException("Error fetching SUID: " + iae); 430 } 431 } 432 } 433 } 434 } 435 436 MessageDigest digest; 437 try { 438 digest = MessageDigest.getInstance("SHA"); 439 } catch (NoSuchAlgorithmException e) { 440 throw new Error(e); 441 } 442 ByteArrayOutputStream sha = new ByteArrayOutputStream(); 443 try { 444 DataOutputStream output = new DataOutputStream(sha); 445 output.writeUTF(cl.getName()); 446 int classModifiers = CLASS_MODIFIERS_MASK & cl.getModifiers(); 447 /* 448 * Workaround for 1F9LOQO. Arrays are ABSTRACT in JDK, but that is 449 * not in the specification. Since we want to be compatible for 450 * X-loading, we have to pretend we have the same shape 451 */ 452 boolean isArray = cl.isArray(); 453 if (isArray) { 454 classModifiers |= Modifier.ABSTRACT; 455 } 456 // Required for JDK UID compatibility 457 if (cl.isInterface() && !Modifier.isPublic(classModifiers)) { 458 classModifiers &= ~Modifier.ABSTRACT; 459 } 460 output.writeInt(classModifiers); 461 462 /* 463 * In JDK1.2 arrays implement Cloneable and Serializable but not in 464 * JDK 1.1.7. So, JDK 1.2 "pretends" arrays have no interfaces when 465 * computing SHA-1 to be compatible. 466 */ 467 if (!isArray) { 468 // Interface information 469 Class<?>[] interfaces = cl.getInterfaces(); 470 if (interfaces.length > 1) { 471 // Only attempt to sort if really needed (saves object 472 // creation, etc) 473 Comparator<Class<?>> interfaceComparator = new Comparator<Class<?>>() { 474 public int compare(Class<?> itf1, Class<?> itf2) { 475 return itf1.getName().compareTo(itf2.getName()); 476 } 477 }; 478 Arrays.sort(interfaces, interfaceComparator); 479 } 480 481 // Dump them 482 for (int i = 0; i < interfaces.length; i++) { 483 output.writeUTF(interfaces[i].getName()); 484 } 485 } 486 487 // Field information 488 if (fields.length > 1) { 489 // Only attempt to sort if really needed (saves object creation, 490 // etc) 491 Comparator<Field> fieldComparator = new Comparator<Field>() { 492 public int compare(Field field1, Field field2) { 493 return field1.getName().compareTo(field2.getName()); 494 } 495 }; 496 Arrays.sort(fields, fieldComparator); 497 } 498 499 // Dump them 500 for (int i = 0; i < fields.length; i++) { 501 Field field = fields[i]; 502 int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK; 503 504 boolean skip = Modifier.isPrivate(modifiers) && 505 (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)); 506 if (!skip) { 507 // write name, modifier & "descriptor" of all but private 508 // static and private transient 509 output.writeUTF(field.getName()); 510 output.writeInt(modifiers); 511 output.writeUTF(descriptorForFieldSignature(getFieldSignature(field))); 512 } 513 } 514 515 /* 516 * Normally constructors come before methods (because <init> < 517 * anyMethodName). However, <clinit> is an exception. Besides, 518 * reflect will not let us get to it. 519 */ 520 if (hasClinit(cl)) { 521 // write name, modifier & "descriptor" 522 output.writeUTF(CLINIT_NAME); 523 output.writeInt(CLINIT_MODIFIERS); 524 output.writeUTF(CLINIT_SIGNATURE); 525 } 526 527 // Constructor information 528 Constructor<?>[] constructors = cl.getDeclaredConstructors(); 529 if (constructors.length > 1) { 530 // Only attempt to sort if really needed (saves object creation, 531 // etc) 532 Comparator<Constructor<?>> constructorComparator = new Comparator<Constructor<?>>() { 533 public int compare(Constructor<?> ctr1, Constructor<?> ctr2) { 534 // All constructors have same name, so we sort based on 535 // signature 536 return (getConstructorSignature(ctr1) 537 .compareTo(getConstructorSignature(ctr2))); 538 } 539 }; 540 Arrays.sort(constructors, constructorComparator); 541 } 542 543 // Dump them 544 for (int i = 0; i < constructors.length; i++) { 545 Constructor<?> constructor = constructors[i]; 546 int modifiers = constructor.getModifiers() 547 & METHOD_MODIFIERS_MASK; 548 boolean isPrivate = Modifier.isPrivate(modifiers); 549 if (!isPrivate) { 550 /* 551 * write name, modifier & "descriptor" of all but private 552 * ones 553 * 554 * constructor.getName() returns the constructor name as 555 * typed, not the VM name 556 */ 557 output.writeUTF("<init>"); 558 output.writeInt(modifiers); 559 output.writeUTF(descriptorForSignature( 560 getConstructorSignature(constructor)).replace('/', 561 '.')); 562 } 563 } 564 565 // Method information 566 Method[] methods = cl.getDeclaredMethods(); 567 if (methods.length > 1) { 568 Comparator<Method> methodComparator = new Comparator<Method>() { 569 public int compare(Method m1, Method m2) { 570 int result = m1.getName().compareTo(m2.getName()); 571 if (result == 0) { 572 // same name, signature will tell which one comes 573 // first 574 return getMethodSignature(m1).compareTo( 575 getMethodSignature(m2)); 576 } 577 return result; 578 } 579 }; 580 Arrays.sort(methods, methodComparator); 581 } 582 583 // Dump them 584 for (int i = 0; i < methods.length; i++) { 585 Method method = methods[i]; 586 int modifiers = method.getModifiers() & METHOD_MODIFIERS_MASK; 587 boolean isPrivate = Modifier.isPrivate(modifiers); 588 if (!isPrivate) { 589 // write name, modifier & "descriptor" of all but private 590 // ones 591 output.writeUTF(method.getName()); 592 output.writeInt(modifiers); 593 output.writeUTF(descriptorForSignature( 594 getMethodSignature(method)).replace('/', '.')); 595 } 596 } 597 } catch (IOException e) { 598 throw new RuntimeException(e + " computing SHA-1/SUID"); 599 } 600 601 // now compute the UID based on the SHA 602 byte[] hash = digest.digest(sha.toByteArray()); 603 return Memory.peekLong(hash, 0, ByteOrder.LITTLE_ENDIAN); 604 } 605 606 /** 607 * Returns what the serialization specification calls "descriptor" given a 608 * field signature. 609 * 610 * @param signature 611 * a field signature 612 * @return containing the descriptor 613 */ descriptorForFieldSignature(String signature)614 private static String descriptorForFieldSignature(String signature) { 615 return signature.replace('.', '/'); 616 } 617 618 /** 619 * Return what the serialization specification calls "descriptor" given a 620 * method/constructor signature. 621 * 622 * @param signature 623 * a method or constructor signature 624 * @return containing the descriptor 625 */ descriptorForSignature(String signature)626 private static String descriptorForSignature(String signature) { 627 return signature.substring(signature.indexOf("(")); 628 } 629 630 /** 631 * Return the java.lang.reflect.Field {@code serialPersistentFields} 632 * if class {@code cl} implements it. Return null otherwise. 633 * 634 * @param cl 635 * a java.lang.Class which to test 636 * @return {@code java.lang.reflect.Field} if the class has 637 * serialPersistentFields {@code null} if the class does not 638 * have serialPersistentFields 639 */ fieldSerialPersistentFields(Class<?> cl)640 static Field fieldSerialPersistentFields(Class<?> cl) { 641 try { 642 Field f = cl.getDeclaredField("serialPersistentFields"); 643 int modifiers = f.getModifiers(); 644 if (Modifier.isStatic(modifiers) && Modifier.isPrivate(modifiers) 645 && Modifier.isFinal(modifiers)) { 646 if (f.getType() == ARRAY_OF_FIELDS) { 647 return f; 648 } 649 } 650 } catch (NoSuchFieldException nsm) { 651 // Ignored 652 } 653 return null; 654 } 655 656 /** 657 * Returns the class (java.lang.Class) for this descriptor. 658 * 659 * @return the class in the local VM that this descriptor represents; 660 * {@code null} if there is no corresponding class. 661 */ forClass()662 public Class<?> forClass() { 663 return resolvedClass; 664 } 665 666 /** 667 * Create and return a new instance of class 'instantiationClass' 668 * using JNI to call the constructor chosen by resolveConstructorClass. 669 * 670 * The returned instance may have uninitialized fields, including final fields. 671 */ newInstance(Class<?> instantiationClass)672 Object newInstance(Class<?> instantiationClass) throws InvalidClassException { 673 resolveConstructorClass(instantiationClass); 674 return newInstance(instantiationClass, resolvedConstructorMethodId); 675 } newInstance(Class<?> instantiationClass, long methodId)676 private static native Object newInstance(Class<?> instantiationClass, long methodId); 677 resolveConstructorClass(Class<?> objectClass)678 private Class<?> resolveConstructorClass(Class<?> objectClass) throws InvalidClassException { 679 if (resolvedConstructorClass != null) { 680 return resolvedConstructorClass; 681 } 682 683 // The class of the instance may not be the same as the class of the 684 // constructor to run 685 // This is the constructor to run if Externalizable 686 Class<?> constructorClass = objectClass; 687 688 // WARNING - What if the object is serializable and externalizable ? 689 // Is that possible ? 690 boolean wasSerializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; 691 if (wasSerializable) { 692 // Now we must run the constructor of the class just above the 693 // one that implements Serializable so that slots that were not 694 // dumped can be initialized properly 695 while (constructorClass != null && ObjectStreamClass.isSerializable(constructorClass)) { 696 constructorClass = constructorClass.getSuperclass(); 697 } 698 } 699 700 // Fetch the empty constructor, or null if none. 701 Constructor<?> constructor = null; 702 if (constructorClass != null) { 703 try { 704 constructor = constructorClass.getDeclaredConstructor(EmptyArray.CLASS); 705 } catch (NoSuchMethodException ignored) { 706 } 707 } 708 709 // Has to have an empty constructor 710 if (constructor == null) { 711 String className = constructorClass != null ? constructorClass.getName() : null; 712 throw new InvalidClassException(className, "IllegalAccessException"); 713 } 714 715 int constructorModifiers = constructor.getModifiers(); 716 boolean isPublic = Modifier.isPublic(constructorModifiers); 717 boolean isProtected = Modifier.isProtected(constructorModifiers); 718 boolean isPrivate = Modifier.isPrivate(constructorModifiers); 719 720 // Now we must check if the empty constructor is visible to the 721 // instantiation class 722 boolean wasExternalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; 723 if (isPrivate || (wasExternalizable && !isPublic)) { 724 throw new InvalidClassException(constructorClass.getName(), "IllegalAccessException"); 725 } 726 727 // We know we are testing from a subclass, so the only other case 728 // where the visibility is not allowed is when the constructor has 729 // default visibility and the instantiation class is in a different 730 // package than the constructor class 731 if (!isPublic && !isProtected) { 732 // Not public, not private and not protected...means default 733 // visibility. Check if same package 734 if (!inSamePackage(constructorClass, objectClass)) { 735 throw new InvalidClassException(constructorClass.getName(), "IllegalAccessException"); 736 } 737 } 738 739 resolvedConstructorClass = constructorClass; 740 resolvedConstructorMethodId = getConstructorId(resolvedConstructorClass); 741 return constructorClass; 742 } getConstructorId(Class<?> c)743 private static native long getConstructorId(Class<?> c); 744 745 /** 746 * Checks if two classes belong to the same package. 747 * 748 * @param c1 749 * one of the classes to test. 750 * @param c2 751 * the other class to test. 752 * @return {@code true} if the two classes belong to the same package, 753 * {@code false} otherwise. 754 */ inSamePackage(Class<?> c1, Class<?> c2)755 private boolean inSamePackage(Class<?> c1, Class<?> c2) { 756 String nameC1 = c1.getName(); 757 String nameC2 = c2.getName(); 758 int indexDotC1 = nameC1.lastIndexOf('.'); 759 int indexDotC2 = nameC2.lastIndexOf('.'); 760 if (indexDotC1 != indexDotC2) { 761 return false; // cannot be in the same package if indices are not the same 762 } 763 if (indexDotC1 == -1) { 764 return true; // both of them are in default package 765 } 766 return nameC1.regionMatches(0, nameC2, 0, indexDotC1); 767 } 768 769 /** 770 * Return a String representing the signature for a Constructor {@code c}. 771 * 772 * @param c 773 * a java.lang.reflect.Constructor for which to compute the 774 * signature 775 * @return the constructor's signature 776 */ getConstructorSignature(Constructor<?> c)777 static native String getConstructorSignature(Constructor<?> c); 778 779 /** 780 * Gets a field descriptor of the class represented by this class 781 * descriptor. 782 * 783 * @param name 784 * the name of the desired field. 785 * @return the field identified by {@code name} or {@code null} if there is 786 * no such field. 787 */ getField(String name)788 public ObjectStreamField getField(String name) { 789 ObjectStreamField[] allFields = getFields(); 790 for (int i = 0; i < allFields.length; i++) { 791 ObjectStreamField f = allFields[i]; 792 if (f.getName().equals(name)) { 793 return f; 794 } 795 } 796 return null; 797 } 798 799 /** 800 * Returns the collection of field descriptors for the fields of the 801 * corresponding class 802 * 803 * @return the receiver's collection of declared fields for the class it 804 * represents 805 */ fields()806 ObjectStreamField[] fields() { 807 if (fields == null) { 808 Class<?> forCl = forClass(); 809 if (forCl != null && isSerializable() && !forCl.isArray()) { 810 buildFieldDescriptors(forCl.getDeclaredFields()); 811 } else { 812 // Externalizables or arrays do not need FieldDesc info 813 setFields(NO_FIELDS); 814 } 815 } 816 return fields; 817 } 818 819 /** 820 * Returns a collection of field descriptors for the serialized fields of 821 * the class represented by this class descriptor. 822 * 823 * @return an array of field descriptors or an array of length zero if there 824 * are no fields in this descriptor's class. 825 */ getFields()826 public ObjectStreamField[] getFields() { 827 copyFieldAttributes(); 828 return loadFields == null ? fields().clone() : loadFields.clone(); 829 } 830 831 private transient volatile List<ObjectStreamClass> cachedHierarchy; 832 getHierarchy()833 List<ObjectStreamClass> getHierarchy() { 834 List<ObjectStreamClass> result = cachedHierarchy; 835 if (result == null) { 836 cachedHierarchy = result = makeHierarchy(); 837 } 838 return result; 839 } 840 makeHierarchy()841 private List<ObjectStreamClass> makeHierarchy() { 842 ArrayList<ObjectStreamClass> result = new ArrayList<ObjectStreamClass>(); 843 for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuperclass()) { 844 result.add(0, osc); 845 } 846 return result; 847 } 848 849 /** 850 * If a Class uses "serialPersistentFields" to define the serialized fields, 851 * this.loadFields cannot get the "unshared" information when deserializing 852 * fields using current implementation of ObjectInputStream. This method 853 * provides a way to copy the "unshared" attribute from this.fields. 854 * 855 */ copyFieldAttributes()856 private void copyFieldAttributes() { 857 if ((loadFields == null) || fields == null) { 858 return; 859 } 860 861 for (int i = 0; i < loadFields.length; i++) { 862 ObjectStreamField loadField = loadFields[i]; 863 String name = loadField.getName(); 864 for (int j = 0; j < fields.length; j++) { 865 ObjectStreamField field = fields[j]; 866 if (name.equals(field.getName())) { 867 loadField.setUnshared(field.isUnshared()); 868 loadField.setOffset(field.getOffset()); 869 break; 870 } 871 } 872 } 873 } 874 875 /** 876 * Returns the collection of field descriptors for the input fields of the 877 * corresponding class 878 * 879 * @return the receiver's collection of input fields for the class it 880 * represents 881 */ getLoadFields()882 ObjectStreamField[] getLoadFields() { 883 return loadFields; 884 } 885 886 /** 887 * Return a String representing the signature for a field {@code f}. 888 * 889 * @param f 890 * a java.lang.reflect.Field for which to compute the signature 891 * @return the field's signature 892 */ getFieldSignature(Field f)893 private static native String getFieldSignature(Field f); 894 895 /** 896 * Returns the flags for this descriptor, where possible combined values are 897 * 898 * ObjectStreamConstants.SC_WRITE_METHOD 899 * ObjectStreamConstants.SC_SERIALIZABLE 900 * ObjectStreamConstants.SC_EXTERNALIZABLE 901 * 902 * @return byte the receiver's flags for the class it represents 903 */ getFlags()904 byte getFlags() { 905 return flags; 906 } 907 908 /** 909 * Return a String representing the signature for a method {@code m}. 910 * 911 * @param m 912 * a java.lang.reflect.Method for which to compute the signature 913 * @return the method's signature 914 */ getMethodSignature(Method m)915 static native String getMethodSignature(Method m); 916 917 /** 918 * Returns the name of the class represented by this descriptor. 919 * 920 * @return the fully qualified name of the class this descriptor represents. 921 */ getName()922 public String getName() { 923 return className; 924 } 925 926 /** 927 * Returns the Serial Version User ID of the class represented by this 928 * descriptor. 929 * 930 * @return the SUID for the class represented by this descriptor. 931 */ getSerialVersionUID()932 public long getSerialVersionUID() { 933 return svUID; 934 } 935 936 /** 937 * Returns the descriptor (ObjectStreamClass) of the superclass of the class 938 * represented by the receiver. 939 * 940 * @return an ObjectStreamClass representing the superclass of the class 941 * represented by the receiver. 942 */ getSuperclass()943 ObjectStreamClass getSuperclass() { 944 return superclass; 945 } 946 947 /** 948 * Return true if the given class {@code cl} has the 949 * compiler-generated method {@code clinit}. Even though it is 950 * compiler-generated, it is used by the serialization code to compute SUID. 951 * This is unfortunate, since it may depend on compiler optimizations in 952 * some cases. 953 * 954 * @param cl 955 * a java.lang.Class which to test 956 * @return {@code true} if the class has <clinit> {@code false} 957 * if the class does not have <clinit> 958 */ hasClinit(Class<?> cl)959 private static native boolean hasClinit(Class<?> cl); 960 961 /** 962 * Return true if instances of class {@code cl} are Externalizable, 963 * false otherwise. 964 * 965 * @param cl 966 * a java.lang.Class which to test 967 * @return {@code true} if instances of the class are Externalizable 968 * {@code false} if instances of the class are not 969 * Externalizable 970 * 971 * @see Object#hashCode 972 */ isExternalizable(Class<?> cl)973 static boolean isExternalizable(Class<?> cl) { 974 return EXTERNALIZABLE.isAssignableFrom(cl); 975 } 976 977 /** 978 * Return true if the type code 979 * <code>typecode<code> describes a primitive type 980 * 981 * @param typecode a char describing the typecode 982 * @return {@code true} if the typecode represents a primitive type 983 * {@code false} if the typecode represents an Object type (including arrays) 984 * 985 * @see Object#hashCode 986 */ isPrimitiveType(char typecode)987 static boolean isPrimitiveType(char typecode) { 988 return !(typecode == '[' || typecode == 'L'); 989 } 990 991 /** 992 * Return true if instances of class {@code cl} are Serializable, 993 * false otherwise. 994 * 995 * @param cl 996 * a java.lang.Class which to test 997 * @return {@code true} if instances of the class are Serializable 998 * {@code false} if instances of the class are not 999 * Serializable 1000 * 1001 * @see Object#hashCode 1002 */ isSerializable(Class<?> cl)1003 static boolean isSerializable(Class<?> cl) { 1004 return SERIALIZABLE.isAssignableFrom(cl); 1005 } 1006 1007 /** 1008 * Resolves the class properties, if they weren't already 1009 */ resolveProperties()1010 private void resolveProperties() { 1011 if (arePropertiesResolved) { 1012 return; 1013 } 1014 1015 Class<?> cl = forClass(); 1016 isProxy = Proxy.isProxyClass(cl); 1017 isEnum = Enum.class.isAssignableFrom(cl); 1018 isSerializable = isSerializable(cl); 1019 isExternalizable = isExternalizable(cl); 1020 1021 arePropertiesResolved = true; 1022 } 1023 isSerializable()1024 boolean isSerializable() { 1025 resolveProperties(); 1026 return isSerializable; 1027 } 1028 isExternalizable()1029 boolean isExternalizable() { 1030 resolveProperties(); 1031 return isExternalizable; 1032 } 1033 isProxy()1034 boolean isProxy() { 1035 resolveProperties(); 1036 return isProxy; 1037 } 1038 isEnum()1039 boolean isEnum() { 1040 resolveProperties(); 1041 return isEnum; 1042 } 1043 1044 /** 1045 * Returns the descriptor for a serializable class. 1046 * Returns null if the class doesn't implement {@code Serializable} or {@code Externalizable}. 1047 * 1048 * @param cl 1049 * a java.lang.Class for which to obtain the corresponding 1050 * descriptor 1051 * @return the corresponding descriptor if the class is serializable or 1052 * externalizable; null otherwise. 1053 */ lookup(Class<?> cl)1054 public static ObjectStreamClass lookup(Class<?> cl) { 1055 ObjectStreamClass osc = lookupStreamClass(cl); 1056 return (osc.isSerializable() || osc.isExternalizable()) ? osc : null; 1057 } 1058 1059 /** 1060 * Returns the descriptor for any class, whether or not the class 1061 * implements Serializable or Externalizable. 1062 * 1063 * @param cl 1064 * a java.lang.Class for which to obtain the corresponding 1065 * descriptor 1066 * @return the descriptor 1067 * @since 1.6 1068 */ lookupAny(Class<?> cl)1069 public static ObjectStreamClass lookupAny(Class<?> cl) { 1070 return lookupStreamClass(cl); 1071 } 1072 1073 /** 1074 * Return the descriptor (ObjectStreamClass) corresponding to the class 1075 * {@code cl}. Returns an ObjectStreamClass even if instances of the 1076 * class cannot be serialized 1077 * 1078 * @param cl 1079 * a java.langClass for which to obtain the corresponding 1080 * descriptor 1081 * @return the corresponding descriptor 1082 */ lookupStreamClass(Class<?> cl)1083 static ObjectStreamClass lookupStreamClass(Class<?> cl) { 1084 WeakHashMap<Class<?>, ObjectStreamClass> tlc = getCache(); 1085 ObjectStreamClass cachedValue = tlc.get(cl); 1086 if (cachedValue == null) { 1087 cachedValue = createClassDesc(cl); 1088 tlc.put(cl, cachedValue); 1089 } 1090 return cachedValue; 1091 } 1092 1093 /** 1094 * A ThreadLocal cache for lookupStreamClass, with the possibility of discarding the thread 1095 * local storage content when the heap is exhausted. 1096 */ 1097 private static SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>> storage = 1098 new SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>>(null); 1099 getCache()1100 private static WeakHashMap<Class<?>, ObjectStreamClass> getCache() { 1101 ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>> tls = storage.get(); 1102 if (tls == null) { 1103 tls = new ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>() { 1104 public WeakHashMap<Class<?>, ObjectStreamClass> initialValue() { 1105 return new WeakHashMap<Class<?>, ObjectStreamClass>(); 1106 } 1107 }; 1108 storage = new SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>>(tls); 1109 } 1110 return tls.get(); 1111 } 1112 1113 /** 1114 * Return the java.lang.reflect.Method if class <code>cl</code> implements 1115 * <code>methodName</code> . Return null otherwise. 1116 * 1117 * @param cl 1118 * a java.lang.Class which to test 1119 * @return <code>java.lang.reflect.Method</code> if the class implements 1120 * writeReplace <code>null</code> if the class does not implement 1121 * writeReplace 1122 */ findMethod(Class<?> cl, String methodName)1123 static Method findMethod(Class<?> cl, String methodName) { 1124 Class<?> search = cl; 1125 Method method = null; 1126 while (search != null) { 1127 try { 1128 method = search.getDeclaredMethod(methodName, (Class[]) null); 1129 if (search == cl 1130 || (method.getModifiers() & Modifier.PRIVATE) == 0) { 1131 method.setAccessible(true); 1132 return method; 1133 } 1134 } catch (NoSuchMethodException nsm) { 1135 } 1136 search = search.getSuperclass(); 1137 } 1138 return null; 1139 } 1140 1141 /** 1142 * Return the java.lang.reflect.Method if class <code>cl</code> implements 1143 * private <code>methodName</code> . Return null otherwise. 1144 * 1145 * @param cl 1146 * a java.lang.Class which to test 1147 * @return {@code java.lang.reflect.Method} if the class implements 1148 * writeReplace {@code null} if the class does not implement 1149 * writeReplace 1150 */ findPrivateMethod(Class<?> cl, String methodName, Class<?>[] param)1151 static Method findPrivateMethod(Class<?> cl, String methodName, 1152 Class<?>[] param) { 1153 try { 1154 Method method = cl.getDeclaredMethod(methodName, param); 1155 if (Modifier.isPrivate(method.getModifiers()) && method.getReturnType() == void.class) { 1156 method.setAccessible(true); 1157 return method; 1158 } 1159 } catch (NoSuchMethodException nsm) { 1160 // Ignored 1161 } 1162 return null; 1163 } 1164 hasMethodWriteReplace()1165 boolean hasMethodWriteReplace() { 1166 return (methodWriteReplace != null); 1167 } 1168 getMethodWriteReplace()1169 Method getMethodWriteReplace() { 1170 return methodWriteReplace; 1171 } 1172 hasMethodReadResolve()1173 boolean hasMethodReadResolve() { 1174 return (methodReadResolve != null); 1175 } 1176 getMethodReadResolve()1177 Method getMethodReadResolve() { 1178 return methodReadResolve; 1179 } 1180 hasMethodWriteObject()1181 boolean hasMethodWriteObject() { 1182 return (methodWriteObject != null); 1183 } 1184 getMethodWriteObject()1185 Method getMethodWriteObject() { 1186 return methodWriteObject; 1187 } 1188 hasMethodReadObject()1189 boolean hasMethodReadObject() { 1190 return (methodReadObject != null); 1191 } 1192 getMethodReadObject()1193 Method getMethodReadObject() { 1194 return methodReadObject; 1195 } 1196 hasMethodReadObjectNoData()1197 boolean hasMethodReadObjectNoData() { 1198 return (methodReadObjectNoData != null); 1199 } 1200 getMethodReadObjectNoData()1201 Method getMethodReadObjectNoData() { 1202 return methodReadObjectNoData; 1203 } 1204 initPrivateFields(ObjectStreamClass desc)1205 void initPrivateFields(ObjectStreamClass desc) { 1206 methodWriteReplace = desc.methodWriteReplace; 1207 methodReadResolve = desc.methodReadResolve; 1208 methodWriteObject = desc.methodWriteObject; 1209 methodReadObject = desc.methodReadObject; 1210 methodReadObjectNoData = desc.methodReadObjectNoData; 1211 } 1212 1213 /** 1214 * Set the class (java.lang.Class) that the receiver represents 1215 * 1216 * @param c 1217 * aClass, the new class that the receiver describes 1218 */ setClass(Class<?> c)1219 void setClass(Class<?> c) { 1220 resolvedClass = c; 1221 } 1222 1223 /** 1224 * Set the collection of field descriptors for the fields of the 1225 * corresponding class 1226 * 1227 * @param f 1228 * ObjectStreamField[], the receiver's new collection of declared 1229 * fields for the class it represents 1230 */ setFields(ObjectStreamField[] f)1231 void setFields(ObjectStreamField[] f) { 1232 fields = f; 1233 } 1234 1235 /** 1236 * Set the collection of field descriptors for the input fields of the 1237 * corresponding class 1238 * 1239 * @param f 1240 * ObjectStreamField[], the receiver's new collection of input 1241 * fields for the class it represents 1242 */ setLoadFields(ObjectStreamField[] f)1243 void setLoadFields(ObjectStreamField[] f) { 1244 loadFields = f; 1245 } 1246 1247 /** 1248 * Set the flags for this descriptor, where possible combined values are 1249 * 1250 * ObjectStreamConstants.SC_WRITE_METHOD 1251 * ObjectStreamConstants.SC_SERIALIZABLE 1252 * ObjectStreamConstants.SC_EXTERNALIZABLE 1253 * 1254 * @param b 1255 * byte, the receiver's new flags for the class it represents 1256 */ setFlags(byte b)1257 void setFlags(byte b) { 1258 flags = b; 1259 } 1260 1261 /** 1262 * Set the name of the class represented by the receiver 1263 * 1264 * @param newName 1265 * a String, the new fully qualified name of the class the 1266 * receiver represents 1267 */ setName(String newName)1268 void setName(String newName) { 1269 className = newName; 1270 } 1271 1272 /** 1273 * Set the Serial Version User ID of the class represented by the receiver 1274 * 1275 * @param l 1276 * a long, the new SUID for the class represented by the receiver 1277 */ setSerialVersionUID(long l)1278 void setSerialVersionUID(long l) { 1279 svUID = l; 1280 } 1281 1282 /** 1283 * Set the descriptor for the superclass of the class described by the 1284 * receiver 1285 * 1286 * @param c 1287 * an ObjectStreamClass, the new ObjectStreamClass for the 1288 * superclass of the class represented by the receiver 1289 */ setSuperclass(ObjectStreamClass c)1290 void setSuperclass(ObjectStreamClass c) { 1291 superclass = c; 1292 } 1293 primitiveSize(Class<?> type)1294 private int primitiveSize(Class<?> type) { 1295 if (type == byte.class || type == boolean.class) { 1296 return 1; 1297 } 1298 if (type == short.class || type == char.class) { 1299 return 2; 1300 } 1301 if (type == int.class || type == float.class) { 1302 return 4; 1303 } 1304 if (type == long.class || type == double.class) { 1305 return 8; 1306 } 1307 throw new AssertionError(); 1308 } 1309 1310 /** 1311 * Returns a string containing a concise, human-readable description of this 1312 * descriptor. 1313 * 1314 * @return a printable representation of this descriptor. 1315 */ 1316 @Override toString()1317 public String toString() { 1318 return getName() + ": static final long serialVersionUID =" + getSerialVersionUID() + "L;"; 1319 } 1320 1321 /** 1322 * Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_OBJECT} 1323 * deserialization. Also performs some sanity checks of the stream data. This method is used 1324 * during deserialization to confirm the local class is likely to be compatible with the coming 1325 * stream data, but before an instance is instantiated. 1326 * 1327 * @hide used internally during deserialization 1328 */ checkAndGetTcObjectClass()1329 public Class<?> checkAndGetTcObjectClass() throws InvalidClassException { 1330 // We check some error possibilities that might cause problems later. 1331 boolean wasSerializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; 1332 boolean wasExternalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; 1333 if (wasSerializable == wasExternalizable) { 1334 throw new InvalidClassException( 1335 getName() + " stream data is corrupt: SC_SERIALIZABLE=" + wasSerializable 1336 + " SC_EXTERNALIZABLE=" + wasExternalizable 1337 + ", classDescFlags must have one or the other"); 1338 } 1339 1340 // TC_ENUM is handled elsewhere. See checkAndGetTcEnumClass(). 1341 if (isEnum()) { 1342 throw new InvalidClassException( 1343 getName() + " local class is incompatible: Local class is an enum, streamed" 1344 + " data is tagged with TC_OBJECT"); 1345 } 1346 1347 // isSerializable() is true if the local class implements Serializable. Externalizable 1348 // classes are also Serializable via inheritance. 1349 if (!isSerializable()) { 1350 throw new InvalidClassException(getName() + " local class is incompatible: Not" 1351 + " Serializable"); 1352 } 1353 1354 // The stream class was externalizable, but is only serializable locally. 1355 if (wasExternalizable != isExternalizable()) { 1356 throw new InvalidClassException( 1357 getName() + " local class is incompatible: Local class is Serializable, stream" 1358 + " data requires Externalizable"); 1359 } 1360 1361 // The following are left unchecked and thus are treated leniently at this point. 1362 // SC_BLOCK_DATA may be set iff SC_EXTERNALIZABLE is set AND version 2 of the protocol is in 1363 // use. 1364 // SC_ENUM should not be set. 1365 1366 return forClass(); 1367 } 1368 1369 /** 1370 * Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_ENUM} 1371 * deserialization. This method is used during deserialization to confirm the local class is 1372 * likely to be compatible with the coming stream data, but before an instance is instantiated. 1373 * 1374 * @hide used internally during deserialization 1375 */ checkAndGetTcEnumClass()1376 public Class<?> checkAndGetTcEnumClass() throws InvalidClassException { 1377 if (!isEnum()) { 1378 throw new InvalidClassException( 1379 getName() + " local class is incompatible: Local class is not an enum," 1380 + " streamed data is tagged with TC_ENUM"); 1381 } 1382 1383 // The stream flags are expected to be SC_SERIALIZABLE | SC_ENUM but these and the 1384 // other flags are not used when reading enum data so they are treated leniently. 1385 1386 return forClass(); 1387 } 1388 } 1389