RewriteException.java revision 953:221a84ef44c0
1193326Sed/*
2193326Sed * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3193326Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4193326Sed *
5193326Sed * This code is free software; you can redistribute it and/or modify it
6193326Sed * under the terms of the GNU General Public License version 2 only, as
7193326Sed * published by the Free Software Foundation.  Oracle designates this
8193326Sed * particular file as subject to the "Classpath" exception as provided
9193326Sed * by Oracle in the LICENSE file that accompanied this code.
10193326Sed *
11193326Sed * This code is distributed in the hope that it will be useful, but WITHOUT
12193326Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13193326Sed * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14193326Sed * version 2 for more details (a copy is included in the LICENSE file that
15193326Sed * accompanied this code).
16193326Sed *
17193326Sed * You should have received a copy of the GNU General Public License version
18193326Sed * 2 along with this work; if not, write to the Free Software Foundation,
19193326Sed * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20193326Sed *
21193326Sed * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22193326Sed * or visit www.oracle.com if you need additional information or have any
23193326Sed * questions.
24193326Sed */
25193326Sedpackage jdk.nashorn.internal.runtime;
26193326Sed
27193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
28193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
29193326Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
30193326Sed
31198893Srdivackyimport java.io.NotSerializableException;
32198893Srdivackyimport java.io.ObjectInputStream;
33251662Sdimimport java.io.ObjectOutputStream;
34251662Sdimimport java.lang.invoke.CallSite;
35251662Sdimimport java.lang.invoke.ConstantCallSite;
36251662Sdimimport java.lang.invoke.MethodHandle;
37251662Sdimimport java.lang.invoke.MethodHandles;
38251662Sdimimport java.lang.invoke.MethodHandles.Lookup;
39251662Sdimimport java.lang.invoke.MethodType;
40251662Sdimimport java.lang.reflect.Array;
41251662Sdimimport java.util.Arrays;
42251662Sdimimport jdk.nashorn.internal.codegen.CompilerConstants;
43251662Sdimimport jdk.nashorn.internal.codegen.CompilerConstants.Call;
44251662Sdimimport jdk.nashorn.internal.codegen.types.Type;
45251662Sdimimport jdk.nashorn.internal.lookup.MethodHandleFactory;
46251662Sdimimport jdk.nashorn.internal.lookup.MethodHandleFunctionality;
47251662Sdimimport jdk.nashorn.internal.objects.Global;
48251662Sdim
49251662Sdim/**
50251662Sdim * Used to signal to the linker to relink the callee
51251662Sdim */
52251662Sdim@SuppressWarnings("serial")
53251662Sdimpublic final class RewriteException extends Exception {
54251662Sdim    private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
55251662Sdim
56251662Sdim    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
57251662Sdim    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
58251662Sdim    private ScriptObject runtimeScope;
59251662Sdim
60251662Sdim    // Contents of bytecode slots
61251662Sdim    private Object[] byteCodeSlots;
62251662Sdim
63251662Sdim    private final int[] previousContinuationEntryPoints;
64193326Sed
65251662Sdim    /** Call for getting the contents of the bytecode slots in the exception */
66251662Sdim    public static final Call GET_BYTECODE_SLOTS       = virtualCallNoLookup(RewriteException.class, "getByteCodeSlots", Object[].class);
67251662Sdim    /** Call for getting the program point in the exception */
68251662Sdim    public static final Call GET_PROGRAM_POINT        = virtualCallNoLookup(RewriteException.class, "getProgramPoint", int.class);
69251662Sdim    /** Call for getting the return value for the exception */
70251662Sdim    public static final Call GET_RETURN_VALUE         = virtualCallNoLookup(RewriteException.class, "getReturnValueDestructive", Object.class);
71251662Sdim    /** Call for the populate array bootstrap */
72251662Sdim    public static final Call BOOTSTRAP                = staticCallNoLookup(RewriteException.class, "populateArrayBootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class);
73251662Sdim
74251662Sdim    /** Call for populating an array with local variable state */
75193326Sed    private static final Call POPULATE_ARRAY           = staticCall(MethodHandles.lookup(), RewriteException.class, "populateArray", Object[].class, Object[].class, int.class, Object[].class);
76193326Sed
77193326Sed    /** Call for converting an array to a long array. */
78193326Sed    public static final Call TO_LONG_ARRAY   = staticCallNoLookup(RewriteException.class, "toLongArray",   long[].class, Object.class, RewriteException.class);
79193326Sed    /** Call for converting an array to a double array. */
80199482Srdivacky    public static final Call TO_DOUBLE_ARRAY = staticCallNoLookup(RewriteException.class, "toDoubleArray", double[].class, Object.class, RewriteException.class);
81199482Srdivacky    /** Call for converting an array to an object array. */
82199482Srdivacky    public static final Call TO_OBJECT_ARRAY = staticCallNoLookup(RewriteException.class, "toObjectArray", Object[].class, Object.class, RewriteException.class);
83199482Srdivacky    /** Call for converting an object to null if it can't be represented as an instance of a class. */
84199482Srdivacky    public static final Call INSTANCE_OR_NULL = staticCallNoLookup(RewriteException.class, "instanceOrNull", Object.class, Object.class, Class.class);
85199482Srdivacky    /** Call for asserting the length of an array. */
86199482Srdivacky    public static final Call ASSERT_ARRAY_LENGTH = staticCallNoLookup(RewriteException.class, "assertArrayLength", void.class, Object[].class, int.class);
87199482Srdivacky
88199482Srdivacky    private RewriteException(
89199482Srdivacky            final UnwarrantedOptimismException e,
90221345Sdim            final Object[] byteCodeSlots,
91199482Srdivacky            final String[] byteCodeSymbolNames,
92199482Srdivacky            final int[] previousContinuationEntryPoints) {
93199482Srdivacky        super("", e, false, Context.DEBUG);
94199482Srdivacky        this.byteCodeSlots = byteCodeSlots;
95199482Srdivacky        this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames);
96199482Srdivacky        this.previousContinuationEntryPoints = previousContinuationEntryPoints;
97199482Srdivacky    }
98199482Srdivacky
99199482Srdivacky    /**
100199482Srdivacky     * Constructor for a rewrite exception thrown from an optimistic function.
101199482Srdivacky     * @param e the {@link UnwarrantedOptimismException} that triggered this exception.
102221345Sdim     * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
103199482Srdivacky     * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might
104193326Sed     * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an
105193326Sed     * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation,
106198954Srdivacky     * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and
107199482Srdivacky     * the more unnecessary deoptimizing compilations can be avoided.
108199482Srdivacky     * @return a new rewrite exception
109199482Srdivacky     */
110198954Srdivacky    public static RewriteException create(final UnwarrantedOptimismException e,
111199482Srdivacky            final Object[] byteCodeSlots,
112199482Srdivacky            final String[] byteCodeSymbolNames) {
113199482Srdivacky        return create(e, byteCodeSlots, byteCodeSymbolNames, null);
114199482Srdivacky    }
115199482Srdivacky
116199482Srdivacky    /**
117199482Srdivacky     * Constructor for a rewrite exception thrown from a rest-of method.
118199482Srdivacky     * @param e the {@link UnwarrantedOptimismException} that triggered this exception.
119199482Srdivacky     * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
120193326Sed     * @param byteCodeSymbolNames the names of the variables in the {@code byteCodeSlots} parameter. The array might
121199482Srdivacky     * have less elements, and some elements might be unnamed (the name can be null). The information is provided in an
122199482Srdivacky     * effort to assist evaluation of expressions for their types by the compiler doing the deoptimizing recompilation,
123199482Srdivacky     * and can thus be incomplete - the more complete it is, the more expressions can be evaluated by the compiler, and
124199482Srdivacky     * the more unnecessary deoptimizing compilations can be avoided.
125199482Srdivacky     * @param previousContinuationEntryPoints an array of continuation entry points that were already executed during
126199482Srdivacky     * one logical invocation of the function (a rest-of triggering a rest-of triggering a...)
127199482Srdivacky     * @return a new rewrite exception
128199482Srdivacky     */
129199482Srdivacky    public static RewriteException create(final UnwarrantedOptimismException e,
130199482Srdivacky            final Object[] byteCodeSlots,
131199482Srdivacky            final String[] byteCodeSymbolNames,
132199482Srdivacky            final int[] previousContinuationEntryPoints) {
133199482Srdivacky        return new RewriteException(e, byteCodeSlots, byteCodeSymbolNames, previousContinuationEntryPoints);
134199482Srdivacky    }
135199482Srdivacky
136199482Srdivacky    /**
137199482Srdivacky     * Bootstrap method for populate array
138199482Srdivacky     * @param lookup     lookup
139199482Srdivacky     * @param name       name (ignored)
140199482Srdivacky     * @param type       method type for signature
141199482Srdivacky     * @param startIndex start index to start writing to
142199482Srdivacky     * @return callsite to array populator (constant)
143199482Srdivacky     */
144199482Srdivacky    public static CallSite populateArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int startIndex) {
145199482Srdivacky        MethodHandle mh = POPULATE_ARRAY.methodHandle();
146199482Srdivacky        mh = MH.insertArguments(mh, 1, startIndex);
147199482Srdivacky        mh = MH.asCollector(mh, Object[].class, type.parameterCount() - 1);
148199482Srdivacky        mh = MH.asType(mh, type);
149199482Srdivacky        return new ConstantCallSite(mh);
150199482Srdivacky    }
151199482Srdivacky
152199482Srdivacky    private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames) {
153199482Srdivacky        final ScriptObject locals = Global.newEmptyInstance();
154199482Srdivacky        final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length);
155199482Srdivacky        ScriptObject runtimeScope = null;
156199482Srdivacky        final String scopeName = CompilerConstants.SCOPE.symbolName();
157199482Srdivacky        for(int i = 0; i < l; ++i) {
158199482Srdivacky            final String name = byteCodeSymbolNames[i];
159199482Srdivacky            final Object value = byteCodeSlots[i];
160199482Srdivacky            if(scopeName.equals(name)) {
161199482Srdivacky                assert runtimeScope == null;
162199482Srdivacky                runtimeScope = (ScriptObject)value;
163199482Srdivacky            } else if(name != null) {
164199482Srdivacky                locals.set(name, value, true);
165199482Srdivacky            }
166199482Srdivacky        }
167199482Srdivacky        locals.setProto(runtimeScope);
168199482Srdivacky        return locals;
169199482Srdivacky    }
170199482Srdivacky
171199482Srdivacky    /**
172199482Srdivacky     * Array populator used for saving the local variable state into the array contained in the
173199482Srdivacky     * RewriteException
174199482Srdivacky     * @param arrayToBePopluated array to be populated
175199482Srdivacky     * @param startIndex start index to write to
176199482Srdivacky     * @param items items with which to populate the array
177199482Srdivacky     * @return the populated array - same array object
178199482Srdivacky     */
179199482Srdivacky    public static Object[] populateArray(final Object[] arrayToBePopluated, final int startIndex, final Object[] items) {
180199482Srdivacky        System.arraycopy(items, 0, arrayToBePopluated, startIndex, items.length);
181199482Srdivacky        return arrayToBePopluated;
182199482Srdivacky    }
183199482Srdivacky
184193326Sed    /**
185199482Srdivacky     * Continuation handler calls this method when a local variable carried over into the continuation is expected to be
186193326Sed     * a long array in the continued method. Normally, it will also be a long array in the original (interrupted by
187199482Srdivacky     * deoptimization) method, but it can actually be an int array that underwent widening in the new code version.
188199482Srdivacky     * @param obj the object that has to be converted into a long array
189199482Srdivacky     * @param e the exception being processed
190199482Srdivacky     * @return a long array
191199482Srdivacky     */
192199482Srdivacky    public static long[] toLongArray(final Object obj, final RewriteException e) {
193199482Srdivacky        if(obj instanceof long[]) {
194198954Srdivacky            return (long[])obj;
195199482Srdivacky        }
196199482Srdivacky
197199482Srdivacky        assert obj instanceof int[];
198199482Srdivacky
199199482Srdivacky        final int[] in = (int[])obj;
200199482Srdivacky        final long[] out = new long[in.length];
201199482Srdivacky        for(int i = 0; i < in.length; ++i) {
202199482Srdivacky            out[i] = in[i];
203199482Srdivacky        }
204199482Srdivacky        return e.replaceByteCodeValue(in, out);
205199482Srdivacky    }
206199482Srdivacky
207199482Srdivacky    /**
208199482Srdivacky     * Continuation handler calls this method when a local variable carried over into the continuation is expected to be
209199482Srdivacky     * a double array in the continued method. Normally, it will also be a double array in the original (interrupted by
210199482Srdivacky     * deoptimization) method, but it can actually be an int or long array that underwent widening in the new code version.
211199482Srdivacky     * @param obj the object that has to be converted into a double array
212199482Srdivacky     * @param e the exception being processed
213199482Srdivacky     * @return a double array
214199482Srdivacky     */
215199482Srdivacky    public static double[] toDoubleArray(final Object obj, final RewriteException e) {
216199482Srdivacky        if(obj instanceof double[]) {
217199482Srdivacky            return (double[])obj;
218199482Srdivacky        }
219198954Srdivacky
220199482Srdivacky        assert obj instanceof int[] || obj instanceof long[];
221199482Srdivacky
222199482Srdivacky        final int l = Array.getLength(obj);
223199482Srdivacky        final double[] out = new double[l];
224199482Srdivacky        for(int i = 0; i < l; ++i) {
225193326Sed            out[i] = Array.getDouble(obj, i);
226199482Srdivacky        }
227199482Srdivacky        return e.replaceByteCodeValue(obj, out);
228199482Srdivacky    }
229199482Srdivacky
230199482Srdivacky    /**
231199482Srdivacky     * Continuation handler calls this method when a local variable carried over into the continuation is expected to be
232193326Sed     * an Object array in the continued method. Normally, it will also be an Object array in the original (interrupted by
233199482Srdivacky     * deoptimization) method, but it can actually be an int, long, or double array that underwent widening in the new
234199482Srdivacky     * code version.
235199482Srdivacky     * @param obj the object that has to be converted into an Object array
236198954Srdivacky     * @param e the exception being processed
237199482Srdivacky     * @return an Object array
238198954Srdivacky     */
239199482Srdivacky    public static Object[] toObjectArray(final Object obj, final RewriteException e) {
240199482Srdivacky        if(obj instanceof Object[]) {
241199482Srdivacky            return (Object[])obj;
242198954Srdivacky        }
243199482Srdivacky
244199482Srdivacky        assert obj instanceof int[] || obj instanceof long[] || obj instanceof double[] : obj + " is " + obj.getClass().getName();
245199482Srdivacky
246199482Srdivacky        final int l = Array.getLength(obj);
247199482Srdivacky        final Object[] out = new Object[l];
248199482Srdivacky        for(int i = 0; i < l; ++i) {
249199482Srdivacky            out[i] = Array.get(obj, i);
250198954Srdivacky        }
251198954Srdivacky        return e.replaceByteCodeValue(obj, out);
252198954Srdivacky    }
253198954Srdivacky
254198954Srdivacky    /**
255193326Sed     * Continuation handler calls this method when a local variable carried over into the continuation is expected to
256193326Sed     * have a certain type, but the value can have a different type coming from the deoptimized method as it was a dead
257199512Srdivacky     * store. If we had precise liveness analysis, we wouldn't need this.
258199512Srdivacky     * @param obj the object inspected for being of a particular type
259199990Srdivacky     * @param clazz the type the object must belong to
260199990Srdivacky     * @return the object if it belongs to the type, or null otherwise
261199990Srdivacky     */
262207619Srdivacky    public static Object instanceOrNull(final Object obj, final Class<?> clazz) {
263193326Sed        return clazz.isInstance(obj) ? obj : null;
264199990Srdivacky    }
265193326Sed
266207619Srdivacky    /**
267193326Sed     * Asserts the length of an array. Invoked from continuation handler only when running with assertions enabled.
268207619Srdivacky     * The array can, in fact, have more elements than asserted, but they must all have Undefined as their value. The
269207619Srdivacky     * method does not test for the array having less elements than asserted, as those would already have caused an
270207619Srdivacky     * {@code ArrayIndexOutOfBoundsException} to be thrown as the continuation handler attempts to access the missing
271199990Srdivacky     * elements.
272207619Srdivacky     * @param arr the array
273207619Srdivacky     * @param length the asserted length
274193326Sed     */
275193326Sed    public static void assertArrayLength(final Object[] arr, final int length) {
276193326Sed        for(int i = arr.length; i-- > length;) {
277210299Sed            if(arr[i] != ScriptRuntime.UNDEFINED) {
278210299Sed                throw new AssertionError(String.format("Expected array length %d, but it is %d", length, i + 1));
279193326Sed            }
280198954Srdivacky        }
281198954Srdivacky    }
282199482Srdivacky
283199482Srdivacky    private <T> T replaceByteCodeValue(final Object in, final T out) {
284199482Srdivacky        for(int i = 0; i < byteCodeSlots.length; ++i) {
285199482Srdivacky            if(byteCodeSlots[i] == in) {
286199482Srdivacky                byteCodeSlots[i] = out;
287199482Srdivacky            }
288199482Srdivacky        }
289199482Srdivacky        return out;
290199482Srdivacky    }
291199482Srdivacky
292199482Srdivacky    private UnwarrantedOptimismException getUOE() {
293198954Srdivacky        return (UnwarrantedOptimismException)getCause();
294198954Srdivacky    }
295198954Srdivacky    /**
296198954Srdivacky     * Get return value. This method is destructive, after it is invoked subsequent invocation of either
297199482Srdivacky     * {@link #getByteCodeSlots()} or this method will return null. This method is invoked from the generated
298199482Srdivacky     * continuation code as the last step before continuing the execution, and we need to make sure we don't hang on to
299199482Srdivacky     * either the entry bytecode slot values or the return value and prevent them from being garbage collected.
300199482Srdivacky     * @return return value
301199482Srdivacky     */
302198954Srdivacky    public Object getReturnValueDestructive() {
303199482Srdivacky        assert byteCodeSlots != null;
304199482Srdivacky        byteCodeSlots = null;
305199482Srdivacky        runtimeScope = null;
306199482Srdivacky        return getUOE().getReturnValueDestructive();
307199482Srdivacky    }
308199482Srdivacky
309199482Srdivacky    Object getReturnValueNonDestructive() {
310199482Srdivacky        return getUOE().getReturnValueNonDestructive();
311199482Srdivacky    }
312199482Srdivacky
313199482Srdivacky    /**
314199482Srdivacky     * Get return type
315198954Srdivacky     * @return return type
316199482Srdivacky     */
317199482Srdivacky    public Type getReturnType() {
318199482Srdivacky        return getUOE().getReturnType();
319199482Srdivacky    }
320199482Srdivacky
321199482Srdivacky    /**
322199482Srdivacky     * Get the program point.
323199482Srdivacky     * @return program point.
324199482Srdivacky     */
325198954Srdivacky    public int getProgramPoint() {
326199482Srdivacky        return getUOE().getProgramPoint();
327199482Srdivacky    }
328199482Srdivacky
329199482Srdivacky    /**
330199482Srdivacky     * Get the bytecode slot contents.
331199482Srdivacky     * @return bytecode slot contents.
332199482Srdivacky     */
333199482Srdivacky    public Object[] getByteCodeSlots() {
334199482Srdivacky        return byteCodeSlots == null ? null : byteCodeSlots.clone();
335199482Srdivacky    }
336199482Srdivacky
337199482Srdivacky    /**
338199482Srdivacky     * @return an array of continuation entry points that were already executed during one logical invocation of the
339199482Srdivacky     * function (a rest-of triggering a rest-of triggering a...)
340199482Srdivacky     */
341199482Srdivacky    public int[] getPreviousContinuationEntryPoints() {
342199482Srdivacky        return previousContinuationEntryPoints == null ? null : previousContinuationEntryPoints.clone();
343199482Srdivacky    }
344199482Srdivacky
345199482Srdivacky    /**
346199482Srdivacky     * Returns the runtime scope that was in effect when the exception was thrown.
347199482Srdivacky     * @return the runtime scope.
348199482Srdivacky     */
349199482Srdivacky    public ScriptObject getRuntimeScope() {
350199482Srdivacky        return runtimeScope;
351199482Srdivacky    }
352199482Srdivacky
353199482Srdivacky    private static String stringify(final Object returnValue) {
354199482Srdivacky        if (returnValue == null) {
355199482Srdivacky            return "null";
356199482Srdivacky        }
357199482Srdivacky        String str = returnValue.toString();
358199482Srdivacky        if (returnValue instanceof String) {
359199482Srdivacky            str = '\'' + str + '\'';
360199482Srdivacky        } else if (returnValue instanceof Double) {
361199482Srdivacky            str = str + 'd';
362199482Srdivacky        } else if (returnValue instanceof Long) {
363199482Srdivacky            str = str + 'l';
364199482Srdivacky        }
365199482Srdivacky        return str;
366199482Srdivacky    }
367199482Srdivacky
368199482Srdivacky    @Override
369199482Srdivacky    public String getMessage() {
370199482Srdivacky        return getMessage(false);
371199482Srdivacky    }
372199482Srdivacky
373199482Srdivacky    /**
374199482Srdivacky     * Short toString function for message
375199482Srdivacky     * @return short message
376199482Srdivacky     */
377199482Srdivacky    public String getMessageShort() {
378199482Srdivacky        return getMessage(true);
379199482Srdivacky    }
380199482Srdivacky
381199482Srdivacky    private String getMessage(final boolean isShort) {
382199482Srdivacky        final StringBuilder sb = new StringBuilder();
383199482Srdivacky
384199482Srdivacky        //program point
385199482Srdivacky        sb.append("[pp=").
386199482Srdivacky            append(getProgramPoint()).
387199482Srdivacky            append(", ");
388199482Srdivacky
389199482Srdivacky        //slot contents
390199482Srdivacky        if (!isShort) {
391199482Srdivacky            final Object[] slots = byteCodeSlots;
392199482Srdivacky            if (slots != null) {
393199482Srdivacky                sb.append("slots=").
394199482Srdivacky                    append(Arrays.asList(slots)).
395199482Srdivacky                    append(", ");
396199482Srdivacky            }
397199482Srdivacky        }
398199482Srdivacky
399199482Srdivacky        //return type
400199482Srdivacky        sb.append("type=").
401199482Srdivacky            append(getReturnType()).
402199482Srdivacky            append(", ");
403199482Srdivacky
404199482Srdivacky        //return value
405199482Srdivacky        sb.append("value=").
406199482Srdivacky            append(stringify(getReturnValueNonDestructive())).
407199482Srdivacky            append(")]");
408199482Srdivacky
409199482Srdivacky        return sb.toString();
410199482Srdivacky    }
411199482Srdivacky
412199482Srdivacky    private void writeObject(final ObjectOutputStream out) throws NotSerializableException {
413199482Srdivacky        throw new NotSerializableException(getClass().getName());
414199482Srdivacky    }
415199482Srdivacky
416199482Srdivacky    private void readObject(final ObjectInputStream in) throws NotSerializableException {
417199482Srdivacky        throw new NotSerializableException(getClass().getName());
418199482Srdivacky    }
419199482Srdivacky}
420199482Srdivacky