jaroslav@601
|
1 |
/*
|
jaroslav@601
|
2 |
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
|
jaroslav@601
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
jaroslav@601
|
4 |
*
|
jaroslav@601
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
jaroslav@601
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
jaroslav@601
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
jaroslav@601
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
jaroslav@601
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
jaroslav@601
|
10 |
*
|
jaroslav@601
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
jaroslav@601
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
jaroslav@601
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
jaroslav@601
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
jaroslav@601
|
15 |
* accompanied this code).
|
jaroslav@601
|
16 |
*
|
jaroslav@601
|
17 |
* You should have received a copy of the GNU General Public License version
|
jaroslav@601
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
jaroslav@601
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
jaroslav@601
|
20 |
*
|
jaroslav@601
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
jaroslav@601
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
jaroslav@601
|
23 |
* questions.
|
jaroslav@601
|
24 |
*/
|
jaroslav@601
|
25 |
|
jaroslav@601
|
26 |
package java.io;
|
jaroslav@601
|
27 |
|
jaroslav@601
|
28 |
import java.lang.ref.Reference;
|
jaroslav@601
|
29 |
import java.lang.ref.ReferenceQueue;
|
jaroslav@601
|
30 |
import java.lang.ref.SoftReference;
|
jaroslav@601
|
31 |
import java.lang.ref.WeakReference;
|
jaroslav@601
|
32 |
import java.lang.reflect.Constructor;
|
jaroslav@601
|
33 |
import java.lang.reflect.Field;
|
jaroslav@601
|
34 |
import java.lang.reflect.InvocationTargetException;
|
jaroslav@601
|
35 |
import java.lang.reflect.Member;
|
jaroslav@601
|
36 |
import java.lang.reflect.Method;
|
jaroslav@601
|
37 |
import java.lang.reflect.Modifier;
|
jaroslav@601
|
38 |
import java.lang.reflect.Proxy;
|
jaroslav@601
|
39 |
import java.util.ArrayList;
|
jaroslav@601
|
40 |
import java.util.Arrays;
|
jaroslav@601
|
41 |
import java.util.Collections;
|
jaroslav@601
|
42 |
import java.util.Comparator;
|
jaroslav@601
|
43 |
import java.util.HashSet;
|
jaroslav@601
|
44 |
import java.util.Set;
|
jaroslav@601
|
45 |
|
jaroslav@601
|
46 |
/**
|
jaroslav@601
|
47 |
* Serialization's descriptor for classes. It contains the name and
|
jaroslav@601
|
48 |
* serialVersionUID of the class. The ObjectStreamClass for a specific class
|
jaroslav@601
|
49 |
* loaded in this Java VM can be found/created using the lookup method.
|
jaroslav@601
|
50 |
*
|
jaroslav@601
|
51 |
* <p>The algorithm to compute the SerialVersionUID is described in
|
jaroslav@601
|
52 |
* <a href="../../../platform/serialization/spec/class.html#4100">Object
|
jaroslav@601
|
53 |
* Serialization Specification, Section 4.6, Stream Unique Identifiers</a>.
|
jaroslav@601
|
54 |
*
|
jaroslav@601
|
55 |
* @author Mike Warres
|
jaroslav@601
|
56 |
* @author Roger Riggs
|
jaroslav@601
|
57 |
* @see ObjectStreamField
|
jaroslav@601
|
58 |
* @see <a href="../../../platform/serialization/spec/class.html">Object Serialization Specification, Section 4, Class Descriptors</a>
|
jaroslav@601
|
59 |
* @since JDK1.1
|
jaroslav@601
|
60 |
*/
|
jaroslav@601
|
61 |
public class ObjectStreamClass implements Serializable {
|
jaroslav@601
|
62 |
|
jaroslav@601
|
63 |
/** serialPersistentFields value indicating no serializable fields */
|
jaroslav@601
|
64 |
public static final ObjectStreamField[] NO_FIELDS =
|
jaroslav@601
|
65 |
new ObjectStreamField[0];
|
jaroslav@601
|
66 |
|
jaroslav@601
|
67 |
private static final long serialVersionUID = -6120832682080437368L;
|
jaroslav@601
|
68 |
private static final ObjectStreamField[] serialPersistentFields =
|
jaroslav@601
|
69 |
NO_FIELDS;
|
jaroslav@601
|
70 |
|
jaroslav@601
|
71 |
|
jaroslav@601
|
72 |
/** class associated with this descriptor (if any) */
|
jaroslav@601
|
73 |
private Class<?> cl;
|
jaroslav@601
|
74 |
/** name of class represented by this descriptor */
|
jaroslav@601
|
75 |
private String name;
|
jaroslav@601
|
76 |
/** serialVersionUID of represented class (null if not computed yet) */
|
jaroslav@601
|
77 |
private volatile Long suid;
|
jaroslav@601
|
78 |
|
jaroslav@601
|
79 |
/** true if represents dynamic proxy class */
|
jaroslav@601
|
80 |
private boolean isProxy;
|
jaroslav@601
|
81 |
/** true if represents enum type */
|
jaroslav@601
|
82 |
private boolean isEnum;
|
jaroslav@601
|
83 |
/** true if represented class implements Serializable */
|
jaroslav@601
|
84 |
private boolean serializable;
|
jaroslav@601
|
85 |
/** true if represented class implements Externalizable */
|
jaroslav@601
|
86 |
private boolean externalizable;
|
jaroslav@601
|
87 |
/** true if desc has data written by class-defined writeObject method */
|
jaroslav@601
|
88 |
private boolean hasWriteObjectData;
|
jaroslav@601
|
89 |
/**
|
jaroslav@601
|
90 |
* true if desc has externalizable data written in block data format; this
|
jaroslav@601
|
91 |
* must be true by default to accommodate ObjectInputStream subclasses which
|
jaroslav@601
|
92 |
* override readClassDescriptor() to return class descriptors obtained from
|
jaroslav@601
|
93 |
* ObjectStreamClass.lookup() (see 4461737)
|
jaroslav@601
|
94 |
*/
|
jaroslav@601
|
95 |
private boolean hasBlockExternalData = true;
|
jaroslav@601
|
96 |
|
jaroslav@601
|
97 |
/** exception (if any) thrown while attempting to resolve class */
|
jaroslav@601
|
98 |
private ClassNotFoundException resolveEx;
|
jaroslav@601
|
99 |
/** exception (if any) to throw if non-enum deserialization attempted */
|
jaroslav@601
|
100 |
private InvalidClassException deserializeEx;
|
jaroslav@601
|
101 |
/** exception (if any) to throw if non-enum serialization attempted */
|
jaroslav@601
|
102 |
private InvalidClassException serializeEx;
|
jaroslav@601
|
103 |
/** exception (if any) to throw if default serialization attempted */
|
jaroslav@601
|
104 |
private InvalidClassException defaultSerializeEx;
|
jaroslav@601
|
105 |
|
jaroslav@601
|
106 |
/** serializable fields */
|
jaroslav@601
|
107 |
private ObjectStreamField[] fields;
|
jaroslav@601
|
108 |
/** aggregate marshalled size of primitive fields */
|
jaroslav@601
|
109 |
private int primDataSize;
|
jaroslav@601
|
110 |
/** number of non-primitive fields */
|
jaroslav@601
|
111 |
private int numObjFields;
|
jaroslav@601
|
112 |
/** reflector for setting/getting serializable field values */
|
jaroslav@604
|
113 |
// private FieldReflector fieldRefl;
|
jaroslav@601
|
114 |
/** data layout of serialized objects described by this class desc */
|
jaroslav@601
|
115 |
private volatile ClassDataSlot[] dataLayout;
|
jaroslav@601
|
116 |
|
jaroslav@601
|
117 |
/** serialization-appropriate constructor, or null if none */
|
jaroslav@601
|
118 |
private Constructor cons;
|
jaroslav@601
|
119 |
/** class-defined writeObject method, or null if none */
|
jaroslav@601
|
120 |
private Method writeObjectMethod;
|
jaroslav@601
|
121 |
/** class-defined readObject method, or null if none */
|
jaroslav@601
|
122 |
private Method readObjectMethod;
|
jaroslav@601
|
123 |
/** class-defined readObjectNoData method, or null if none */
|
jaroslav@601
|
124 |
private Method readObjectNoDataMethod;
|
jaroslav@601
|
125 |
/** class-defined writeReplace method, or null if none */
|
jaroslav@601
|
126 |
private Method writeReplaceMethod;
|
jaroslav@601
|
127 |
/** class-defined readResolve method, or null if none */
|
jaroslav@601
|
128 |
private Method readResolveMethod;
|
jaroslav@601
|
129 |
|
jaroslav@601
|
130 |
/** local class descriptor for represented class (may point to self) */
|
jaroslav@601
|
131 |
private ObjectStreamClass localDesc;
|
jaroslav@601
|
132 |
/** superclass descriptor appearing in stream */
|
jaroslav@601
|
133 |
private ObjectStreamClass superDesc;
|
jaroslav@601
|
134 |
|
jaroslav@601
|
135 |
/**
|
jaroslav@601
|
136 |
* Initializes native code.
|
jaroslav@601
|
137 |
*/
|
jaroslav@601
|
138 |
private static native void initNative();
|
jaroslav@601
|
139 |
static {
|
jaroslav@601
|
140 |
initNative();
|
jaroslav@601
|
141 |
}
|
jaroslav@601
|
142 |
|
jaroslav@601
|
143 |
/**
|
jaroslav@601
|
144 |
* Find the descriptor for a class that can be serialized. Creates an
|
jaroslav@601
|
145 |
* ObjectStreamClass instance if one does not exist yet for class. Null is
|
jaroslav@601
|
146 |
* returned if the specified class does not implement java.io.Serializable
|
jaroslav@601
|
147 |
* or java.io.Externalizable.
|
jaroslav@601
|
148 |
*
|
jaroslav@601
|
149 |
* @param cl class for which to get the descriptor
|
jaroslav@601
|
150 |
* @return the class descriptor for the specified class
|
jaroslav@601
|
151 |
*/
|
jaroslav@601
|
152 |
public static ObjectStreamClass lookup(Class<?> cl) {
|
jaroslav@601
|
153 |
return lookup(cl, false);
|
jaroslav@601
|
154 |
}
|
jaroslav@601
|
155 |
|
jaroslav@601
|
156 |
/**
|
jaroslav@601
|
157 |
* Returns the descriptor for any class, regardless of whether it
|
jaroslav@601
|
158 |
* implements {@link Serializable}.
|
jaroslav@601
|
159 |
*
|
jaroslav@601
|
160 |
* @param cl class for which to get the descriptor
|
jaroslav@601
|
161 |
* @return the class descriptor for the specified class
|
jaroslav@601
|
162 |
* @since 1.6
|
jaroslav@601
|
163 |
*/
|
jaroslav@601
|
164 |
public static ObjectStreamClass lookupAny(Class<?> cl) {
|
jaroslav@601
|
165 |
return lookup(cl, true);
|
jaroslav@601
|
166 |
}
|
jaroslav@601
|
167 |
|
jaroslav@601
|
168 |
/**
|
jaroslav@601
|
169 |
* Returns the name of the class described by this descriptor.
|
jaroslav@601
|
170 |
* This method returns the name of the class in the format that
|
jaroslav@601
|
171 |
* is used by the {@link Class#getName} method.
|
jaroslav@601
|
172 |
*
|
jaroslav@601
|
173 |
* @return a string representing the name of the class
|
jaroslav@601
|
174 |
*/
|
jaroslav@601
|
175 |
public String getName() {
|
jaroslav@601
|
176 |
return name;
|
jaroslav@601
|
177 |
}
|
jaroslav@601
|
178 |
|
jaroslav@601
|
179 |
/**
|
jaroslav@601
|
180 |
* Return the serialVersionUID for this class. The serialVersionUID
|
jaroslav@601
|
181 |
* defines a set of classes all with the same name that have evolved from a
|
jaroslav@601
|
182 |
* common root class and agree to be serialized and deserialized using a
|
jaroslav@601
|
183 |
* common format. NonSerializable classes have a serialVersionUID of 0L.
|
jaroslav@601
|
184 |
*
|
jaroslav@601
|
185 |
* @return the SUID of the class described by this descriptor
|
jaroslav@601
|
186 |
*/
|
jaroslav@601
|
187 |
public long getSerialVersionUID() {
|
jaroslav@601
|
188 |
// REMIND: synchronize instead of relying on volatile?
|
jaroslav@601
|
189 |
if (suid == null) {
|
jaroslav@604
|
190 |
return computeDefaultSUID(cl);
|
jaroslav@601
|
191 |
}
|
jaroslav@601
|
192 |
return suid.longValue();
|
jaroslav@601
|
193 |
}
|
jaroslav@601
|
194 |
|
jaroslav@601
|
195 |
/**
|
jaroslav@601
|
196 |
* Return the class in the local VM that this version is mapped to. Null
|
jaroslav@601
|
197 |
* is returned if there is no corresponding local class.
|
jaroslav@601
|
198 |
*
|
jaroslav@601
|
199 |
* @return the <code>Class</code> instance that this descriptor represents
|
jaroslav@601
|
200 |
*/
|
jaroslav@601
|
201 |
public Class<?> forClass() {
|
jaroslav@601
|
202 |
return cl;
|
jaroslav@601
|
203 |
}
|
jaroslav@601
|
204 |
|
jaroslav@601
|
205 |
/**
|
jaroslav@601
|
206 |
* Return an array of the fields of this serializable class.
|
jaroslav@601
|
207 |
*
|
jaroslav@601
|
208 |
* @return an array containing an element for each persistent field of
|
jaroslav@601
|
209 |
* this class. Returns an array of length zero if there are no
|
jaroslav@601
|
210 |
* fields.
|
jaroslav@601
|
211 |
* @since 1.2
|
jaroslav@601
|
212 |
*/
|
jaroslav@601
|
213 |
public ObjectStreamField[] getFields() {
|
jaroslav@601
|
214 |
return getFields(true);
|
jaroslav@601
|
215 |
}
|
jaroslav@601
|
216 |
|
jaroslav@601
|
217 |
/**
|
jaroslav@601
|
218 |
* Get the field of this class by name.
|
jaroslav@601
|
219 |
*
|
jaroslav@601
|
220 |
* @param name the name of the data field to look for
|
jaroslav@601
|
221 |
* @return The ObjectStreamField object of the named field or null if
|
jaroslav@601
|
222 |
* there is no such named field.
|
jaroslav@601
|
223 |
*/
|
jaroslav@601
|
224 |
public ObjectStreamField getField(String name) {
|
jaroslav@601
|
225 |
return getField(name, null);
|
jaroslav@601
|
226 |
}
|
jaroslav@601
|
227 |
|
jaroslav@601
|
228 |
/**
|
jaroslav@601
|
229 |
* Return a string describing this ObjectStreamClass.
|
jaroslav@601
|
230 |
*/
|
jaroslav@601
|
231 |
public String toString() {
|
jaroslav@601
|
232 |
return name + ": static final long serialVersionUID = " +
|
jaroslav@601
|
233 |
getSerialVersionUID() + "L;";
|
jaroslav@601
|
234 |
}
|
jaroslav@601
|
235 |
|
jaroslav@601
|
236 |
/**
|
jaroslav@601
|
237 |
* Looks up and returns class descriptor for given class, or null if class
|
jaroslav@601
|
238 |
* is non-serializable and "all" is set to false.
|
jaroslav@601
|
239 |
*
|
jaroslav@601
|
240 |
* @param cl class to look up
|
jaroslav@601
|
241 |
* @param all if true, return descriptors for all classes; if false, only
|
jaroslav@601
|
242 |
* return descriptors for serializable classes
|
jaroslav@601
|
243 |
*/
|
jaroslav@601
|
244 |
static ObjectStreamClass lookup(Class<?> cl, boolean all) {
|
jaroslav@601
|
245 |
if (!(all || Serializable.class.isAssignableFrom(cl))) {
|
jaroslav@601
|
246 |
return null;
|
jaroslav@601
|
247 |
}
|
jaroslav@601
|
248 |
Object entry = null;
|
jaroslav@601
|
249 |
EntryFuture future = null;
|
jaroslav@601
|
250 |
if (entry == null) {
|
jaroslav@601
|
251 |
EntryFuture newEntry = new EntryFuture();
|
jaroslav@601
|
252 |
Reference<?> newRef = new SoftReference<>(newEntry);
|
jaroslav@601
|
253 |
if (entry == null) {
|
jaroslav@601
|
254 |
future = newEntry;
|
jaroslav@601
|
255 |
}
|
jaroslav@601
|
256 |
}
|
jaroslav@601
|
257 |
|
jaroslav@601
|
258 |
if (entry instanceof ObjectStreamClass) { // check common case first
|
jaroslav@601
|
259 |
return (ObjectStreamClass) entry;
|
jaroslav@601
|
260 |
}
|
jaroslav@601
|
261 |
if (entry instanceof EntryFuture) {
|
jaroslav@601
|
262 |
future = (EntryFuture) entry;
|
jaroslav@604
|
263 |
if (true) {
|
jaroslav@601
|
264 |
/*
|
jaroslav@601
|
265 |
* Handle nested call situation described by 4803747: waiting
|
jaroslav@601
|
266 |
* for future value to be set by a lookup() call further up the
|
jaroslav@601
|
267 |
* stack will result in deadlock, so calculate and set the
|
jaroslav@601
|
268 |
* future value here instead.
|
jaroslav@601
|
269 |
*/
|
jaroslav@601
|
270 |
entry = null;
|
jaroslav@601
|
271 |
} else {
|
jaroslav@601
|
272 |
entry = future.get();
|
jaroslav@601
|
273 |
}
|
jaroslav@601
|
274 |
}
|
jaroslav@601
|
275 |
if (entry == null) {
|
jaroslav@601
|
276 |
try {
|
jaroslav@601
|
277 |
entry = new ObjectStreamClass(cl);
|
jaroslav@601
|
278 |
} catch (Throwable th) {
|
jaroslav@601
|
279 |
entry = th;
|
jaroslav@601
|
280 |
}
|
jaroslav@604
|
281 |
// nested lookup call already set future
|
jaroslav@604
|
282 |
entry = future.get();
|
jaroslav@601
|
283 |
}
|
jaroslav@601
|
284 |
|
jaroslav@601
|
285 |
if (entry instanceof ObjectStreamClass) {
|
jaroslav@601
|
286 |
return (ObjectStreamClass) entry;
|
jaroslav@601
|
287 |
} else if (entry instanceof RuntimeException) {
|
jaroslav@601
|
288 |
throw (RuntimeException) entry;
|
jaroslav@601
|
289 |
} else if (entry instanceof Error) {
|
jaroslav@601
|
290 |
throw (Error) entry;
|
jaroslav@601
|
291 |
} else {
|
jaroslav@601
|
292 |
throw new InternalError("unexpected entry: " + entry);
|
jaroslav@601
|
293 |
}
|
jaroslav@601
|
294 |
}
|
jaroslav@601
|
295 |
|
jaroslav@601
|
296 |
/**
|
jaroslav@601
|
297 |
* Placeholder used in class descriptor and field reflector lookup tables
|
jaroslav@601
|
298 |
* for an entry in the process of being initialized. (Internal) callers
|
jaroslav@601
|
299 |
* which receive an EntryFuture belonging to another thread as the result
|
jaroslav@601
|
300 |
* of a lookup should call the get() method of the EntryFuture; this will
|
jaroslav@601
|
301 |
* return the actual entry once it is ready for use and has been set(). To
|
jaroslav@601
|
302 |
* conserve objects, EntryFutures synchronize on themselves.
|
jaroslav@601
|
303 |
*/
|
jaroslav@601
|
304 |
private static class EntryFuture {
|
jaroslav@601
|
305 |
|
jaroslav@601
|
306 |
private static final Object unset = new Object();
|
jaroslav@601
|
307 |
private Object entry = unset;
|
jaroslav@601
|
308 |
|
jaroslav@601
|
309 |
/**
|
jaroslav@601
|
310 |
* Attempts to set the value contained by this EntryFuture. If the
|
jaroslav@601
|
311 |
* EntryFuture's value has not been set already, then the value is
|
jaroslav@601
|
312 |
* saved, any callers blocked in the get() method are notified, and
|
jaroslav@601
|
313 |
* true is returned. If the value has already been set, then no saving
|
jaroslav@601
|
314 |
* or notification occurs, and false is returned.
|
jaroslav@601
|
315 |
*/
|
jaroslav@601
|
316 |
synchronized boolean set(Object entry) {
|
jaroslav@601
|
317 |
if (this.entry != unset) {
|
jaroslav@601
|
318 |
return false;
|
jaroslav@601
|
319 |
}
|
jaroslav@601
|
320 |
this.entry = entry;
|
jaroslav@601
|
321 |
notifyAll();
|
jaroslav@601
|
322 |
return true;
|
jaroslav@601
|
323 |
}
|
jaroslav@601
|
324 |
|
jaroslav@601
|
325 |
/**
|
jaroslav@601
|
326 |
* Returns the value contained by this EntryFuture, blocking if
|
jaroslav@601
|
327 |
* necessary until a value is set.
|
jaroslav@601
|
328 |
*/
|
jaroslav@601
|
329 |
synchronized Object get() {
|
jaroslav@601
|
330 |
boolean interrupted = false;
|
jaroslav@601
|
331 |
while (entry == unset) {
|
jaroslav@601
|
332 |
try {
|
jaroslav@601
|
333 |
wait();
|
jaroslav@601
|
334 |
} catch (InterruptedException ex) {
|
jaroslav@601
|
335 |
interrupted = true;
|
jaroslav@601
|
336 |
}
|
jaroslav@601
|
337 |
}
|
jaroslav@601
|
338 |
return entry;
|
jaroslav@601
|
339 |
}
|
jaroslav@601
|
340 |
}
|
jaroslav@601
|
341 |
|
jaroslav@601
|
342 |
/**
|
jaroslav@601
|
343 |
* Creates local class descriptor representing given class.
|
jaroslav@601
|
344 |
*/
|
jaroslav@601
|
345 |
private ObjectStreamClass(final Class<?> cl) {
|
jaroslav@601
|
346 |
this.cl = cl;
|
jaroslav@601
|
347 |
name = cl.getName();
|
jaroslav@601
|
348 |
isProxy = Proxy.isProxyClass(cl);
|
jaroslav@601
|
349 |
isEnum = Enum.class.isAssignableFrom(cl);
|
jaroslav@601
|
350 |
serializable = Serializable.class.isAssignableFrom(cl);
|
jaroslav@601
|
351 |
externalizable = Externalizable.class.isAssignableFrom(cl);
|
jaroslav@601
|
352 |
|
jaroslav@601
|
353 |
Class<?> superCl = cl.getSuperclass();
|
jaroslav@601
|
354 |
superDesc = (superCl != null) ? lookup(superCl, false) : null;
|
jaroslav@601
|
355 |
localDesc = this;
|
jaroslav@601
|
356 |
|
jaroslav@604
|
357 |
suid = Long.valueOf(0);
|
jaroslav@604
|
358 |
fields = NO_FIELDS;
|
jaroslav@601
|
359 |
|
jaroslav@601
|
360 |
|
jaroslav@601
|
361 |
if (deserializeEx == null) {
|
jaroslav@601
|
362 |
if (isEnum) {
|
jaroslav@601
|
363 |
deserializeEx = new InvalidClassException(name, "enum type");
|
jaroslav@601
|
364 |
} else if (cons == null) {
|
jaroslav@601
|
365 |
deserializeEx = new InvalidClassException(
|
jaroslav@601
|
366 |
name, "no valid constructor");
|
jaroslav@601
|
367 |
}
|
jaroslav@601
|
368 |
}
|
jaroslav@601
|
369 |
for (int i = 0; i < fields.length; i++) {
|
jaroslav@601
|
370 |
if (fields[i].getField() == null) {
|
jaroslav@601
|
371 |
defaultSerializeEx = new InvalidClassException(
|
jaroslav@601
|
372 |
name, "unmatched serializable field(s) declared");
|
jaroslav@601
|
373 |
}
|
jaroslav@601
|
374 |
}
|
jaroslav@601
|
375 |
}
|
jaroslav@601
|
376 |
|
jaroslav@601
|
377 |
/**
|
jaroslav@601
|
378 |
* Creates blank class descriptor which should be initialized via a
|
jaroslav@601
|
379 |
* subsequent call to initProxy(), initNonProxy() or readNonProxy().
|
jaroslav@601
|
380 |
*/
|
jaroslav@601
|
381 |
ObjectStreamClass() {
|
jaroslav@601
|
382 |
}
|
jaroslav@601
|
383 |
|
jaroslav@601
|
384 |
/**
|
jaroslav@601
|
385 |
* Initializes class descriptor representing a proxy class.
|
jaroslav@601
|
386 |
*/
|
jaroslav@601
|
387 |
void initProxy(Class<?> cl,
|
jaroslav@601
|
388 |
ClassNotFoundException resolveEx,
|
jaroslav@601
|
389 |
ObjectStreamClass superDesc)
|
jaroslav@601
|
390 |
throws InvalidClassException
|
jaroslav@601
|
391 |
{
|
jaroslav@601
|
392 |
this.cl = cl;
|
jaroslav@601
|
393 |
this.resolveEx = resolveEx;
|
jaroslav@601
|
394 |
this.superDesc = superDesc;
|
jaroslav@601
|
395 |
isProxy = true;
|
jaroslav@601
|
396 |
serializable = true;
|
jaroslav@601
|
397 |
suid = Long.valueOf(0);
|
jaroslav@601
|
398 |
fields = NO_FIELDS;
|
jaroslav@601
|
399 |
|
jaroslav@601
|
400 |
if (cl != null) {
|
jaroslav@601
|
401 |
localDesc = lookup(cl, true);
|
jaroslav@601
|
402 |
if (!localDesc.isProxy) {
|
jaroslav@601
|
403 |
throw new InvalidClassException(
|
jaroslav@601
|
404 |
"cannot bind proxy descriptor to a non-proxy class");
|
jaroslav@601
|
405 |
}
|
jaroslav@601
|
406 |
name = localDesc.name;
|
jaroslav@601
|
407 |
externalizable = localDesc.externalizable;
|
jaroslav@601
|
408 |
cons = localDesc.cons;
|
jaroslav@601
|
409 |
writeReplaceMethod = localDesc.writeReplaceMethod;
|
jaroslav@601
|
410 |
readResolveMethod = localDesc.readResolveMethod;
|
jaroslav@601
|
411 |
deserializeEx = localDesc.deserializeEx;
|
jaroslav@601
|
412 |
}
|
jaroslav@601
|
413 |
}
|
jaroslav@601
|
414 |
|
jaroslav@601
|
415 |
/**
|
jaroslav@601
|
416 |
* Initializes class descriptor representing a non-proxy class.
|
jaroslav@601
|
417 |
*/
|
jaroslav@601
|
418 |
void initNonProxy(ObjectStreamClass model,
|
jaroslav@601
|
419 |
Class<?> cl,
|
jaroslav@601
|
420 |
ClassNotFoundException resolveEx,
|
jaroslav@601
|
421 |
ObjectStreamClass superDesc)
|
jaroslav@601
|
422 |
throws InvalidClassException
|
jaroslav@601
|
423 |
{
|
jaroslav@601
|
424 |
this.cl = cl;
|
jaroslav@601
|
425 |
this.resolveEx = resolveEx;
|
jaroslav@601
|
426 |
this.superDesc = superDesc;
|
jaroslav@601
|
427 |
name = model.name;
|
jaroslav@601
|
428 |
suid = Long.valueOf(model.getSerialVersionUID());
|
jaroslav@601
|
429 |
isProxy = false;
|
jaroslav@601
|
430 |
isEnum = model.isEnum;
|
jaroslav@601
|
431 |
serializable = model.serializable;
|
jaroslav@601
|
432 |
externalizable = model.externalizable;
|
jaroslav@601
|
433 |
hasBlockExternalData = model.hasBlockExternalData;
|
jaroslav@601
|
434 |
hasWriteObjectData = model.hasWriteObjectData;
|
jaroslav@601
|
435 |
fields = model.fields;
|
jaroslav@601
|
436 |
primDataSize = model.primDataSize;
|
jaroslav@601
|
437 |
numObjFields = model.numObjFields;
|
jaroslav@601
|
438 |
|
jaroslav@601
|
439 |
if (cl != null) {
|
jaroslav@601
|
440 |
localDesc = lookup(cl, true);
|
jaroslav@601
|
441 |
if (localDesc.isProxy) {
|
jaroslav@601
|
442 |
throw new InvalidClassException(
|
jaroslav@601
|
443 |
"cannot bind non-proxy descriptor to a proxy class");
|
jaroslav@601
|
444 |
}
|
jaroslav@601
|
445 |
if (isEnum != localDesc.isEnum) {
|
jaroslav@601
|
446 |
throw new InvalidClassException(isEnum ?
|
jaroslav@601
|
447 |
"cannot bind enum descriptor to a non-enum class" :
|
jaroslav@601
|
448 |
"cannot bind non-enum descriptor to an enum class");
|
jaroslav@601
|
449 |
}
|
jaroslav@601
|
450 |
|
jaroslav@601
|
451 |
if (serializable == localDesc.serializable &&
|
jaroslav@601
|
452 |
!cl.isArray() &&
|
jaroslav@601
|
453 |
suid.longValue() != localDesc.getSerialVersionUID())
|
jaroslav@601
|
454 |
{
|
jaroslav@601
|
455 |
throw new InvalidClassException(localDesc.name,
|
jaroslav@601
|
456 |
"local class incompatible: " +
|
jaroslav@601
|
457 |
"stream classdesc serialVersionUID = " + suid +
|
jaroslav@601
|
458 |
", local class serialVersionUID = " +
|
jaroslav@601
|
459 |
localDesc.getSerialVersionUID());
|
jaroslav@601
|
460 |
}
|
jaroslav@601
|
461 |
|
jaroslav@601
|
462 |
if (!classNamesEqual(name, localDesc.name)) {
|
jaroslav@601
|
463 |
throw new InvalidClassException(localDesc.name,
|
jaroslav@601
|
464 |
"local class name incompatible with stream class " +
|
jaroslav@601
|
465 |
"name \"" + name + "\"");
|
jaroslav@601
|
466 |
}
|
jaroslav@601
|
467 |
|
jaroslav@601
|
468 |
if (!isEnum) {
|
jaroslav@601
|
469 |
if ((serializable == localDesc.serializable) &&
|
jaroslav@601
|
470 |
(externalizable != localDesc.externalizable))
|
jaroslav@601
|
471 |
{
|
jaroslav@601
|
472 |
throw new InvalidClassException(localDesc.name,
|
jaroslav@601
|
473 |
"Serializable incompatible with Externalizable");
|
jaroslav@601
|
474 |
}
|
jaroslav@601
|
475 |
|
jaroslav@601
|
476 |
if ((serializable != localDesc.serializable) ||
|
jaroslav@601
|
477 |
(externalizable != localDesc.externalizable) ||
|
jaroslav@601
|
478 |
!(serializable || externalizable))
|
jaroslav@601
|
479 |
{
|
jaroslav@601
|
480 |
deserializeEx = new InvalidClassException(localDesc.name,
|
jaroslav@601
|
481 |
"class invalid for deserialization");
|
jaroslav@601
|
482 |
}
|
jaroslav@601
|
483 |
}
|
jaroslav@601
|
484 |
|
jaroslav@601
|
485 |
cons = localDesc.cons;
|
jaroslav@601
|
486 |
writeObjectMethod = localDesc.writeObjectMethod;
|
jaroslav@601
|
487 |
readObjectMethod = localDesc.readObjectMethod;
|
jaroslav@601
|
488 |
readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
|
jaroslav@601
|
489 |
writeReplaceMethod = localDesc.writeReplaceMethod;
|
jaroslav@601
|
490 |
readResolveMethod = localDesc.readResolveMethod;
|
jaroslav@601
|
491 |
if (deserializeEx == null) {
|
jaroslav@601
|
492 |
deserializeEx = localDesc.deserializeEx;
|
jaroslav@601
|
493 |
}
|
jaroslav@601
|
494 |
}
|
jaroslav@601
|
495 |
// reassign to matched fields so as to reflect local unshared settings
|
jaroslav@604
|
496 |
fields = null;
|
jaroslav@601
|
497 |
}
|
jaroslav@601
|
498 |
|
jaroslav@601
|
499 |
/**
|
jaroslav@601
|
500 |
* Reads non-proxy class descriptor information from given input stream.
|
jaroslav@601
|
501 |
* The resulting class descriptor is not fully functional; it can only be
|
jaroslav@601
|
502 |
* used as input to the ObjectInputStream.resolveClass() and
|
jaroslav@601
|
503 |
* ObjectStreamClass.initNonProxy() methods.
|
jaroslav@601
|
504 |
*/
|
jaroslav@601
|
505 |
void readNonProxy(ObjectInputStream in)
|
jaroslav@601
|
506 |
throws IOException, ClassNotFoundException
|
jaroslav@601
|
507 |
{
|
jaroslav@601
|
508 |
name = in.readUTF();
|
jaroslav@601
|
509 |
suid = Long.valueOf(in.readLong());
|
jaroslav@601
|
510 |
isProxy = false;
|
jaroslav@601
|
511 |
|
jaroslav@601
|
512 |
byte flags = in.readByte();
|
jaroslav@601
|
513 |
hasWriteObjectData =
|
jaroslav@601
|
514 |
((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
|
jaroslav@601
|
515 |
hasBlockExternalData =
|
jaroslav@601
|
516 |
((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
|
jaroslav@601
|
517 |
externalizable =
|
jaroslav@601
|
518 |
((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
|
jaroslav@601
|
519 |
boolean sflag =
|
jaroslav@601
|
520 |
((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
|
jaroslav@601
|
521 |
if (externalizable && sflag) {
|
jaroslav@601
|
522 |
throw new InvalidClassException(
|
jaroslav@601
|
523 |
name, "serializable and externalizable flags conflict");
|
jaroslav@601
|
524 |
}
|
jaroslav@601
|
525 |
serializable = externalizable || sflag;
|
jaroslav@601
|
526 |
isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
|
jaroslav@601
|
527 |
if (isEnum && suid.longValue() != 0L) {
|
jaroslav@601
|
528 |
throw new InvalidClassException(name,
|
jaroslav@601
|
529 |
"enum descriptor has non-zero serialVersionUID: " + suid);
|
jaroslav@601
|
530 |
}
|
jaroslav@601
|
531 |
|
jaroslav@601
|
532 |
int numFields = in.readShort();
|
jaroslav@601
|
533 |
if (isEnum && numFields != 0) {
|
jaroslav@601
|
534 |
throw new InvalidClassException(name,
|
jaroslav@601
|
535 |
"enum descriptor has non-zero field count: " + numFields);
|
jaroslav@601
|
536 |
}
|
jaroslav@601
|
537 |
fields = (numFields > 0) ?
|
jaroslav@601
|
538 |
new ObjectStreamField[numFields] : NO_FIELDS;
|
jaroslav@601
|
539 |
for (int i = 0; i < numFields; i++) {
|
jaroslav@601
|
540 |
char tcode = (char) in.readByte();
|
jaroslav@601
|
541 |
String fname = in.readUTF();
|
jaroslav@601
|
542 |
String signature = ((tcode == 'L') || (tcode == '[')) ?
|
jaroslav@601
|
543 |
in.readTypeString() : new String(new char[] { tcode });
|
jaroslav@601
|
544 |
try {
|
jaroslav@601
|
545 |
fields[i] = new ObjectStreamField(fname, signature, false);
|
jaroslav@601
|
546 |
} catch (RuntimeException e) {
|
jaroslav@601
|
547 |
throw (IOException) new InvalidClassException(name,
|
jaroslav@601
|
548 |
"invalid descriptor for field " + fname).initCause(e);
|
jaroslav@601
|
549 |
}
|
jaroslav@601
|
550 |
}
|
jaroslav@601
|
551 |
computeFieldOffsets();
|
jaroslav@601
|
552 |
}
|
jaroslav@601
|
553 |
|
jaroslav@601
|
554 |
/**
|
jaroslav@601
|
555 |
* Writes non-proxy class descriptor information to given output stream.
|
jaroslav@601
|
556 |
*/
|
jaroslav@601
|
557 |
void writeNonProxy(ObjectOutputStream out) throws IOException {
|
jaroslav@601
|
558 |
out.writeUTF(name);
|
jaroslav@601
|
559 |
out.writeLong(getSerialVersionUID());
|
jaroslav@601
|
560 |
|
jaroslav@601
|
561 |
byte flags = 0;
|
jaroslav@601
|
562 |
if (externalizable) {
|
jaroslav@601
|
563 |
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
|
jaroslav@601
|
564 |
int protocol = out.getProtocolVersion();
|
jaroslav@601
|
565 |
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
|
jaroslav@601
|
566 |
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
|
jaroslav@601
|
567 |
}
|
jaroslav@601
|
568 |
} else if (serializable) {
|
jaroslav@601
|
569 |
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
|
jaroslav@601
|
570 |
}
|
jaroslav@601
|
571 |
if (hasWriteObjectData) {
|
jaroslav@601
|
572 |
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
|
jaroslav@601
|
573 |
}
|
jaroslav@601
|
574 |
if (isEnum) {
|
jaroslav@601
|
575 |
flags |= ObjectStreamConstants.SC_ENUM;
|
jaroslav@601
|
576 |
}
|
jaroslav@601
|
577 |
out.writeByte(flags);
|
jaroslav@601
|
578 |
|
jaroslav@601
|
579 |
out.writeShort(fields.length);
|
jaroslav@601
|
580 |
for (int i = 0; i < fields.length; i++) {
|
jaroslav@601
|
581 |
ObjectStreamField f = fields[i];
|
jaroslav@601
|
582 |
out.writeByte(f.getTypeCode());
|
jaroslav@601
|
583 |
out.writeUTF(f.getName());
|
jaroslav@601
|
584 |
if (!f.isPrimitive()) {
|
jaroslav@601
|
585 |
out.writeTypeString(f.getTypeString());
|
jaroslav@601
|
586 |
}
|
jaroslav@601
|
587 |
}
|
jaroslav@601
|
588 |
}
|
jaroslav@601
|
589 |
|
jaroslav@601
|
590 |
/**
|
jaroslav@601
|
591 |
* Returns ClassNotFoundException (if any) thrown while attempting to
|
jaroslav@601
|
592 |
* resolve local class corresponding to this class descriptor.
|
jaroslav@601
|
593 |
*/
|
jaroslav@601
|
594 |
ClassNotFoundException getResolveException() {
|
jaroslav@601
|
595 |
return resolveEx;
|
jaroslav@601
|
596 |
}
|
jaroslav@601
|
597 |
|
jaroslav@601
|
598 |
/**
|
jaroslav@601
|
599 |
* Throws an InvalidClassException if object instances referencing this
|
jaroslav@601
|
600 |
* class descriptor should not be allowed to deserialize. This method does
|
jaroslav@601
|
601 |
* not apply to deserialization of enum constants.
|
jaroslav@601
|
602 |
*/
|
jaroslav@601
|
603 |
void checkDeserialize() throws InvalidClassException {
|
jaroslav@601
|
604 |
if (deserializeEx != null) {
|
jaroslav@601
|
605 |
InvalidClassException ice =
|
jaroslav@601
|
606 |
new InvalidClassException(deserializeEx.classname,
|
jaroslav@601
|
607 |
deserializeEx.getMessage());
|
jaroslav@601
|
608 |
ice.initCause(deserializeEx);
|
jaroslav@601
|
609 |
throw ice;
|
jaroslav@601
|
610 |
}
|
jaroslav@601
|
611 |
}
|
jaroslav@601
|
612 |
|
jaroslav@601
|
613 |
/**
|
jaroslav@601
|
614 |
* Throws an InvalidClassException if objects whose class is represented by
|
jaroslav@601
|
615 |
* this descriptor should not be allowed to serialize. This method does
|
jaroslav@601
|
616 |
* not apply to serialization of enum constants.
|
jaroslav@601
|
617 |
*/
|
jaroslav@601
|
618 |
void checkSerialize() throws InvalidClassException {
|
jaroslav@601
|
619 |
if (serializeEx != null) {
|
jaroslav@601
|
620 |
InvalidClassException ice =
|
jaroslav@601
|
621 |
new InvalidClassException(serializeEx.classname,
|
jaroslav@601
|
622 |
serializeEx.getMessage());
|
jaroslav@601
|
623 |
ice.initCause(serializeEx);
|
jaroslav@601
|
624 |
throw ice;
|
jaroslav@601
|
625 |
}
|
jaroslav@601
|
626 |
}
|
jaroslav@601
|
627 |
|
jaroslav@601
|
628 |
/**
|
jaroslav@601
|
629 |
* Throws an InvalidClassException if objects whose class is represented by
|
jaroslav@601
|
630 |
* this descriptor should not be permitted to use default serialization
|
jaroslav@601
|
631 |
* (e.g., if the class declares serializable fields that do not correspond
|
jaroslav@601
|
632 |
* to actual fields, and hence must use the GetField API). This method
|
jaroslav@601
|
633 |
* does not apply to deserialization of enum constants.
|
jaroslav@601
|
634 |
*/
|
jaroslav@601
|
635 |
void checkDefaultSerialize() throws InvalidClassException {
|
jaroslav@601
|
636 |
if (defaultSerializeEx != null) {
|
jaroslav@601
|
637 |
InvalidClassException ice =
|
jaroslav@601
|
638 |
new InvalidClassException(defaultSerializeEx.classname,
|
jaroslav@601
|
639 |
defaultSerializeEx.getMessage());
|
jaroslav@601
|
640 |
ice.initCause(defaultSerializeEx);
|
jaroslav@601
|
641 |
throw ice;
|
jaroslav@601
|
642 |
}
|
jaroslav@601
|
643 |
}
|
jaroslav@601
|
644 |
|
jaroslav@601
|
645 |
/**
|
jaroslav@601
|
646 |
* Returns superclass descriptor. Note that on the receiving side, the
|
jaroslav@601
|
647 |
* superclass descriptor may be bound to a class that is not a superclass
|
jaroslav@601
|
648 |
* of the subclass descriptor's bound class.
|
jaroslav@601
|
649 |
*/
|
jaroslav@601
|
650 |
ObjectStreamClass getSuperDesc() {
|
jaroslav@601
|
651 |
return superDesc;
|
jaroslav@601
|
652 |
}
|
jaroslav@601
|
653 |
|
jaroslav@601
|
654 |
/**
|
jaroslav@601
|
655 |
* Returns the "local" class descriptor for the class associated with this
|
jaroslav@601
|
656 |
* class descriptor (i.e., the result of
|
jaroslav@601
|
657 |
* ObjectStreamClass.lookup(this.forClass())) or null if there is no class
|
jaroslav@601
|
658 |
* associated with this descriptor.
|
jaroslav@601
|
659 |
*/
|
jaroslav@601
|
660 |
ObjectStreamClass getLocalDesc() {
|
jaroslav@601
|
661 |
return localDesc;
|
jaroslav@601
|
662 |
}
|
jaroslav@601
|
663 |
|
jaroslav@601
|
664 |
/**
|
jaroslav@601
|
665 |
* Returns arrays of ObjectStreamFields representing the serializable
|
jaroslav@601
|
666 |
* fields of the represented class. If copy is true, a clone of this class
|
jaroslav@601
|
667 |
* descriptor's field array is returned, otherwise the array itself is
|
jaroslav@601
|
668 |
* returned.
|
jaroslav@601
|
669 |
*/
|
jaroslav@601
|
670 |
ObjectStreamField[] getFields(boolean copy) {
|
jaroslav@601
|
671 |
return copy ? fields.clone() : fields;
|
jaroslav@601
|
672 |
}
|
jaroslav@601
|
673 |
|
jaroslav@601
|
674 |
/**
|
jaroslav@601
|
675 |
* Looks up a serializable field of the represented class by name and type.
|
jaroslav@601
|
676 |
* A specified type of null matches all types, Object.class matches all
|
jaroslav@601
|
677 |
* non-primitive types, and any other non-null type matches assignable
|
jaroslav@601
|
678 |
* types only. Returns matching field, or null if no match found.
|
jaroslav@601
|
679 |
*/
|
jaroslav@601
|
680 |
ObjectStreamField getField(String name, Class<?> type) {
|
jaroslav@601
|
681 |
for (int i = 0; i < fields.length; i++) {
|
jaroslav@601
|
682 |
ObjectStreamField f = fields[i];
|
jaroslav@601
|
683 |
if (f.getName().equals(name)) {
|
jaroslav@601
|
684 |
if (type == null ||
|
jaroslav@601
|
685 |
(type == Object.class && !f.isPrimitive()))
|
jaroslav@601
|
686 |
{
|
jaroslav@601
|
687 |
return f;
|
jaroslav@601
|
688 |
}
|
jaroslav@601
|
689 |
Class<?> ftype = f.getType();
|
jaroslav@601
|
690 |
if (ftype != null && type.isAssignableFrom(ftype)) {
|
jaroslav@601
|
691 |
return f;
|
jaroslav@601
|
692 |
}
|
jaroslav@601
|
693 |
}
|
jaroslav@601
|
694 |
}
|
jaroslav@601
|
695 |
return null;
|
jaroslav@601
|
696 |
}
|
jaroslav@601
|
697 |
|
jaroslav@601
|
698 |
/**
|
jaroslav@601
|
699 |
* Returns true if class descriptor represents a dynamic proxy class, false
|
jaroslav@601
|
700 |
* otherwise.
|
jaroslav@601
|
701 |
*/
|
jaroslav@601
|
702 |
boolean isProxy() {
|
jaroslav@601
|
703 |
return isProxy;
|
jaroslav@601
|
704 |
}
|
jaroslav@601
|
705 |
|
jaroslav@601
|
706 |
/**
|
jaroslav@601
|
707 |
* Returns true if class descriptor represents an enum type, false
|
jaroslav@601
|
708 |
* otherwise.
|
jaroslav@601
|
709 |
*/
|
jaroslav@601
|
710 |
boolean isEnum() {
|
jaroslav@601
|
711 |
return isEnum;
|
jaroslav@601
|
712 |
}
|
jaroslav@601
|
713 |
|
jaroslav@601
|
714 |
/**
|
jaroslav@601
|
715 |
* Returns true if represented class implements Externalizable, false
|
jaroslav@601
|
716 |
* otherwise.
|
jaroslav@601
|
717 |
*/
|
jaroslav@601
|
718 |
boolean isExternalizable() {
|
jaroslav@601
|
719 |
return externalizable;
|
jaroslav@601
|
720 |
}
|
jaroslav@601
|
721 |
|
jaroslav@601
|
722 |
/**
|
jaroslav@601
|
723 |
* Returns true if represented class implements Serializable, false
|
jaroslav@601
|
724 |
* otherwise.
|
jaroslav@601
|
725 |
*/
|
jaroslav@601
|
726 |
boolean isSerializable() {
|
jaroslav@601
|
727 |
return serializable;
|
jaroslav@601
|
728 |
}
|
jaroslav@601
|
729 |
|
jaroslav@601
|
730 |
/**
|
jaroslav@601
|
731 |
* Returns true if class descriptor represents externalizable class that
|
jaroslav@601
|
732 |
* has written its data in 1.2 (block data) format, false otherwise.
|
jaroslav@601
|
733 |
*/
|
jaroslav@601
|
734 |
boolean hasBlockExternalData() {
|
jaroslav@601
|
735 |
return hasBlockExternalData;
|
jaroslav@601
|
736 |
}
|
jaroslav@601
|
737 |
|
jaroslav@601
|
738 |
/**
|
jaroslav@601
|
739 |
* Returns true if class descriptor represents serializable (but not
|
jaroslav@601
|
740 |
* externalizable) class which has written its data via a custom
|
jaroslav@601
|
741 |
* writeObject() method, false otherwise.
|
jaroslav@601
|
742 |
*/
|
jaroslav@601
|
743 |
boolean hasWriteObjectData() {
|
jaroslav@601
|
744 |
return hasWriteObjectData;
|
jaroslav@601
|
745 |
}
|
jaroslav@601
|
746 |
|
jaroslav@601
|
747 |
/**
|
jaroslav@601
|
748 |
* Returns true if represented class is serializable/externalizable and can
|
jaroslav@601
|
749 |
* be instantiated by the serialization runtime--i.e., if it is
|
jaroslav@601
|
750 |
* externalizable and defines a public no-arg constructor, or if it is
|
jaroslav@601
|
751 |
* non-externalizable and its first non-serializable superclass defines an
|
jaroslav@601
|
752 |
* accessible no-arg constructor. Otherwise, returns false.
|
jaroslav@601
|
753 |
*/
|
jaroslav@601
|
754 |
boolean isInstantiable() {
|
jaroslav@601
|
755 |
return (cons != null);
|
jaroslav@601
|
756 |
}
|
jaroslav@601
|
757 |
|
jaroslav@601
|
758 |
/**
|
jaroslav@601
|
759 |
* Returns true if represented class is serializable (but not
|
jaroslav@601
|
760 |
* externalizable) and defines a conformant writeObject method. Otherwise,
|
jaroslav@601
|
761 |
* returns false.
|
jaroslav@601
|
762 |
*/
|
jaroslav@601
|
763 |
boolean hasWriteObjectMethod() {
|
jaroslav@601
|
764 |
return (writeObjectMethod != null);
|
jaroslav@601
|
765 |
}
|
jaroslav@601
|
766 |
|
jaroslav@601
|
767 |
/**
|
jaroslav@601
|
768 |
* Returns true if represented class is serializable (but not
|
jaroslav@601
|
769 |
* externalizable) and defines a conformant readObject method. Otherwise,
|
jaroslav@601
|
770 |
* returns false.
|
jaroslav@601
|
771 |
*/
|
jaroslav@601
|
772 |
boolean hasReadObjectMethod() {
|
jaroslav@601
|
773 |
return (readObjectMethod != null);
|
jaroslav@601
|
774 |
}
|
jaroslav@601
|
775 |
|
jaroslav@601
|
776 |
/**
|
jaroslav@601
|
777 |
* Returns true if represented class is serializable (but not
|
jaroslav@601
|
778 |
* externalizable) and defines a conformant readObjectNoData method.
|
jaroslav@601
|
779 |
* Otherwise, returns false.
|
jaroslav@601
|
780 |
*/
|
jaroslav@601
|
781 |
boolean hasReadObjectNoDataMethod() {
|
jaroslav@601
|
782 |
return (readObjectNoDataMethod != null);
|
jaroslav@601
|
783 |
}
|
jaroslav@601
|
784 |
|
jaroslav@601
|
785 |
/**
|
jaroslav@601
|
786 |
* Returns true if represented class is serializable or externalizable and
|
jaroslav@601
|
787 |
* defines a conformant writeReplace method. Otherwise, returns false.
|
jaroslav@601
|
788 |
*/
|
jaroslav@601
|
789 |
boolean hasWriteReplaceMethod() {
|
jaroslav@601
|
790 |
return (writeReplaceMethod != null);
|
jaroslav@601
|
791 |
}
|
jaroslav@601
|
792 |
|
jaroslav@601
|
793 |
/**
|
jaroslav@601
|
794 |
* Returns true if represented class is serializable or externalizable and
|
jaroslav@601
|
795 |
* defines a conformant readResolve method. Otherwise, returns false.
|
jaroslav@601
|
796 |
*/
|
jaroslav@601
|
797 |
boolean hasReadResolveMethod() {
|
jaroslav@601
|
798 |
return (readResolveMethod != null);
|
jaroslav@601
|
799 |
}
|
jaroslav@601
|
800 |
|
jaroslav@601
|
801 |
/**
|
jaroslav@601
|
802 |
* Creates a new instance of the represented class. If the class is
|
jaroslav@601
|
803 |
* externalizable, invokes its public no-arg constructor; otherwise, if the
|
jaroslav@601
|
804 |
* class is serializable, invokes the no-arg constructor of the first
|
jaroslav@601
|
805 |
* non-serializable superclass. Throws UnsupportedOperationException if
|
jaroslav@601
|
806 |
* this class descriptor is not associated with a class, if the associated
|
jaroslav@601
|
807 |
* class is non-serializable or if the appropriate no-arg constructor is
|
jaroslav@601
|
808 |
* inaccessible/unavailable.
|
jaroslav@601
|
809 |
*/
|
jaroslav@601
|
810 |
Object newInstance()
|
jaroslav@601
|
811 |
throws InstantiationException, InvocationTargetException,
|
jaroslav@601
|
812 |
UnsupportedOperationException
|
jaroslav@601
|
813 |
{
|
jaroslav@601
|
814 |
if (cons != null) {
|
jaroslav@601
|
815 |
try {
|
jaroslav@601
|
816 |
return cons.newInstance();
|
jaroslav@601
|
817 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
818 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
819 |
throw new InternalError();
|
jaroslav@601
|
820 |
}
|
jaroslav@601
|
821 |
} else {
|
jaroslav@601
|
822 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
823 |
}
|
jaroslav@601
|
824 |
}
|
jaroslav@601
|
825 |
|
jaroslav@601
|
826 |
/**
|
jaroslav@601
|
827 |
* Invokes the writeObject method of the represented serializable class.
|
jaroslav@601
|
828 |
* Throws UnsupportedOperationException if this class descriptor is not
|
jaroslav@601
|
829 |
* associated with a class, or if the class is externalizable,
|
jaroslav@601
|
830 |
* non-serializable or does not define writeObject.
|
jaroslav@601
|
831 |
*/
|
jaroslav@601
|
832 |
void invokeWriteObject(Object obj, ObjectOutputStream out)
|
jaroslav@601
|
833 |
throws IOException, UnsupportedOperationException
|
jaroslav@601
|
834 |
{
|
jaroslav@601
|
835 |
if (writeObjectMethod != null) {
|
jaroslav@601
|
836 |
try {
|
jaroslav@601
|
837 |
writeObjectMethod.invoke(obj, new Object[]{ out });
|
jaroslav@601
|
838 |
} catch (InvocationTargetException ex) {
|
jaroslav@601
|
839 |
Throwable th = ex.getTargetException();
|
jaroslav@601
|
840 |
if (th instanceof IOException) {
|
jaroslav@601
|
841 |
throw (IOException) th;
|
jaroslav@601
|
842 |
} else {
|
jaroslav@601
|
843 |
throwMiscException(th);
|
jaroslav@601
|
844 |
}
|
jaroslav@601
|
845 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
846 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
847 |
throw new InternalError();
|
jaroslav@601
|
848 |
}
|
jaroslav@601
|
849 |
} else {
|
jaroslav@601
|
850 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
851 |
}
|
jaroslav@601
|
852 |
}
|
jaroslav@601
|
853 |
|
jaroslav@601
|
854 |
/**
|
jaroslav@601
|
855 |
* Invokes the readObject method of the represented serializable class.
|
jaroslav@601
|
856 |
* Throws UnsupportedOperationException if this class descriptor is not
|
jaroslav@601
|
857 |
* associated with a class, or if the class is externalizable,
|
jaroslav@601
|
858 |
* non-serializable or does not define readObject.
|
jaroslav@601
|
859 |
*/
|
jaroslav@601
|
860 |
void invokeReadObject(Object obj, ObjectInputStream in)
|
jaroslav@601
|
861 |
throws ClassNotFoundException, IOException,
|
jaroslav@601
|
862 |
UnsupportedOperationException
|
jaroslav@601
|
863 |
{
|
jaroslav@601
|
864 |
if (readObjectMethod != null) {
|
jaroslav@601
|
865 |
try {
|
jaroslav@601
|
866 |
readObjectMethod.invoke(obj, new Object[]{ in });
|
jaroslav@601
|
867 |
} catch (InvocationTargetException ex) {
|
jaroslav@601
|
868 |
Throwable th = ex.getTargetException();
|
jaroslav@601
|
869 |
if (th instanceof ClassNotFoundException) {
|
jaroslav@601
|
870 |
throw (ClassNotFoundException) th;
|
jaroslav@601
|
871 |
} else if (th instanceof IOException) {
|
jaroslav@601
|
872 |
throw (IOException) th;
|
jaroslav@601
|
873 |
} else {
|
jaroslav@601
|
874 |
throwMiscException(th);
|
jaroslav@601
|
875 |
}
|
jaroslav@601
|
876 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
877 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
878 |
throw new InternalError();
|
jaroslav@601
|
879 |
}
|
jaroslav@601
|
880 |
} else {
|
jaroslav@601
|
881 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
882 |
}
|
jaroslav@601
|
883 |
}
|
jaroslav@601
|
884 |
|
jaroslav@601
|
885 |
/**
|
jaroslav@601
|
886 |
* Invokes the readObjectNoData method of the represented serializable
|
jaroslav@601
|
887 |
* class. Throws UnsupportedOperationException if this class descriptor is
|
jaroslav@601
|
888 |
* not associated with a class, or if the class is externalizable,
|
jaroslav@601
|
889 |
* non-serializable or does not define readObjectNoData.
|
jaroslav@601
|
890 |
*/
|
jaroslav@601
|
891 |
void invokeReadObjectNoData(Object obj)
|
jaroslav@601
|
892 |
throws IOException, UnsupportedOperationException
|
jaroslav@601
|
893 |
{
|
jaroslav@601
|
894 |
if (readObjectNoDataMethod != null) {
|
jaroslav@601
|
895 |
try {
|
jaroslav@601
|
896 |
readObjectNoDataMethod.invoke(obj, (Object[]) null);
|
jaroslav@601
|
897 |
} catch (InvocationTargetException ex) {
|
jaroslav@601
|
898 |
Throwable th = ex.getTargetException();
|
jaroslav@601
|
899 |
if (th instanceof ObjectStreamException) {
|
jaroslav@601
|
900 |
throw (ObjectStreamException) th;
|
jaroslav@601
|
901 |
} else {
|
jaroslav@601
|
902 |
throwMiscException(th);
|
jaroslav@601
|
903 |
}
|
jaroslav@601
|
904 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
905 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
906 |
throw new InternalError();
|
jaroslav@601
|
907 |
}
|
jaroslav@601
|
908 |
} else {
|
jaroslav@601
|
909 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
910 |
}
|
jaroslav@601
|
911 |
}
|
jaroslav@601
|
912 |
|
jaroslav@601
|
913 |
/**
|
jaroslav@601
|
914 |
* Invokes the writeReplace method of the represented serializable class and
|
jaroslav@601
|
915 |
* returns the result. Throws UnsupportedOperationException if this class
|
jaroslav@601
|
916 |
* descriptor is not associated with a class, or if the class is
|
jaroslav@601
|
917 |
* non-serializable or does not define writeReplace.
|
jaroslav@601
|
918 |
*/
|
jaroslav@601
|
919 |
Object invokeWriteReplace(Object obj)
|
jaroslav@601
|
920 |
throws IOException, UnsupportedOperationException
|
jaroslav@601
|
921 |
{
|
jaroslav@601
|
922 |
if (writeReplaceMethod != null) {
|
jaroslav@601
|
923 |
try {
|
jaroslav@601
|
924 |
return writeReplaceMethod.invoke(obj, (Object[]) null);
|
jaroslav@601
|
925 |
} catch (InvocationTargetException ex) {
|
jaroslav@601
|
926 |
Throwable th = ex.getTargetException();
|
jaroslav@601
|
927 |
if (th instanceof ObjectStreamException) {
|
jaroslav@601
|
928 |
throw (ObjectStreamException) th;
|
jaroslav@601
|
929 |
} else {
|
jaroslav@601
|
930 |
throwMiscException(th);
|
jaroslav@601
|
931 |
throw new InternalError(); // never reached
|
jaroslav@601
|
932 |
}
|
jaroslav@601
|
933 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
934 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
935 |
throw new InternalError();
|
jaroslav@601
|
936 |
}
|
jaroslav@601
|
937 |
} else {
|
jaroslav@601
|
938 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
939 |
}
|
jaroslav@601
|
940 |
}
|
jaroslav@601
|
941 |
|
jaroslav@601
|
942 |
/**
|
jaroslav@601
|
943 |
* Invokes the readResolve method of the represented serializable class and
|
jaroslav@601
|
944 |
* returns the result. Throws UnsupportedOperationException if this class
|
jaroslav@601
|
945 |
* descriptor is not associated with a class, or if the class is
|
jaroslav@601
|
946 |
* non-serializable or does not define readResolve.
|
jaroslav@601
|
947 |
*/
|
jaroslav@601
|
948 |
Object invokeReadResolve(Object obj)
|
jaroslav@601
|
949 |
throws IOException, UnsupportedOperationException
|
jaroslav@601
|
950 |
{
|
jaroslav@601
|
951 |
if (readResolveMethod != null) {
|
jaroslav@601
|
952 |
try {
|
jaroslav@601
|
953 |
return readResolveMethod.invoke(obj, (Object[]) null);
|
jaroslav@601
|
954 |
} catch (InvocationTargetException ex) {
|
jaroslav@601
|
955 |
Throwable th = ex.getTargetException();
|
jaroslav@601
|
956 |
if (th instanceof ObjectStreamException) {
|
jaroslav@601
|
957 |
throw (ObjectStreamException) th;
|
jaroslav@601
|
958 |
} else {
|
jaroslav@601
|
959 |
throwMiscException(th);
|
jaroslav@601
|
960 |
throw new InternalError(); // never reached
|
jaroslav@601
|
961 |
}
|
jaroslav@601
|
962 |
} catch (IllegalAccessException ex) {
|
jaroslav@601
|
963 |
// should not occur, as access checks have been suppressed
|
jaroslav@601
|
964 |
throw new InternalError();
|
jaroslav@601
|
965 |
}
|
jaroslav@601
|
966 |
} else {
|
jaroslav@601
|
967 |
throw new UnsupportedOperationException();
|
jaroslav@601
|
968 |
}
|
jaroslav@601
|
969 |
}
|
jaroslav@601
|
970 |
|
jaroslav@601
|
971 |
/**
|
jaroslav@601
|
972 |
* Class representing the portion of an object's serialized form allotted
|
jaroslav@601
|
973 |
* to data described by a given class descriptor. If "hasData" is false,
|
jaroslav@601
|
974 |
* the object's serialized form does not contain data associated with the
|
jaroslav@601
|
975 |
* class descriptor.
|
jaroslav@601
|
976 |
*/
|
jaroslav@601
|
977 |
static class ClassDataSlot {
|
jaroslav@601
|
978 |
|
jaroslav@601
|
979 |
/** class descriptor "occupying" this slot */
|
jaroslav@601
|
980 |
final ObjectStreamClass desc;
|
jaroslav@601
|
981 |
/** true if serialized form includes data for this slot's descriptor */
|
jaroslav@601
|
982 |
final boolean hasData;
|
jaroslav@601
|
983 |
|
jaroslav@601
|
984 |
ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
|
jaroslav@601
|
985 |
this.desc = desc;
|
jaroslav@601
|
986 |
this.hasData = hasData;
|
jaroslav@601
|
987 |
}
|
jaroslav@601
|
988 |
}
|
jaroslav@601
|
989 |
|
jaroslav@601
|
990 |
/**
|
jaroslav@601
|
991 |
* Returns array of ClassDataSlot instances representing the data layout
|
jaroslav@601
|
992 |
* (including superclass data) for serialized objects described by this
|
jaroslav@601
|
993 |
* class descriptor. ClassDataSlots are ordered by inheritance with those
|
jaroslav@601
|
994 |
* containing "higher" superclasses appearing first. The final
|
jaroslav@601
|
995 |
* ClassDataSlot contains a reference to this descriptor.
|
jaroslav@601
|
996 |
*/
|
jaroslav@601
|
997 |
ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
|
jaroslav@601
|
998 |
// REMIND: synchronize instead of relying on volatile?
|
jaroslav@601
|
999 |
if (dataLayout == null) {
|
jaroslav@601
|
1000 |
dataLayout = getClassDataLayout0();
|
jaroslav@601
|
1001 |
}
|
jaroslav@601
|
1002 |
return dataLayout;
|
jaroslav@601
|
1003 |
}
|
jaroslav@601
|
1004 |
|
jaroslav@601
|
1005 |
private ClassDataSlot[] getClassDataLayout0()
|
jaroslav@601
|
1006 |
throws InvalidClassException
|
jaroslav@601
|
1007 |
{
|
jaroslav@601
|
1008 |
ArrayList<ClassDataSlot> slots = new ArrayList<>();
|
jaroslav@601
|
1009 |
Class<?> start = cl, end = cl;
|
jaroslav@601
|
1010 |
|
jaroslav@601
|
1011 |
// locate closest non-serializable superclass
|
jaroslav@601
|
1012 |
while (end != null && Serializable.class.isAssignableFrom(end)) {
|
jaroslav@601
|
1013 |
end = end.getSuperclass();
|
jaroslav@601
|
1014 |
}
|
jaroslav@601
|
1015 |
|
jaroslav@601
|
1016 |
for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
|
jaroslav@601
|
1017 |
|
jaroslav@601
|
1018 |
// search up inheritance hierarchy for class with matching name
|
jaroslav@601
|
1019 |
String searchName = (d.cl != null) ? d.cl.getName() : d.name;
|
jaroslav@601
|
1020 |
Class<?> match = null;
|
jaroslav@601
|
1021 |
for (Class<?> c = start; c != end; c = c.getSuperclass()) {
|
jaroslav@601
|
1022 |
if (searchName.equals(c.getName())) {
|
jaroslav@601
|
1023 |
match = c;
|
jaroslav@601
|
1024 |
break;
|
jaroslav@601
|
1025 |
}
|
jaroslav@601
|
1026 |
}
|
jaroslav@601
|
1027 |
|
jaroslav@601
|
1028 |
// add "no data" slot for each unmatched class below match
|
jaroslav@601
|
1029 |
if (match != null) {
|
jaroslav@601
|
1030 |
for (Class<?> c = start; c != match; c = c.getSuperclass()) {
|
jaroslav@601
|
1031 |
slots.add(new ClassDataSlot(
|
jaroslav@601
|
1032 |
ObjectStreamClass.lookup(c, true), false));
|
jaroslav@601
|
1033 |
}
|
jaroslav@601
|
1034 |
start = match.getSuperclass();
|
jaroslav@601
|
1035 |
}
|
jaroslav@601
|
1036 |
|
jaroslav@601
|
1037 |
// record descriptor/class pairing
|
jaroslav@601
|
1038 |
slots.add(new ClassDataSlot(d.getVariantFor(match), true));
|
jaroslav@601
|
1039 |
}
|
jaroslav@601
|
1040 |
|
jaroslav@601
|
1041 |
// add "no data" slot for any leftover unmatched classes
|
jaroslav@601
|
1042 |
for (Class<?> c = start; c != end; c = c.getSuperclass()) {
|
jaroslav@601
|
1043 |
slots.add(new ClassDataSlot(
|
jaroslav@601
|
1044 |
ObjectStreamClass.lookup(c, true), false));
|
jaroslav@601
|
1045 |
}
|
jaroslav@601
|
1046 |
|
jaroslav@601
|
1047 |
// order slots from superclass -> subclass
|
jaroslav@601
|
1048 |
Collections.reverse(slots);
|
jaroslav@601
|
1049 |
return slots.toArray(new ClassDataSlot[slots.size()]);
|
jaroslav@601
|
1050 |
}
|
jaroslav@601
|
1051 |
|
jaroslav@601
|
1052 |
/**
|
jaroslav@601
|
1053 |
* Returns aggregate size (in bytes) of marshalled primitive field values
|
jaroslav@601
|
1054 |
* for represented class.
|
jaroslav@601
|
1055 |
*/
|
jaroslav@601
|
1056 |
int getPrimDataSize() {
|
jaroslav@601
|
1057 |
return primDataSize;
|
jaroslav@601
|
1058 |
}
|
jaroslav@601
|
1059 |
|
jaroslav@601
|
1060 |
/**
|
jaroslav@601
|
1061 |
* Returns number of non-primitive serializable fields of represented
|
jaroslav@601
|
1062 |
* class.
|
jaroslav@601
|
1063 |
*/
|
jaroslav@601
|
1064 |
int getNumObjFields() {
|
jaroslav@601
|
1065 |
return numObjFields;
|
jaroslav@601
|
1066 |
}
|
jaroslav@601
|
1067 |
|
jaroslav@601
|
1068 |
/**
|
jaroslav@601
|
1069 |
* Fetches the serializable primitive field values of object obj and
|
jaroslav@601
|
1070 |
* marshals them into byte array buf starting at offset 0. It is the
|
jaroslav@601
|
1071 |
* responsibility of the caller to ensure that obj is of the proper type if
|
jaroslav@601
|
1072 |
* non-null.
|
jaroslav@601
|
1073 |
*/
|
jaroslav@601
|
1074 |
void getPrimFieldValues(Object obj, byte[] buf) {
|
jaroslav@601
|
1075 |
}
|
jaroslav@601
|
1076 |
|
jaroslav@601
|
1077 |
/**
|
jaroslav@601
|
1078 |
* Sets the serializable primitive fields of object obj using values
|
jaroslav@601
|
1079 |
* unmarshalled from byte array buf starting at offset 0. It is the
|
jaroslav@601
|
1080 |
* responsibility of the caller to ensure that obj is of the proper type if
|
jaroslav@601
|
1081 |
* non-null.
|
jaroslav@601
|
1082 |
*/
|
jaroslav@601
|
1083 |
void setPrimFieldValues(Object obj, byte[] buf) {
|
jaroslav@601
|
1084 |
}
|
jaroslav@601
|
1085 |
|
jaroslav@601
|
1086 |
/**
|
jaroslav@601
|
1087 |
* Fetches the serializable object field values of object obj and stores
|
jaroslav@601
|
1088 |
* them in array vals starting at offset 0. It is the responsibility of
|
jaroslav@601
|
1089 |
* the caller to ensure that obj is of the proper type if non-null.
|
jaroslav@601
|
1090 |
*/
|
jaroslav@601
|
1091 |
void getObjFieldValues(Object obj, Object[] vals) {
|
jaroslav@601
|
1092 |
}
|
jaroslav@601
|
1093 |
|
jaroslav@601
|
1094 |
/**
|
jaroslav@601
|
1095 |
* Sets the serializable object fields of object obj using values from
|
jaroslav@601
|
1096 |
* array vals starting at offset 0. It is the responsibility of the caller
|
jaroslav@601
|
1097 |
* to ensure that obj is of the proper type if non-null.
|
jaroslav@601
|
1098 |
*/
|
jaroslav@601
|
1099 |
void setObjFieldValues(Object obj, Object[] vals) {
|
jaroslav@601
|
1100 |
}
|
jaroslav@601
|
1101 |
|
jaroslav@601
|
1102 |
/**
|
jaroslav@601
|
1103 |
* Calculates and sets serializable field offsets, as well as primitive
|
jaroslav@601
|
1104 |
* data size and object field count totals. Throws InvalidClassException
|
jaroslav@601
|
1105 |
* if fields are illegally ordered.
|
jaroslav@601
|
1106 |
*/
|
jaroslav@601
|
1107 |
private void computeFieldOffsets() throws InvalidClassException {
|
jaroslav@601
|
1108 |
primDataSize = 0;
|
jaroslav@601
|
1109 |
numObjFields = 0;
|
jaroslav@601
|
1110 |
int firstObjIndex = -1;
|
jaroslav@601
|
1111 |
|
jaroslav@601
|
1112 |
for (int i = 0; i < fields.length; i++) {
|
jaroslav@601
|
1113 |
ObjectStreamField f = fields[i];
|
jaroslav@601
|
1114 |
switch (f.getTypeCode()) {
|
jaroslav@601
|
1115 |
case 'Z':
|
jaroslav@601
|
1116 |
case 'B':
|
jaroslav@601
|
1117 |
f.setOffset(primDataSize++);
|
jaroslav@601
|
1118 |
break;
|
jaroslav@601
|
1119 |
|
jaroslav@601
|
1120 |
case 'C':
|
jaroslav@601
|
1121 |
case 'S':
|
jaroslav@601
|
1122 |
f.setOffset(primDataSize);
|
jaroslav@601
|
1123 |
primDataSize += 2;
|
jaroslav@601
|
1124 |
break;
|
jaroslav@601
|
1125 |
|
jaroslav@601
|
1126 |
case 'I':
|
jaroslav@601
|
1127 |
case 'F':
|
jaroslav@601
|
1128 |
f.setOffset(primDataSize);
|
jaroslav@601
|
1129 |
primDataSize += 4;
|
jaroslav@601
|
1130 |
break;
|
jaroslav@601
|
1131 |
|
jaroslav@601
|
1132 |
case 'J':
|
jaroslav@601
|
1133 |
case 'D':
|
jaroslav@601
|
1134 |
f.setOffset(primDataSize);
|
jaroslav@601
|
1135 |
primDataSize += 8;
|
jaroslav@601
|
1136 |
break;
|
jaroslav@601
|
1137 |
|
jaroslav@601
|
1138 |
case '[':
|
jaroslav@601
|
1139 |
case 'L':
|
jaroslav@601
|
1140 |
f.setOffset(numObjFields++);
|
jaroslav@601
|
1141 |
if (firstObjIndex == -1) {
|
jaroslav@601
|
1142 |
firstObjIndex = i;
|
jaroslav@601
|
1143 |
}
|
jaroslav@601
|
1144 |
break;
|
jaroslav@601
|
1145 |
|
jaroslav@601
|
1146 |
default:
|
jaroslav@601
|
1147 |
throw new InternalError();
|
jaroslav@601
|
1148 |
}
|
jaroslav@601
|
1149 |
}
|
jaroslav@601
|
1150 |
if (firstObjIndex != -1 &&
|
jaroslav@601
|
1151 |
firstObjIndex + numObjFields != fields.length)
|
jaroslav@601
|
1152 |
{
|
jaroslav@601
|
1153 |
throw new InvalidClassException(name, "illegal field order");
|
jaroslav@601
|
1154 |
}
|
jaroslav@601
|
1155 |
}
|
jaroslav@601
|
1156 |
|
jaroslav@601
|
1157 |
/**
|
jaroslav@601
|
1158 |
* If given class is the same as the class associated with this class
|
jaroslav@601
|
1159 |
* descriptor, returns reference to this class descriptor. Otherwise,
|
jaroslav@601
|
1160 |
* returns variant of this class descriptor bound to given class.
|
jaroslav@601
|
1161 |
*/
|
jaroslav@601
|
1162 |
private ObjectStreamClass getVariantFor(Class<?> cl)
|
jaroslav@601
|
1163 |
throws InvalidClassException
|
jaroslav@601
|
1164 |
{
|
jaroslav@601
|
1165 |
if (this.cl == cl) {
|
jaroslav@601
|
1166 |
return this;
|
jaroslav@601
|
1167 |
}
|
jaroslav@601
|
1168 |
ObjectStreamClass desc = new ObjectStreamClass();
|
jaroslav@601
|
1169 |
if (isProxy) {
|
jaroslav@601
|
1170 |
desc.initProxy(cl, null, superDesc);
|
jaroslav@601
|
1171 |
} else {
|
jaroslav@601
|
1172 |
desc.initNonProxy(this, cl, null, superDesc);
|
jaroslav@601
|
1173 |
}
|
jaroslav@601
|
1174 |
return desc;
|
jaroslav@601
|
1175 |
}
|
jaroslav@601
|
1176 |
|
jaroslav@601
|
1177 |
/**
|
jaroslav@601
|
1178 |
* Returns public no-arg constructor of given class, or null if none found.
|
jaroslav@601
|
1179 |
* Access checks are disabled on the returned constructor (if any), since
|
jaroslav@601
|
1180 |
* the defining class may still be non-public.
|
jaroslav@601
|
1181 |
*/
|
jaroslav@601
|
1182 |
private static Constructor getExternalizableConstructor(Class<?> cl) {
|
jaroslav@604
|
1183 |
throw new SecurityException();
|
jaroslav@601
|
1184 |
}
|
jaroslav@601
|
1185 |
|
jaroslav@601
|
1186 |
/**
|
jaroslav@601
|
1187 |
* Returns subclass-accessible no-arg constructor of first non-serializable
|
jaroslav@601
|
1188 |
* superclass, or null if none found. Access checks are disabled on the
|
jaroslav@601
|
1189 |
* returned constructor (if any).
|
jaroslav@601
|
1190 |
*/
|
jaroslav@601
|
1191 |
private static Constructor getSerializableConstructor(Class<?> cl) {
|
jaroslav@601
|
1192 |
Class<?> initCl = cl;
|
jaroslav@601
|
1193 |
while (Serializable.class.isAssignableFrom(initCl)) {
|
jaroslav@601
|
1194 |
if ((initCl = initCl.getSuperclass()) == null) {
|
jaroslav@601
|
1195 |
return null;
|
jaroslav@601
|
1196 |
}
|
jaroslav@601
|
1197 |
}
|
jaroslav@604
|
1198 |
throw new SecurityException();
|
jaroslav@601
|
1199 |
}
|
jaroslav@601
|
1200 |
|
jaroslav@601
|
1201 |
/**
|
jaroslav@601
|
1202 |
* Returns non-static, non-abstract method with given signature provided it
|
jaroslav@601
|
1203 |
* is defined by or accessible (via inheritance) by the given class, or
|
jaroslav@601
|
1204 |
* null if no match found. Access checks are disabled on the returned
|
jaroslav@601
|
1205 |
* method (if any).
|
jaroslav@601
|
1206 |
*/
|
jaroslav@601
|
1207 |
private static Method getInheritableMethod(Class<?> cl, String name,
|
jaroslav@601
|
1208 |
Class<?>[] argTypes,
|
jaroslav@601
|
1209 |
Class<?> returnType)
|
jaroslav@601
|
1210 |
{
|
jaroslav@604
|
1211 |
throw new SecurityException();
|
jaroslav@601
|
1212 |
}
|
jaroslav@601
|
1213 |
|
jaroslav@601
|
1214 |
/**
|
jaroslav@601
|
1215 |
* Returns non-static private method with given signature defined by given
|
jaroslav@601
|
1216 |
* class, or null if none found. Access checks are disabled on the
|
jaroslav@601
|
1217 |
* returned method (if any).
|
jaroslav@601
|
1218 |
*/
|
jaroslav@601
|
1219 |
private static Method getPrivateMethod(Class<?> cl, String name,
|
jaroslav@601
|
1220 |
Class<?>[] argTypes,
|
jaroslav@601
|
1221 |
Class<?> returnType)
|
jaroslav@601
|
1222 |
{
|
jaroslav@604
|
1223 |
throw new SecurityException();
|
jaroslav@601
|
1224 |
}
|
jaroslav@601
|
1225 |
|
jaroslav@601
|
1226 |
/**
|
jaroslav@601
|
1227 |
* Returns true if classes are defined in the same runtime package, false
|
jaroslav@601
|
1228 |
* otherwise.
|
jaroslav@601
|
1229 |
*/
|
jaroslav@601
|
1230 |
private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
|
jaroslav@601
|
1231 |
return (cl1.getClassLoader() == cl2.getClassLoader() &&
|
jaroslav@601
|
1232 |
getPackageName(cl1).equals(getPackageName(cl2)));
|
jaroslav@601
|
1233 |
}
|
jaroslav@601
|
1234 |
|
jaroslav@601
|
1235 |
/**
|
jaroslav@601
|
1236 |
* Returns package name of given class.
|
jaroslav@601
|
1237 |
*/
|
jaroslav@601
|
1238 |
private static String getPackageName(Class<?> cl) {
|
jaroslav@601
|
1239 |
String s = cl.getName();
|
jaroslav@601
|
1240 |
int i = s.lastIndexOf('[');
|
jaroslav@601
|
1241 |
if (i >= 0) {
|
jaroslav@601
|
1242 |
s = s.substring(i + 2);
|
jaroslav@601
|
1243 |
}
|
jaroslav@601
|
1244 |
i = s.lastIndexOf('.');
|
jaroslav@601
|
1245 |
return (i >= 0) ? s.substring(0, i) : "";
|
jaroslav@601
|
1246 |
}
|
jaroslav@601
|
1247 |
|
jaroslav@601
|
1248 |
/**
|
jaroslav@601
|
1249 |
* Compares class names for equality, ignoring package names. Returns true
|
jaroslav@601
|
1250 |
* if class names equal, false otherwise.
|
jaroslav@601
|
1251 |
*/
|
jaroslav@601
|
1252 |
private static boolean classNamesEqual(String name1, String name2) {
|
jaroslav@601
|
1253 |
name1 = name1.substring(name1.lastIndexOf('.') + 1);
|
jaroslav@601
|
1254 |
name2 = name2.substring(name2.lastIndexOf('.') + 1);
|
jaroslav@601
|
1255 |
return name1.equals(name2);
|
jaroslav@601
|
1256 |
}
|
jaroslav@601
|
1257 |
|
jaroslav@601
|
1258 |
/**
|
jaroslav@601
|
1259 |
* Returns JVM type signature for given class.
|
jaroslav@601
|
1260 |
*/
|
jaroslav@601
|
1261 |
private static String getClassSignature(Class<?> cl) {
|
jaroslav@601
|
1262 |
StringBuilder sbuf = new StringBuilder();
|
jaroslav@601
|
1263 |
while (cl.isArray()) {
|
jaroslav@601
|
1264 |
sbuf.append('[');
|
jaroslav@601
|
1265 |
cl = cl.getComponentType();
|
jaroslav@601
|
1266 |
}
|
jaroslav@601
|
1267 |
if (cl.isPrimitive()) {
|
jaroslav@601
|
1268 |
if (cl == Integer.TYPE) {
|
jaroslav@601
|
1269 |
sbuf.append('I');
|
jaroslav@601
|
1270 |
} else if (cl == Byte.TYPE) {
|
jaroslav@601
|
1271 |
sbuf.append('B');
|
jaroslav@601
|
1272 |
} else if (cl == Long.TYPE) {
|
jaroslav@601
|
1273 |
sbuf.append('J');
|
jaroslav@601
|
1274 |
} else if (cl == Float.TYPE) {
|
jaroslav@601
|
1275 |
sbuf.append('F');
|
jaroslav@601
|
1276 |
} else if (cl == Double.TYPE) {
|
jaroslav@601
|
1277 |
sbuf.append('D');
|
jaroslav@601
|
1278 |
} else if (cl == Short.TYPE) {
|
jaroslav@601
|
1279 |
sbuf.append('S');
|
jaroslav@601
|
1280 |
} else if (cl == Character.TYPE) {
|
jaroslav@601
|
1281 |
sbuf.append('C');
|
jaroslav@601
|
1282 |
} else if (cl == Boolean.TYPE) {
|
jaroslav@601
|
1283 |
sbuf.append('Z');
|
jaroslav@601
|
1284 |
} else if (cl == Void.TYPE) {
|
jaroslav@601
|
1285 |
sbuf.append('V');
|
jaroslav@601
|
1286 |
} else {
|
jaroslav@601
|
1287 |
throw new InternalError();
|
jaroslav@601
|
1288 |
}
|
jaroslav@601
|
1289 |
} else {
|
jaroslav@601
|
1290 |
sbuf.append('L' + cl.getName().replace('.', '/') + ';');
|
jaroslav@601
|
1291 |
}
|
jaroslav@601
|
1292 |
return sbuf.toString();
|
jaroslav@601
|
1293 |
}
|
jaroslav@601
|
1294 |
|
jaroslav@601
|
1295 |
/**
|
jaroslav@601
|
1296 |
* Returns JVM type signature for given list of parameters and return type.
|
jaroslav@601
|
1297 |
*/
|
jaroslav@601
|
1298 |
private static String getMethodSignature(Class<?>[] paramTypes,
|
jaroslav@601
|
1299 |
Class<?> retType)
|
jaroslav@601
|
1300 |
{
|
jaroslav@601
|
1301 |
StringBuilder sbuf = new StringBuilder();
|
jaroslav@601
|
1302 |
sbuf.append('(');
|
jaroslav@601
|
1303 |
for (int i = 0; i < paramTypes.length; i++) {
|
jaroslav@601
|
1304 |
sbuf.append(getClassSignature(paramTypes[i]));
|
jaroslav@601
|
1305 |
}
|
jaroslav@601
|
1306 |
sbuf.append(')');
|
jaroslav@601
|
1307 |
sbuf.append(getClassSignature(retType));
|
jaroslav@601
|
1308 |
return sbuf.toString();
|
jaroslav@601
|
1309 |
}
|
jaroslav@601
|
1310 |
|
jaroslav@601
|
1311 |
/**
|
jaroslav@601
|
1312 |
* Convenience method for throwing an exception that is either a
|
jaroslav@601
|
1313 |
* RuntimeException, Error, or of some unexpected type (in which case it is
|
jaroslav@601
|
1314 |
* wrapped inside an IOException).
|
jaroslav@601
|
1315 |
*/
|
jaroslav@601
|
1316 |
private static void throwMiscException(Throwable th) throws IOException {
|
jaroslav@601
|
1317 |
if (th instanceof RuntimeException) {
|
jaroslav@601
|
1318 |
throw (RuntimeException) th;
|
jaroslav@601
|
1319 |
} else if (th instanceof Error) {
|
jaroslav@601
|
1320 |
throw (Error) th;
|
jaroslav@601
|
1321 |
} else {
|
jaroslav@601
|
1322 |
IOException ex = new IOException("unexpected exception type");
|
jaroslav@601
|
1323 |
ex.initCause(th);
|
jaroslav@601
|
1324 |
throw ex;
|
jaroslav@601
|
1325 |
}
|
jaroslav@601
|
1326 |
}
|
jaroslav@601
|
1327 |
|
jaroslav@601
|
1328 |
/**
|
jaroslav@601
|
1329 |
* Returns ObjectStreamField array describing the serializable fields of
|
jaroslav@601
|
1330 |
* the given class. Serializable fields backed by an actual field of the
|
jaroslav@601
|
1331 |
* class are represented by ObjectStreamFields with corresponding non-null
|
jaroslav@601
|
1332 |
* Field objects. Throws InvalidClassException if the (explicitly
|
jaroslav@601
|
1333 |
* declared) serializable fields are invalid.
|
jaroslav@601
|
1334 |
*/
|
jaroslav@601
|
1335 |
private static ObjectStreamField[] getSerialFields(Class<?> cl)
|
jaroslav@601
|
1336 |
throws InvalidClassException
|
jaroslav@601
|
1337 |
{
|
jaroslav@601
|
1338 |
ObjectStreamField[] fields;
|
jaroslav@601
|
1339 |
if (Serializable.class.isAssignableFrom(cl) &&
|
jaroslav@601
|
1340 |
!Externalizable.class.isAssignableFrom(cl) &&
|
jaroslav@601
|
1341 |
!Proxy.isProxyClass(cl) &&
|
jaroslav@601
|
1342 |
!cl.isInterface())
|
jaroslav@601
|
1343 |
{
|
jaroslav@601
|
1344 |
if ((fields = getDeclaredSerialFields(cl)) == null) {
|
jaroslav@601
|
1345 |
fields = getDefaultSerialFields(cl);
|
jaroslav@601
|
1346 |
}
|
jaroslav@601
|
1347 |
Arrays.sort(fields);
|
jaroslav@601
|
1348 |
} else {
|
jaroslav@601
|
1349 |
fields = NO_FIELDS;
|
jaroslav@601
|
1350 |
}
|
jaroslav@601
|
1351 |
return fields;
|
jaroslav@601
|
1352 |
}
|
jaroslav@601
|
1353 |
|
jaroslav@601
|
1354 |
/**
|
jaroslav@601
|
1355 |
* Returns serializable fields of given class as defined explicitly by a
|
jaroslav@601
|
1356 |
* "serialPersistentFields" field, or null if no appropriate
|
jaroslav@601
|
1357 |
* "serialPersistentFields" field is defined. Serializable fields backed
|
jaroslav@601
|
1358 |
* by an actual field of the class are represented by ObjectStreamFields
|
jaroslav@601
|
1359 |
* with corresponding non-null Field objects. For compatibility with past
|
jaroslav@601
|
1360 |
* releases, a "serialPersistentFields" field with a null value is
|
jaroslav@601
|
1361 |
* considered equivalent to not declaring "serialPersistentFields". Throws
|
jaroslav@601
|
1362 |
* InvalidClassException if the declared serializable fields are
|
jaroslav@601
|
1363 |
* invalid--e.g., if multiple fields share the same name.
|
jaroslav@601
|
1364 |
*/
|
jaroslav@601
|
1365 |
private static ObjectStreamField[] getDeclaredSerialFields(Class<?> cl)
|
jaroslav@601
|
1366 |
throws InvalidClassException
|
jaroslav@601
|
1367 |
{
|
jaroslav@604
|
1368 |
throw new SecurityException();
|
jaroslav@601
|
1369 |
}
|
jaroslav@601
|
1370 |
|
jaroslav@601
|
1371 |
/**
|
jaroslav@601
|
1372 |
* Returns array of ObjectStreamFields corresponding to all non-static
|
jaroslav@601
|
1373 |
* non-transient fields declared by given class. Each ObjectStreamField
|
jaroslav@601
|
1374 |
* contains a Field object for the field it represents. If no default
|
jaroslav@601
|
1375 |
* serializable fields exist, NO_FIELDS is returned.
|
jaroslav@601
|
1376 |
*/
|
jaroslav@601
|
1377 |
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
|
jaroslav@604
|
1378 |
throw new SecurityException();
|
jaroslav@601
|
1379 |
}
|
jaroslav@601
|
1380 |
|
jaroslav@601
|
1381 |
/**
|
jaroslav@601
|
1382 |
* Returns explicit serial version UID value declared by given class, or
|
jaroslav@601
|
1383 |
* null if none.
|
jaroslav@601
|
1384 |
*/
|
jaroslav@601
|
1385 |
private static Long getDeclaredSUID(Class<?> cl) {
|
jaroslav@601
|
1386 |
return null;
|
jaroslav@601
|
1387 |
}
|
jaroslav@601
|
1388 |
|
jaroslav@601
|
1389 |
/**
|
jaroslav@601
|
1390 |
* Computes the default serial version UID value for the given class.
|
jaroslav@601
|
1391 |
*/
|
jaroslav@601
|
1392 |
private static long computeDefaultSUID(Class<?> cl) {
|
jaroslav@604
|
1393 |
throw new SecurityException();
|
jaroslav@601
|
1394 |
}
|
jaroslav@601
|
1395 |
|
jaroslav@601
|
1396 |
}
|