History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/misc/ProxyGenerator.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/misc/ProxyGenerator.java+2009−0
@@ -0,0 +1,2009 @@
1+/*
2+ * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.misc;
27+
28+import java.io.ByteArrayOutputStream;
29+import java.io.DataOutputStream;
30+import java.io.FileOutputStream;
31+import java.io.IOException;
32+import java.io.OutputStream;
33+import java.lang.reflect.Array;
34+import java.lang.reflect.Method;
35+import java.util.ArrayList;
36+import java.util.HashMap;
37+import java.util.LinkedList;
38+import java.util.List;
39+import java.util.ListIterator;
40+import java.util.Map;
41+import sun.security.action.GetBooleanAction;
42+
43+/**
44+ * ProxyGenerator contains the code to generate a dynamic proxy class
45+ * for the java.lang.reflect.Proxy API.
46+ *
47+ * The external interfaces to ProxyGenerator is the static
48+ * "generateProxyClass" method.
49+ *
50+ * @author Peter Jones
51+ * @since 1.3
52+ */
53+public class ProxyGenerator {
54+ /*
55+ * In the comments below, "JVMS" refers to The Java Virtual Machine
56+ * Specification Second Edition and "JLS" refers to the original
57+ * version of The Java Language Specification, unless otherwise
58+ * specified.
59+ */
60+
61+ /* generate 1.5-era class file version */
62+ private static final int CLASSFILE_MAJOR_VERSION = 49;
63+ private static final int CLASSFILE_MINOR_VERSION = 0;
64+
65+ /*
66+ * beginning of constants copied from
67+ * sun.tools.java.RuntimeConstants (which no longer exists):
68+ */
69+
70+ /* constant pool tags */
71+ private static final int CONSTANT_UTF8 = 1;
72+ private static final int CONSTANT_UNICODE = 2;
73+ private static final int CONSTANT_INTEGER = 3;
74+ private static final int CONSTANT_FLOAT = 4;
75+ private static final int CONSTANT_LONG = 5;
76+ private static final int CONSTANT_DOUBLE = 6;
77+ private static final int CONSTANT_CLASS = 7;
78+ private static final int CONSTANT_STRING = 8;
79+ private static final int CONSTANT_FIELD = 9;
80+ private static final int CONSTANT_METHOD = 10;
81+ private static final int CONSTANT_INTERFACEMETHOD = 11;
82+ private static final int CONSTANT_NAMEANDTYPE = 12;
83+
84+ /* access and modifier flags */
85+ private static final int ACC_PUBLIC = 0x00000001;
86+ private static final int ACC_PRIVATE = 0x00000002;
87+// private static final int ACC_PROTECTED = 0x00000004;
88+ private static final int ACC_STATIC = 0x00000008;
89+ private static final int ACC_FINAL = 0x00000010;
90+// private static final int ACC_SYNCHRONIZED = 0x00000020;
91+// private static final int ACC_VOLATILE = 0x00000040;
92+// private static final int ACC_TRANSIENT = 0x00000080;
93+// private static final int ACC_NATIVE = 0x00000100;
94+// private static final int ACC_INTERFACE = 0x00000200;
95+// private static final int ACC_ABSTRACT = 0x00000400;
96+ private static final int ACC_SUPER = 0x00000020;
97+// private static final int ACC_STRICT = 0x00000800;
98+
99+ /* opcodes */
100+// private static final int opc_nop = 0;
101+ private static final int opc_aconst_null = 1;
102+// private static final int opc_iconst_m1 = 2;
103+ private static final int opc_iconst_0 = 3;
104+// private static final int opc_iconst_1 = 4;
105+// private static final int opc_iconst_2 = 5;
106+// private static final int opc_iconst_3 = 6;
107+// private static final int opc_iconst_4 = 7;
108+// private static final int opc_iconst_5 = 8;
109+// private static final int opc_lconst_0 = 9;
110+// private static final int opc_lconst_1 = 10;
111+// private static final int opc_fconst_0 = 11;
112+// private static final int opc_fconst_1 = 12;
113+// private static final int opc_fconst_2 = 13;
114+// private static final int opc_dconst_0 = 14;
115+// private static final int opc_dconst_1 = 15;
116+ private static final int opc_bipush = 16;
117+ private static final int opc_sipush = 17;
118+ private static final int opc_ldc = 18;
119+ private static final int opc_ldc_w = 19;
120+// private static final int opc_ldc2_w = 20;
121+ private static final int opc_iload = 21;
122+ private static final int opc_lload = 22;
123+ private static final int opc_fload = 23;
124+ private static final int opc_dload = 24;
125+ private static final int opc_aload = 25;
126+ private static final int opc_iload_0 = 26;
127+// private static final int opc_iload_1 = 27;
128+// private static final int opc_iload_2 = 28;
129+// private static final int opc_iload_3 = 29;
130+ private static final int opc_lload_0 = 30;
131+// private static final int opc_lload_1 = 31;
132+// private static final int opc_lload_2 = 32;
133+// private static final int opc_lload_3 = 33;
134+ private static final int opc_fload_0 = 34;
135+// private static final int opc_fload_1 = 35;
136+// private static final int opc_fload_2 = 36;
137+// private static final int opc_fload_3 = 37;
138+ private static final int opc_dload_0 = 38;
139+// private static final int opc_dload_1 = 39;
140+// private static final int opc_dload_2 = 40;
141+// private static final int opc_dload_3 = 41;
142+ private static final int opc_aload_0 = 42;
143+// private static final int opc_aload_1 = 43;
144+// private static final int opc_aload_2 = 44;
145+// private static final int opc_aload_3 = 45;
146+// private static final int opc_iaload = 46;
147+// private static final int opc_laload = 47;
148+// private static final int opc_faload = 48;
149+// private static final int opc_daload = 49;
150+// private static final int opc_aaload = 50;
151+// private static final int opc_baload = 51;
152+// private static final int opc_caload = 52;
153+// private static final int opc_saload = 53;
154+// private static final int opc_istore = 54;
155+// private static final int opc_lstore = 55;
156+// private static final int opc_fstore = 56;
157+// private static final int opc_dstore = 57;
158+ private static final int opc_astore = 58;
159+// private static final int opc_istore_0 = 59;
160+// private static final int opc_istore_1 = 60;
161+// private static final int opc_istore_2 = 61;
162+// private static final int opc_istore_3 = 62;
163+// private static final int opc_lstore_0 = 63;
164+// private static final int opc_lstore_1 = 64;
165+// private static final int opc_lstore_2 = 65;
166+// private static final int opc_lstore_3 = 66;
167+// private static final int opc_fstore_0 = 67;
168+// private static final int opc_fstore_1 = 68;
169+// private static final int opc_fstore_2 = 69;
170+// private static final int opc_fstore_3 = 70;
171+// private static final int opc_dstore_0 = 71;
172+// private static final int opc_dstore_1 = 72;
173+// private static final int opc_dstore_2 = 73;
174+// private static final int opc_dstore_3 = 74;
175+ private static final int opc_astore_0 = 75;
176+// private static final int opc_astore_1 = 76;
177+// private static final int opc_astore_2 = 77;
178+// private static final int opc_astore_3 = 78;
179+// private static final int opc_iastore = 79;
180+// private static final int opc_lastore = 80;
181+// private static final int opc_fastore = 81;
182+// private static final int opc_dastore = 82;
183+ private static final int opc_aastore = 83;
184+// private static final int opc_bastore = 84;
185+// private static final int opc_castore = 85;
186+// private static final int opc_sastore = 86;
187+ private static final int opc_pop = 87;
188+// private static final int opc_pop2 = 88;
189+ private static final int opc_dup = 89;
190+// private static final int opc_dup_x1 = 90;
191+// private static final int opc_dup_x2 = 91;
192+// private static final int opc_dup2 = 92;
193+// private static final int opc_dup2_x1 = 93;
194+// private static final int opc_dup2_x2 = 94;
195+// private static final int opc_swap = 95;
196+// private static final int opc_iadd = 96;
197+// private static final int opc_ladd = 97;
198+// private static final int opc_fadd = 98;
199+// private static final int opc_dadd = 99;
200+// private static final int opc_isub = 100;
201+// private static final int opc_lsub = 101;
202+// private static final int opc_fsub = 102;
203+// private static final int opc_dsub = 103;
204+// private static final int opc_imul = 104;
205+// private static final int opc_lmul = 105;
206+// private static final int opc_fmul = 106;
207+// private static final int opc_dmul = 107;
208+// private static final int opc_idiv = 108;
209+// private static final int opc_ldiv = 109;
210+// private static final int opc_fdiv = 110;
211+// private static final int opc_ddiv = 111;
212+// private static final int opc_irem = 112;
213+// private static final int opc_lrem = 113;
214+// private static final int opc_frem = 114;
215+// private static final int opc_drem = 115;
216+// private static final int opc_ineg = 116;
217+// private static final int opc_lneg = 117;
218+// private static final int opc_fneg = 118;
219+// private static final int opc_dneg = 119;
220+// private static final int opc_ishl = 120;
221+// private static final int opc_lshl = 121;
222+// private static final int opc_ishr = 122;
223+// private static final int opc_lshr = 123;
224+// private static final int opc_iushr = 124;
225+// private static final int opc_lushr = 125;
226+// private static final int opc_iand = 126;
227+// private static final int opc_land = 127;
228+// private static final int opc_ior = 128;
229+// private static final int opc_lor = 129;
230+// private static final int opc_ixor = 130;
231+// private static final int opc_lxor = 131;
232+// private static final int opc_iinc = 132;
233+// private static final int opc_i2l = 133;
234+// private static final int opc_i2f = 134;
235+// private static final int opc_i2d = 135;
236+// private static final int opc_l2i = 136;
237+// private static final int opc_l2f = 137;
238+// private static final int opc_l2d = 138;
239+// private static final int opc_f2i = 139;
240+// private static final int opc_f2l = 140;
241+// private static final int opc_f2d = 141;
242+// private static final int opc_d2i = 142;
243+// private static final int opc_d2l = 143;
244+// private static final int opc_d2f = 144;
245+// private static final int opc_i2b = 145;
246+// private static final int opc_i2c = 146;
247+// private static final int opc_i2s = 147;
248+// private static final int opc_lcmp = 148;
249+// private static final int opc_fcmpl = 149;
250+// private static final int opc_fcmpg = 150;
251+// private static final int opc_dcmpl = 151;
252+// private static final int opc_dcmpg = 152;
253+// private static final int opc_ifeq = 153;
254+// private static final int opc_ifne = 154;
255+// private static final int opc_iflt = 155;
256+// private static final int opc_ifge = 156;
257+// private static final int opc_ifgt = 157;
258+// private static final int opc_ifle = 158;
259+// private static final int opc_if_icmpeq = 159;
260+// private static final int opc_if_icmpne = 160;
261+// private static final int opc_if_icmplt = 161;
262+// private static final int opc_if_icmpge = 162;
263+// private static final int opc_if_icmpgt = 163;
264+// private static final int opc_if_icmple = 164;
265+// private static final int opc_if_acmpeq = 165;
266+// private static final int opc_if_acmpne = 166;
267+// private static final int opc_goto = 167;
268+// private static final int opc_jsr = 168;
269+// private static final int opc_ret = 169;
270+// private static final int opc_tableswitch = 170;
271+// private static final int opc_lookupswitch = 171;
272+ private static final int opc_ireturn = 172;
273+ private static final int opc_lreturn = 173;
274+ private static final int opc_freturn = 174;
275+ private static final int opc_dreturn = 175;
276+ private static final int opc_areturn = 176;
277+ private static final int opc_return = 177;
278+ private static final int opc_getstatic = 178;
279+ private static final int opc_putstatic = 179;
280+ private static final int opc_getfield = 180;
281+// private static final int opc_putfield = 181;
282+ private static final int opc_invokevirtual = 182;
283+ private static final int opc_invokespecial = 183;
284+ private static final int opc_invokestatic = 184;
285+ private static final int opc_invokeinterface = 185;
286+ private static final int opc_new = 187;
287+// private static final int opc_newarray = 188;
288+ private static final int opc_anewarray = 189;
289+// private static final int opc_arraylength = 190;
290+ private static final int opc_athrow = 191;
291+ private static final int opc_checkcast = 192;
292+// private static final int opc_instanceof = 193;
293+// private static final int opc_monitorenter = 194;
294+// private static final int opc_monitorexit = 195;
295+ private static final int opc_wide = 196;
296+// private static final int opc_multianewarray = 197;
297+// private static final int opc_ifnull = 198;
298+// private static final int opc_ifnonnull = 199;
299+// private static final int opc_goto_w = 200;
300+// private static final int opc_jsr_w = 201;
301+
302+ // end of constants copied from sun.tools.java.RuntimeConstants
303+
304+ /** name of the superclass of proxy classes */
305+ private final static String superclassName = "java/lang/reflect/Proxy";
306+
307+ /** name of field for storing a proxy instance's invocation handler */
308+ private final static String handlerFieldName = "h";
309+
310+ /** debugging flag for saving generated class files */
311+ private final static boolean saveGeneratedFiles =
312+ java.security.AccessController.doPrivileged(
313+ new GetBooleanAction(
314+ "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();
315+
316+ /**
317+ * Generate a proxy class given a name and a list of proxy interfaces.
318+ */
319+ public static byte[] generateProxyClass(final String name,
320+ Class[] interfaces)
321+ {
322+ ProxyGenerator gen = new ProxyGenerator(name, interfaces);
323+ final byte[] classFile = gen.generateClassFile();
324+
325+ if (saveGeneratedFiles) {
326+ java.security.AccessController.doPrivileged(
327+ new java.security.PrivilegedAction<Void>() {
328+ public Void run() {
329+ try {
330+ FileOutputStream file =
331+ new FileOutputStream(dotToSlash(name) + ".class");
332+ file.write(classFile);
333+ file.close();
334+ return null;
335+ } catch (IOException e) {
336+ throw new InternalError(
337+ "I/O exception saving generated file: " + e);
338+ }
339+ }
340+ });
341+ }
342+
343+ return classFile;
344+ }
345+
346+ /* preloaded Method objects for methods in java.lang.Object */
347+ private static Method hashCodeMethod;
348+ private static Method equalsMethod;
349+ private static Method toStringMethod;
350+ static {
351+ try {
352+ hashCodeMethod = Object.class.getMethod("hashCode");
353+ equalsMethod =
354+ Object.class.getMethod("equals", new Class[] { Object.class });
355+ toStringMethod = Object.class.getMethod("toString");
356+ } catch (NoSuchMethodException e) {
357+ throw new NoSuchMethodError(e.getMessage());
358+ }
359+ }
360+
361+ /** name of proxy class */
362+ private String className;
363+
364+ /** proxy interfaces */
365+ private Class[] interfaces;
366+
367+ /** constant pool of class being generated */
368+ private ConstantPool cp = new ConstantPool();
369+
370+ /** FieldInfo struct for each field of generated class */
371+ private List<FieldInfo> fields = new ArrayList<FieldInfo>();
372+
373+ /** MethodInfo struct for each method of generated class */
374+ private List<MethodInfo> methods = new ArrayList<MethodInfo>();
375+
376+ /**
377+ * maps method signature string to list of ProxyMethod objects for
378+ * proxy methods with that signature
379+ */
380+ private Map<String, List<ProxyMethod>> proxyMethods =
381+ new HashMap<String,List<ProxyMethod>>();
382+
383+ /** count of ProxyMethod objects added to proxyMethods */
384+ private int proxyMethodCount = 0;
385+
386+ /**
387+ * Construct a ProxyGenerator to generate a proxy class with the
388+ * specified name and for the given interfaces.
389+ *
390+ * A ProxyGenerator object contains the state for the ongoing
391+ * generation of a particular proxy class.
392+ */
393+ private ProxyGenerator(String className, Class[] interfaces) {
394+ this.className = className;
395+ this.interfaces = interfaces;
396+ }
397+
398+ /**
399+ * Generate a class file for the proxy class. This method drives the
400+ * class file generation process.
401+ */
402+ private byte[] generateClassFile() {
403+
404+ /* ============================================================
405+ * Step 1: Assemble ProxyMethod objects for all methods to
406+ * generate proxy dispatching code for.
407+ */
408+
409+ /*
410+ * Record that proxy methods are needed for the hashCode, equals,
411+ * and toString methods of java.lang.Object. This is done before
412+ * the methods from the proxy interfaces so that the methods from
413+ * java.lang.Object take precedence over duplicate methods in the
414+ * proxy interfaces.
415+ */
416+ addProxyMethod(hashCodeMethod, Object.class);
417+ addProxyMethod(equalsMethod, Object.class);
418+ addProxyMethod(toStringMethod, Object.class);
419+
420+ /*
421+ * Now record all of the methods from the proxy interfaces, giving
422+ * earlier interfaces precedence over later ones with duplicate
423+ * methods.
424+ */
425+ for (int i = 0; i < interfaces.length; i++) {
426+ Method[] methods = interfaces[i].getMethods();
427+ for (int j = 0; j < methods.length; j++) {
428+ addProxyMethod(methods[j], interfaces[i]);
429+ }
430+ }
431+
432+ /*
433+ * For each set of proxy methods with the same signature,
434+ * verify that the methods' return types are compatible.
435+ */
436+ for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
437+ checkReturnTypes(sigmethods);
438+ }
439+
440+ /* ============================================================
441+ * Step 2: Assemble FieldInfo and MethodInfo structs for all of
442+ * fields and methods in the class we are generating.
443+ */
444+ try {
445+ methods.add(generateConstructor());
446+
447+ for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
448+ for (ProxyMethod pm : sigmethods) {
449+
450+ // add static field for method's Method object
451+ fields.add(new FieldInfo(pm.methodFieldName,
452+ "Ljava/lang/reflect/Method;",
453+ ACC_PRIVATE | ACC_STATIC));
454+
455+ // generate code for proxy method and add it
456+ methods.add(pm.generateMethod());
457+ }
458+ }
459+
460+ methods.add(generateStaticInitializer());
461+
462+ } catch (IOException e) {
463+ throw new InternalError("unexpected I/O Exception");
464+ }
465+
466+ if (methods.size() > 65535) {
467+ throw new IllegalArgumentException("method limit exceeded");
468+ }
469+ if (fields.size() > 65535) {
470+ throw new IllegalArgumentException("field limit exceeded");
471+ }
472+
473+ /* ============================================================
474+ * Step 3: Write the final class file.
475+ */
476+
477+ /*
478+ * Make sure that constant pool indexes are reserved for the
479+ * following items before starting to write the final class file.
480+ */
481+ cp.getClass(dotToSlash(className));
482+ cp.getClass(superclassName);
483+ for (int i = 0; i < interfaces.length; i++) {
484+ cp.getClass(dotToSlash(interfaces[i].getName()));
485+ }
486+
487+ /*
488+ * Disallow new constant pool additions beyond this point, since
489+ * we are about to write the final constant pool table.
490+ */
491+ cp.setReadOnly();
492+
493+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
494+ DataOutputStream dout = new DataOutputStream(bout);
495+
496+ try {
497+ /*
498+ * Write all the items of the "ClassFile" structure.
499+ * See JVMS section 4.1.
500+ */
501+ // u4 magic;
502+ dout.writeInt(0xCAFEBABE);
503+ // u2 minor_version;
504+ dout.writeShort(CLASSFILE_MINOR_VERSION);
505+ // u2 major_version;
506+ dout.writeShort(CLASSFILE_MAJOR_VERSION);
507+
508+ cp.write(dout); // (write constant pool)
509+
510+ // u2 access_flags;
511+ dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
512+ // u2 this_class;
513+ dout.writeShort(cp.getClass(dotToSlash(className)));
514+ // u2 super_class;
515+ dout.writeShort(cp.getClass(superclassName));
516+
517+ // u2 interfaces_count;
518+ dout.writeShort(interfaces.length);
519+ // u2 interfaces[interfaces_count];
520+ for (int i = 0; i < interfaces.length; i++) {
521+ dout.writeShort(cp.getClass(
522+ dotToSlash(interfaces[i].getName())));
523+ }
524+
525+ // u2 fields_count;
526+ dout.writeShort(fields.size());
527+ // field_info fields[fields_count];
528+ for (FieldInfo f : fields) {
529+ f.write(dout);
530+ }
531+
532+ // u2 methods_count;
533+ dout.writeShort(methods.size());
534+ // method_info methods[methods_count];
535+ for (MethodInfo m : methods) {
536+ m.write(dout);
537+ }
538+
539+ // u2 attributes_count;
540+ dout.writeShort(0); // (no ClassFile attributes for proxy classes)
541+
542+ } catch (IOException e) {
543+ throw new InternalError("unexpected I/O Exception");
544+ }
545+
546+ return bout.toByteArray();
547+ }
548+
549+ /**
550+ * Add another method to be proxied, either by creating a new
551+ * ProxyMethod object or augmenting an old one for a duplicate
552+ * method.
553+ *
554+ * "fromClass" indicates the proxy interface that the method was
555+ * found through, which may be different from (a subinterface of)
556+ * the method's "declaring class". Note that the first Method
557+ * object passed for a given name and descriptor identifies the
558+ * Method object (and thus the declaring class) that will be
559+ * passed to the invocation handler's "invoke" method for a given
560+ * set of duplicate methods.
561+ */
562+ private void addProxyMethod(Method m, Class fromClass) {
563+ String name = m.getName();
564+ Class[] parameterTypes = m.getParameterTypes();
565+ Class returnType = m.getReturnType();
566+ Class[] exceptionTypes = m.getExceptionTypes();
567+
568+ String sig = name + getParameterDescriptors(parameterTypes);
569+ List<ProxyMethod> sigmethods = proxyMethods.get(sig);
570+ if (sigmethods != null) {
571+ for (ProxyMethod pm : sigmethods) {
572+ if (returnType == pm.returnType) {
573+ /*
574+ * Found a match: reduce exception types to the
575+ * greatest set of exceptions that can thrown
576+ * compatibly with the throws clauses of both
577+ * overridden methods.
578+ */
579+ List<Class<?>> legalExceptions = new ArrayList<Class<?>>();
580+ collectCompatibleTypes(
581+ exceptionTypes, pm.exceptionTypes, legalExceptions);
582+ collectCompatibleTypes(
583+ pm.exceptionTypes, exceptionTypes, legalExceptions);
584+ pm.exceptionTypes = new Class[legalExceptions.size()];
585+ pm.exceptionTypes =
586+ legalExceptions.toArray(pm.exceptionTypes);
587+ return;
588+ }
589+ }
590+ } else {
591+ sigmethods = new ArrayList<ProxyMethod>(3);
592+ proxyMethods.put(sig, sigmethods);
593+ }
594+ sigmethods.add(new ProxyMethod(name, parameterTypes, returnType,
595+ exceptionTypes, fromClass));
596+ }
597+
598+ /**
599+ * For a given set of proxy methods with the same signature, check
600+ * that their return types are compatible according to the Proxy
601+ * specification.
602+ *
603+ * Specifically, if there is more than one such method, then all
604+ * of the return types must be reference types, and there must be
605+ * one return type that is assignable to each of the rest of them.
606+ */
607+ private static void checkReturnTypes(List<ProxyMethod> methods) {
608+ /*
609+ * If there is only one method with a given signature, there
610+ * cannot be a conflict. This is the only case in which a
611+ * primitive (or void) return type is allowed.
612+ */
613+ if (methods.size() < 2) {
614+ return;
615+ }
616+
617+ /*
618+ * List of return types that are not yet known to be
619+ * assignable from ("covered" by) any of the others.
620+ */
621+ LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<Class<?>>();
622+
623+ nextNewReturnType:
624+ for (ProxyMethod pm : methods) {
625+ Class<?> newReturnType = pm.returnType;
626+ if (newReturnType.isPrimitive()) {
627+ throw new IllegalArgumentException(
628+ "methods with same signature " +
629+ getFriendlyMethodSignature(pm.methodName,
630+ pm.parameterTypes) +
631+ " but incompatible return types: " +
632+ newReturnType.getName() + " and others");
633+ }
634+ boolean added = false;
635+
636+ /*
637+ * Compare the new return type to the existing uncovered
638+ * return types.
639+ */
640+ ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
641+ while (liter.hasNext()) {
642+ Class<?> uncoveredReturnType = liter.next();
643+
644+ /*
645+ * If an existing uncovered return type is assignable
646+ * to this new one, then we can forget the new one.
647+ */
648+ if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
649+ assert !added;
650+ continue nextNewReturnType;
651+ }
652+
653+ /*
654+ * If the new return type is assignable to an existing
655+ * uncovered one, then should replace the existing one
656+ * with the new one (or just forget the existing one,
657+ * if the new one has already be put in the list).
658+ */
659+ if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
660+ // (we can assume that each return type is unique)
661+ if (!added) {
662+ liter.set(newReturnType);
663+ added = true;
664+ } else {
665+ liter.remove();
666+ }
667+ }
668+ }
669+
670+ /*
671+ * If we got through the list of existing uncovered return
672+ * types without an assignability relationship, then add
673+ * the new return type to the list of uncovered ones.
674+ */
675+ if (!added) {
676+ uncoveredReturnTypes.add(newReturnType);
677+ }
678+ }
679+
680+ /*
681+ * We shouldn't end up with more than one return type that is
682+ * not assignable from any of the others.
683+ */
684+ if (uncoveredReturnTypes.size() > 1) {
685+ ProxyMethod pm = methods.get(0);
686+ throw new IllegalArgumentException(
687+ "methods with same signature " +
688+ getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) +
689+ " but incompatible return types: " + uncoveredReturnTypes);
690+ }
691+ }
692+
693+ /**
694+ * A FieldInfo object contains information about a particular field
695+ * in the class being generated. The class mirrors the data items of
696+ * the "field_info" structure of the class file format (see JVMS 4.5).
697+ */
698+ private class FieldInfo {
699+ public int accessFlags;
700+ public String name;
701+ public String descriptor;
702+
703+ public FieldInfo(String name, String descriptor, int accessFlags) {
704+ this.name = name;
705+ this.descriptor = descriptor;
706+ this.accessFlags = accessFlags;
707+
708+ /*
709+ * Make sure that constant pool indexes are reserved for the
710+ * following items before starting to write the final class file.
711+ */
712+ cp.getUtf8(name);
713+ cp.getUtf8(descriptor);
714+ }
715+
716+ public void write(DataOutputStream out) throws IOException {
717+ /*
718+ * Write all the items of the "field_info" structure.
719+ * See JVMS section 4.5.
720+ */
721+ // u2 access_flags;
722+ out.writeShort(accessFlags);
723+ // u2 name_index;
724+ out.writeShort(cp.getUtf8(name));
725+ // u2 descriptor_index;
726+ out.writeShort(cp.getUtf8(descriptor));
727+ // u2 attributes_count;
728+ out.writeShort(0); // (no field_info attributes for proxy classes)
729+ }
730+ }
731+
732+ /**
733+ * An ExceptionTableEntry object holds values for the data items of
734+ * an entry in the "exception_table" item of the "Code" attribute of
735+ * "method_info" structures (see JVMS 4.7.3).
736+ */
737+ private static class ExceptionTableEntry {
738+ public short startPc;
739+ public short endPc;
740+ public short handlerPc;
741+ public short catchType;
742+
743+ public ExceptionTableEntry(short startPc, short endPc,
744+ short handlerPc, short catchType)
745+ {
746+ this.startPc = startPc;
747+ this.endPc = endPc;
748+ this.handlerPc = handlerPc;
749+ this.catchType = catchType;
750+ }
751+ };
752+
753+ /**
754+ * A MethodInfo object contains information about a particular method
755+ * in the class being generated. This class mirrors the data items of
756+ * the "method_info" structure of the class file format (see JVMS 4.6).
757+ */
758+ private class MethodInfo {
759+ public int accessFlags;
760+ public String name;
761+ public String descriptor;
762+ public short maxStack;
763+ public short maxLocals;
764+ public ByteArrayOutputStream code = new ByteArrayOutputStream();
765+ public List<ExceptionTableEntry> exceptionTable =
766+ new ArrayList<ExceptionTableEntry>();
767+ public short[] declaredExceptions;
768+
769+ public MethodInfo(String name, String descriptor, int accessFlags) {
770+ this.name = name;
771+ this.descriptor = descriptor;
772+ this.accessFlags = accessFlags;
773+
774+ /*
775+ * Make sure that constant pool indexes are reserved for the
776+ * following items before starting to write the final class file.
777+ */
778+ cp.getUtf8(name);
779+ cp.getUtf8(descriptor);
780+ cp.getUtf8("Code");
781+ cp.getUtf8("Exceptions");
782+ }
783+
784+ public void write(DataOutputStream out) throws IOException {
785+ /*
786+ * Write all the items of the "method_info" structure.
787+ * See JVMS section 4.6.
788+ */
789+ // u2 access_flags;
790+ out.writeShort(accessFlags);
791+ // u2 name_index;
792+ out.writeShort(cp.getUtf8(name));
793+ // u2 descriptor_index;
794+ out.writeShort(cp.getUtf8(descriptor));
795+ // u2 attributes_count;
796+ out.writeShort(2); // (two method_info attributes:)
797+
798+ // Write "Code" attribute. See JVMS section 4.7.3.
799+
800+ // u2 attribute_name_index;
801+ out.writeShort(cp.getUtf8("Code"));
802+ // u4 attribute_length;
803+ out.writeInt(12 + code.size() + 8 * exceptionTable.size());
804+ // u2 max_stack;
805+ out.writeShort(maxStack);
806+ // u2 max_locals;
807+ out.writeShort(maxLocals);
808+ // u2 code_length;
809+ out.writeInt(code.size());
810+ // u1 code[code_length];
811+ code.writeTo(out);
812+ // u2 exception_table_length;
813+ out.writeShort(exceptionTable.size());
814+ for (ExceptionTableEntry e : exceptionTable) {
815+ // u2 start_pc;
816+ out.writeShort(e.startPc);
817+ // u2 end_pc;
818+ out.writeShort(e.endPc);
819+ // u2 handler_pc;
820+ out.writeShort(e.handlerPc);
821+ // u2 catch_type;
822+ out.writeShort(e.catchType);
823+ }
824+ // u2 attributes_count;
825+ out.writeShort(0);
826+
827+ // write "Exceptions" attribute. See JVMS section 4.7.4.
828+
829+ // u2 attribute_name_index;
830+ out.writeShort(cp.getUtf8("Exceptions"));
831+ // u4 attributes_length;
832+ out.writeInt(2 + 2 * declaredExceptions.length);
833+ // u2 number_of_exceptions;
834+ out.writeShort(declaredExceptions.length);
835+ // u2 exception_index_table[number_of_exceptions];
836+ for (int i = 0; i < declaredExceptions.length; i++) {
837+ out.writeShort(declaredExceptions[i]);
838+ }
839+ }
840+
841+ }
842+
843+ /**
844+ * A ProxyMethod object represents a proxy method in the proxy class
845+ * being generated: a method whose implementation will encode and
846+ * dispatch invocations to the proxy instance's invocation handler.
847+ */
848+ private class ProxyMethod {
849+
850+ public String methodName;
851+ public Class[] parameterTypes;
852+ public Class returnType;
853+ public Class[] exceptionTypes;
854+ public Class fromClass;
855+ public String methodFieldName;
856+
857+ private ProxyMethod(String methodName, Class[] parameterTypes,
858+ Class returnType, Class[] exceptionTypes,
859+ Class fromClass)
860+ {
861+ this.methodName = methodName;
862+ this.parameterTypes = parameterTypes;
863+ this.returnType = returnType;
864+ this.exceptionTypes = exceptionTypes;
865+ this.fromClass = fromClass;
866+ this.methodFieldName = "m" + proxyMethodCount++;
867+ }
868+
869+ /**
870+ * Return a MethodInfo object for this method, including generating
871+ * the code and exception table entry.
872+ */
873+ private MethodInfo generateMethod() throws IOException {
874+ String desc = getMethodDescriptor(parameterTypes, returnType);
875+ MethodInfo minfo = new MethodInfo(methodName, desc,
876+ ACC_PUBLIC | ACC_FINAL);
877+
878+ int[] parameterSlot = new int[parameterTypes.length];
879+ int nextSlot = 1;
880+ for (int i = 0; i < parameterSlot.length; i++) {
881+ parameterSlot[i] = nextSlot;
882+ nextSlot += getWordsPerType(parameterTypes[i]);
883+ }
884+ int localSlot0 = nextSlot;
885+ short pc, tryBegin = 0, tryEnd;
886+
887+ DataOutputStream out = new DataOutputStream(minfo.code);
888+
889+ code_aload(0, out);
890+
891+ out.writeByte(opc_getfield);
892+ out.writeShort(cp.getFieldRef(
893+ superclassName,
894+ handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));
895+
896+ code_aload(0, out);
897+
898+ out.writeByte(opc_getstatic);
899+ out.writeShort(cp.getFieldRef(
900+ dotToSlash(className),
901+ methodFieldName, "Ljava/lang/reflect/Method;"));
902+
903+ if (parameterTypes.length > 0) {
904+
905+ code_ipush(parameterTypes.length, out);
906+
907+ out.writeByte(opc_anewarray);
908+ out.writeShort(cp.getClass("java/lang/Object"));
909+
910+ for (int i = 0; i < parameterTypes.length; i++) {
911+
912+ out.writeByte(opc_dup);
913+
914+ code_ipush(i, out);
915+
916+ codeWrapArgument(parameterTypes[i], parameterSlot[i], out);
917+
918+ out.writeByte(opc_aastore);
919+ }
920+ } else {
921+
922+ out.writeByte(opc_aconst_null);
923+ }
924+
925+ out.writeByte(opc_invokeinterface);
926+ out.writeShort(cp.getInterfaceMethodRef(
927+ "java/lang/reflect/InvocationHandler",
928+ "invoke",
929+ "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
930+ "[Ljava/lang/Object;)Ljava/lang/Object;"));
931+ out.writeByte(4);
932+ out.writeByte(0);
933+
934+ if (returnType == void.class) {
935+
936+ out.writeByte(opc_pop);
937+
938+ out.writeByte(opc_return);
939+
940+ } else {
941+
942+ codeUnwrapReturnValue(returnType, out);
943+ }
944+
945+ tryEnd = pc = (short) minfo.code.size();
946+
947+ List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
948+ if (catchList.size() > 0) {
949+
950+ for (Class<?> ex : catchList) {
951+ minfo.exceptionTable.add(new ExceptionTableEntry(
952+ tryBegin, tryEnd, pc,
953+ cp.getClass(dotToSlash(ex.getName()))));
954+ }
955+
956+ out.writeByte(opc_athrow);
957+
958+ pc = (short) minfo.code.size();
959+
960+ minfo.exceptionTable.add(new ExceptionTableEntry(
961+ tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable")));
962+
963+ code_astore(localSlot0, out);
964+
965+ out.writeByte(opc_new);
966+ out.writeShort(cp.getClass(
967+ "java/lang/reflect/UndeclaredThrowableException"));
968+
969+ out.writeByte(opc_dup);
970+
971+ code_aload(localSlot0, out);
972+
973+ out.writeByte(opc_invokespecial);
974+
975+ out.writeShort(cp.getMethodRef(
976+ "java/lang/reflect/UndeclaredThrowableException",
977+ "<init>", "(Ljava/lang/Throwable;)V"));
978+
979+ out.writeByte(opc_athrow);
980+ }
981+
982+ if (minfo.code.size() > 65535) {
983+ throw new IllegalArgumentException("code size limit exceeded");
984+ }
985+
986+ minfo.maxStack = 10;
987+ minfo.maxLocals = (short) (localSlot0 + 1);
988+ minfo.declaredExceptions = new short[exceptionTypes.length];
989+ for (int i = 0; i < exceptionTypes.length; i++) {
990+ minfo.declaredExceptions[i] = cp.getClass(
991+ dotToSlash(exceptionTypes[i].getName()));
992+ }
993+
994+ return minfo;
995+ }
996+
997+ /**
998+ * Generate code for wrapping an argument of the given type
999+ * whose value can be found at the specified local variable
1000+ * index, in order for it to be passed (as an Object) to the
1001+ * invocation handler's "invoke" method. The code is written
1002+ * to the supplied stream.
1003+ */
1004+ private void codeWrapArgument(Class type, int slot,
1005+ DataOutputStream out)
1006+ throws IOException
1007+ {
1008+ if (type.isPrimitive()) {
1009+ PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
1010+
1011+ if (type == int.class ||
1012+ type == boolean.class ||
1013+ type == byte.class ||
1014+ type == char.class ||
1015+ type == short.class)
1016+ {
1017+ code_iload(slot, out);
1018+ } else if (type == long.class) {
1019+ code_lload(slot, out);
1020+ } else if (type == float.class) {
1021+ code_fload(slot, out);
1022+ } else if (type == double.class) {
1023+ code_dload(slot, out);
1024+ } else {
1025+ throw new AssertionError();
1026+ }
1027+
1028+ out.writeByte(opc_invokestatic);
1029+ out.writeShort(cp.getMethodRef(
1030+ prim.wrapperClassName,
1031+ "valueOf", prim.wrapperValueOfDesc));
1032+
1033+ } else {
1034+
1035+ code_aload(slot, out);
1036+ }
1037+ }
1038+
1039+ /**
1040+ * Generate code for unwrapping a return value of the given
1041+ * type from the invocation handler's "invoke" method (as type
1042+ * Object) to its correct type. The code is written to the
1043+ * supplied stream.
1044+ */
1045+ private void codeUnwrapReturnValue(Class type, DataOutputStream out)
1046+ throws IOException
1047+ {
1048+ if (type.isPrimitive()) {
1049+ PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
1050+
1051+ out.writeByte(opc_checkcast);
1052+ out.writeShort(cp.getClass(prim.wrapperClassName));
1053+
1054+ out.writeByte(opc_invokevirtual);
1055+ out.writeShort(cp.getMethodRef(
1056+ prim.wrapperClassName,
1057+ prim.unwrapMethodName, prim.unwrapMethodDesc));
1058+
1059+ if (type == int.class ||
1060+ type == boolean.class ||
1061+ type == byte.class ||
1062+ type == char.class ||
1063+ type == short.class)
1064+ {
1065+ out.writeByte(opc_ireturn);
1066+ } else if (type == long.class) {
1067+ out.writeByte(opc_lreturn);
1068+ } else if (type == float.class) {
1069+ out.writeByte(opc_freturn);
1070+ } else if (type == double.class) {
1071+ out.writeByte(opc_dreturn);
1072+ } else {
1073+ throw new AssertionError();
1074+ }
1075+
1076+ } else {
1077+
1078+ out.writeByte(opc_checkcast);
1079+ out.writeShort(cp.getClass(dotToSlash(type.getName())));
1080+
1081+ out.writeByte(opc_areturn);
1082+ }
1083+ }
1084+
1085+ /**
1086+ * Generate code for initializing the static field that stores
1087+ * the Method object for this proxy method. The code is written
1088+ * to the supplied stream.
1089+ */
1090+ private void codeFieldInitialization(DataOutputStream out)
1091+ throws IOException
1092+ {
1093+ codeClassForName(fromClass, out);
1094+
1095+ code_ldc(cp.getString(methodName), out);
1096+
1097+ code_ipush(parameterTypes.length, out);
1098+
1099+ out.writeByte(opc_anewarray);
1100+ out.writeShort(cp.getClass("java/lang/Class"));
1101+
1102+ for (int i = 0; i < parameterTypes.length; i++) {
1103+
1104+ out.writeByte(opc_dup);
1105+
1106+ code_ipush(i, out);
1107+
1108+ if (parameterTypes[i].isPrimitive()) {
1109+ PrimitiveTypeInfo prim =
1110+ PrimitiveTypeInfo.get(parameterTypes[i]);
1111+
1112+ out.writeByte(opc_getstatic);
1113+ out.writeShort(cp.getFieldRef(
1114+ prim.wrapperClassName, "TYPE", "Ljava/lang/Class;"));
1115+
1116+ } else {
1117+ codeClassForName(parameterTypes[i], out);
1118+ }
1119+
1120+ out.writeByte(opc_aastore);
1121+ }
1122+
1123+ out.writeByte(opc_invokevirtual);
1124+ out.writeShort(cp.getMethodRef(
1125+ "java/lang/Class",
1126+ "getMethod",
1127+ "(Ljava/lang/String;[Ljava/lang/Class;)" +
1128+ "Ljava/lang/reflect/Method;"));
1129+
1130+ out.writeByte(opc_putstatic);
1131+ out.writeShort(cp.getFieldRef(
1132+ dotToSlash(className),
1133+ methodFieldName, "Ljava/lang/reflect/Method;"));
1134+ }
1135+ }
1136+
1137+ /**
1138+ * Generate the constructor method for the proxy class.
1139+ */
1140+ private MethodInfo generateConstructor() throws IOException {
1141+ MethodInfo minfo = new MethodInfo(
1142+ "<init>", "(Ljava/lang/reflect/InvocationHandler;)V",
1143+ ACC_PUBLIC);
1144+
1145+ DataOutputStream out = new DataOutputStream(minfo.code);
1146+
1147+ code_aload(0, out);
1148+
1149+ code_aload(1, out);
1150+
1151+ out.writeByte(opc_invokespecial);
1152+ out.writeShort(cp.getMethodRef(
1153+ superclassName,
1154+ "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
1155+
1156+ out.writeByte(opc_return);
1157+
1158+ minfo.maxStack = 10;
1159+ minfo.maxLocals = 2;
1160+ minfo.declaredExceptions = new short[0];
1161+
1162+ return minfo;
1163+ }
1164+
1165+ /**
1166+ * Generate the static initializer method for the proxy class.
1167+ */
1168+ private MethodInfo generateStaticInitializer() throws IOException {
1169+ MethodInfo minfo = new MethodInfo(
1170+ "<clinit>", "()V", ACC_STATIC);
1171+
1172+ int localSlot0 = 1;
1173+ short pc, tryBegin = 0, tryEnd;
1174+
1175+ DataOutputStream out = new DataOutputStream(minfo.code);
1176+
1177+ for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
1178+ for (ProxyMethod pm : sigmethods) {
1179+ pm.codeFieldInitialization(out);
1180+ }
1181+ }
1182+
1183+ out.writeByte(opc_return);
1184+
1185+ tryEnd = pc = (short) minfo.code.size();
1186+
1187+ minfo.exceptionTable.add(new ExceptionTableEntry(
1188+ tryBegin, tryEnd, pc,
1189+ cp.getClass("java/lang/NoSuchMethodException")));
1190+
1191+ code_astore(localSlot0, out);
1192+
1193+ out.writeByte(opc_new);
1194+ out.writeShort(cp.getClass("java/lang/NoSuchMethodError"));
1195+
1196+ out.writeByte(opc_dup);
1197+
1198+ code_aload(localSlot0, out);
1199+
1200+ out.writeByte(opc_invokevirtual);
1201+ out.writeShort(cp.getMethodRef(
1202+ "java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
1203+
1204+ out.writeByte(opc_invokespecial);
1205+ out.writeShort(cp.getMethodRef(
1206+ "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V"));
1207+
1208+ out.writeByte(opc_athrow);
1209+
1210+ pc = (short) minfo.code.size();
1211+
1212+ minfo.exceptionTable.add(new ExceptionTableEntry(
1213+ tryBegin, tryEnd, pc,
1214+ cp.getClass("java/lang/ClassNotFoundException")));
1215+
1216+ code_astore(localSlot0, out);
1217+
1218+ out.writeByte(opc_new);
1219+ out.writeShort(cp.getClass("java/lang/NoClassDefFoundError"));
1220+
1221+ out.writeByte(opc_dup);
1222+
1223+ code_aload(localSlot0, out);
1224+
1225+ out.writeByte(opc_invokevirtual);
1226+ out.writeShort(cp.getMethodRef(
1227+ "java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
1228+
1229+ out.writeByte(opc_invokespecial);
1230+ out.writeShort(cp.getMethodRef(
1231+ "java/lang/NoClassDefFoundError",
1232+ "<init>", "(Ljava/lang/String;)V"));
1233+
1234+ out.writeByte(opc_athrow);
1235+
1236+ if (minfo.code.size() > 65535) {
1237+ throw new IllegalArgumentException("code size limit exceeded");
1238+ }
1239+
1240+ minfo.maxStack = 10;
1241+ minfo.maxLocals = (short) (localSlot0 + 1);
1242+ minfo.declaredExceptions = new short[0];
1243+
1244+ return minfo;
1245+ }
1246+
1247+
1248+ /*
1249+ * =============== Code Generation Utility Methods ===============
1250+ */
1251+
1252+ /*
1253+ * The following methods generate code for the load or store operation
1254+ * indicated by their name for the given local variable. The code is
1255+ * written to the supplied stream.
1256+ */
1257+
1258+ private void code_iload(int lvar, DataOutputStream out)
1259+ throws IOException
1260+ {
1261+ codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out);
1262+ }
1263+
1264+ private void code_lload(int lvar, DataOutputStream out)
1265+ throws IOException
1266+ {
1267+ codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out);
1268+ }
1269+
1270+ private void code_fload(int lvar, DataOutputStream out)
1271+ throws IOException
1272+ {
1273+ codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out);
1274+ }
1275+
1276+ private void code_dload(int lvar, DataOutputStream out)
1277+ throws IOException
1278+ {
1279+ codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out);
1280+ }
1281+
1282+ private void code_aload(int lvar, DataOutputStream out)
1283+ throws IOException
1284+ {
1285+ codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out);
1286+ }
1287+
1288+// private void code_istore(int lvar, DataOutputStream out)
1289+// throws IOException
1290+// {
1291+// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out);
1292+// }
1293+
1294+// private void code_lstore(int lvar, DataOutputStream out)
1295+// throws IOException
1296+// {
1297+// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out);
1298+// }
1299+
1300+// private void code_fstore(int lvar, DataOutputStream out)
1301+// throws IOException
1302+// {
1303+// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out);
1304+// }
1305+
1306+// private void code_dstore(int lvar, DataOutputStream out)
1307+// throws IOException
1308+// {
1309+// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out);
1310+// }
1311+
1312+ private void code_astore(int lvar, DataOutputStream out)
1313+ throws IOException
1314+ {
1315+ codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out);
1316+ }
1317+
1318+ /**
1319+ * Generate code for a load or store instruction for the given local
1320+ * variable. The code is written to the supplied stream.
1321+ *
1322+ * "opcode" indicates the opcode form of the desired load or store
1323+ * instruction that takes an explicit local variable index, and
1324+ * "opcode_0" indicates the corresponding form of the instruction
1325+ * with the implicit index 0.
1326+ */
1327+ private void codeLocalLoadStore(int lvar, int opcode, int opcode_0,
1328+ DataOutputStream out)
1329+ throws IOException
1330+ {
1331+ assert lvar >= 0 && lvar <= 0xFFFF;
1332+ if (lvar <= 3) {
1333+ out.writeByte(opcode_0 + lvar);
1334+ } else if (lvar <= 0xFF) {
1335+ out.writeByte(opcode);
1336+ out.writeByte(lvar & 0xFF);
1337+ } else {
1338+ /*
1339+ * Use the "wide" instruction modifier for local variable
1340+ * indexes that do not fit into an unsigned byte.
1341+ */
1342+ out.writeByte(opc_wide);
1343+ out.writeByte(opcode);
1344+ out.writeShort(lvar & 0xFFFF);
1345+ }
1346+ }
1347+
1348+ /**
1349+ * Generate code for an "ldc" instruction for the given constant pool
1350+ * index (the "ldc_w" instruction is used if the index does not fit
1351+ * into an unsigned byte). The code is written to the supplied stream.
1352+ */
1353+ private void code_ldc(int index, DataOutputStream out)
1354+ throws IOException
1355+ {
1356+ assert index >= 0 && index <= 0xFFFF;
1357+ if (index <= 0xFF) {
1358+ out.writeByte(opc_ldc);
1359+ out.writeByte(index & 0xFF);
1360+ } else {
1361+ out.writeByte(opc_ldc_w);
1362+ out.writeShort(index & 0xFFFF);
1363+ }
1364+ }
1365+
1366+ /**
1367+ * Generate code to push a constant integer value on to the operand
1368+ * stack, using the "iconst_<i>", "bipush", or "sipush" instructions
1369+ * depending on the size of the value. The code is written to the
1370+ * supplied stream.
1371+ */
1372+ private void code_ipush(int value, DataOutputStream out)
1373+ throws IOException
1374+ {
1375+ if (value >= -1 && value <= 5) {
1376+ out.writeByte(opc_iconst_0 + value);
1377+ } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
1378+ out.writeByte(opc_bipush);
1379+ out.writeByte(value & 0xFF);
1380+ } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
1381+ out.writeByte(opc_sipush);
1382+ out.writeShort(value & 0xFFFF);
1383+ } else {
1384+ throw new AssertionError();
1385+ }
1386+ }
1387+
1388+ /**
1389+ * Generate code to invoke the Class.forName with the name of the given
1390+ * class to get its Class object at runtime. The code is written to
1391+ * the supplied stream. Note that the code generated by this method
1392+ * may caused the checked ClassNotFoundException to be thrown.
1393+ */
1394+ private void codeClassForName(Class cl, DataOutputStream out)
1395+ throws IOException
1396+ {
1397+ code_ldc(cp.getString(cl.getName()), out);
1398+
1399+ out.writeByte(opc_invokestatic);
1400+ out.writeShort(cp.getMethodRef(
1401+ "java/lang/Class",
1402+ "forName", "(Ljava/lang/String;)Ljava/lang/Class;"));
1403+ }
1404+
1405+
1406+ /*
1407+ * ==================== General Utility Methods ====================
1408+ */
1409+
1410+ /**
1411+ * Convert a fully qualified class name that uses '.' as the package
1412+ * separator, the external representation used by the Java language
1413+ * and APIs, to a fully qualified class name that uses '/' as the
1414+ * package separator, the representation used in the class file
1415+ * format (see JVMS section 4.2).
1416+ */
1417+ private static String dotToSlash(String name) {
1418+ return name.replace('.', '/');
1419+ }
1420+
1421+ /**
1422+ * Return the "method descriptor" string for a method with the given
1423+ * parameter types and return type. See JVMS section 4.3.3.
1424+ */
1425+ private static String getMethodDescriptor(Class[] parameterTypes,
1426+ Class returnType)
1427+ {
1428+ return getParameterDescriptors(parameterTypes) +
1429+ ((returnType == void.class) ? "V" : getFieldType(returnType));
1430+ }
1431+
1432+ /**
1433+ * Return the list of "parameter descriptor" strings enclosed in
1434+ * parentheses corresponding to the given parameter types (in other
1435+ * words, a method descriptor without a return descriptor). This
1436+ * string is useful for constructing string keys for methods without
1437+ * regard to their return type.
1438+ */
1439+ private static String getParameterDescriptors(Class[] parameterTypes) {
1440+ StringBuilder desc = new StringBuilder("(");
1441+ for (int i = 0; i < parameterTypes.length; i++) {
1442+ desc.append(getFieldType(parameterTypes[i]));
1443+ }
1444+ desc.append(')');
1445+ return desc.toString();
1446+ }
1447+
1448+ /**
1449+ * Return the "field type" string for the given type, appropriate for
1450+ * a field descriptor, a parameter descriptor, or a return descriptor
1451+ * other than "void". See JVMS section 4.3.2.
1452+ */
1453+ private static String getFieldType(Class type) {
1454+ if (type.isPrimitive()) {
1455+ return PrimitiveTypeInfo.get(type).baseTypeString;
1456+ } else if (type.isArray()) {
1457+ /*
1458+ * According to JLS 20.3.2, the getName() method on Class does
1459+ * return the VM type descriptor format for array classes (only);
1460+ * using that should be quicker than the otherwise obvious code:
1461+ *
1462+ * return "[" + getTypeDescriptor(type.getComponentType());
1463+ */
1464+ return type.getName().replace('.', '/');
1465+ } else {
1466+ return "L" + dotToSlash(type.getName()) + ";";
1467+ }
1468+ }
1469+
1470+ /**
1471+ * Returns a human-readable string representing the signature of a
1472+ * method with the given name and parameter types.
1473+ */
1474+ private static String getFriendlyMethodSignature(String name,
1475+ Class[] parameterTypes)
1476+ {
1477+ StringBuilder sig = new StringBuilder(name);
1478+ sig.append('(');
1479+ for (int i = 0; i < parameterTypes.length; i++) {
1480+ if (i > 0) {
1481+ sig.append(',');
1482+ }
1483+ Class parameterType = parameterTypes[i];
1484+ int dimensions = 0;
1485+ while (parameterType.isArray()) {
1486+ parameterType = parameterType.getComponentType();
1487+ dimensions++;
1488+ }
1489+ sig.append(parameterType.getName());
1490+ while (dimensions-- > 0) {
1491+ sig.append("[]");
1492+ }
1493+ }
1494+ sig.append(')');
1495+ return sig.toString();
1496+ }
1497+
1498+ /**
1499+ * Return the number of abstract "words", or consecutive local variable
1500+ * indexes, required to contain a value of the given type. See JVMS
1501+ * section 3.6.1.
1502+ *
1503+ * Note that the original version of the JVMS contained a definition of
1504+ * this abstract notion of a "word" in section 3.4, but that definition
1505+ * was removed for the second edition.
1506+ */
1507+ private static int getWordsPerType(Class type) {
1508+ if (type == long.class || type == double.class) {
1509+ return 2;
1510+ } else {
1511+ return 1;
1512+ }
1513+ }
1514+
1515+ /**
1516+ * Add to the given list all of the types in the "from" array that
1517+ * are not already contained in the list and are assignable to at
1518+ * least one of the types in the "with" array.
1519+ *
1520+ * This method is useful for computing the greatest common set of
1521+ * declared exceptions from duplicate methods inherited from
1522+ * different interfaces.
1523+ */
1524+ private static void collectCompatibleTypes(Class<?>[] from,
1525+ Class<?>[] with,
1526+ List<Class<?>> list)
1527+ {
1528+ for (int i = 0; i < from.length; i++) {
1529+ if (!list.contains(from[i])) {
1530+ for (int j = 0; j < with.length; j++) {
1531+ if (with[j].isAssignableFrom(from[i])) {
1532+ list.add(from[i]);
1533+ break;
1534+ }
1535+ }
1536+ }
1537+ }
1538+ }
1539+
1540+ /**
1541+ * Given the exceptions declared in the throws clause of a proxy method,
1542+ * compute the exceptions that need to be caught from the invocation
1543+ * handler's invoke method and rethrown intact in the method's
1544+ * implementation before catching other Throwables and wrapping them
1545+ * in UndeclaredThrowableExceptions.
1546+ *
1547+ * The exceptions to be caught are returned in a List object. Each
1548+ * exception in the returned list is guaranteed to not be a subclass of
1549+ * any of the other exceptions in the list, so the catch blocks for
1550+ * these exceptions may be generated in any order relative to each other.
1551+ *
1552+ * Error and RuntimeException are each always contained by the returned
1553+ * list (if none of their superclasses are contained), since those
1554+ * unchecked exceptions should always be rethrown intact, and thus their
1555+ * subclasses will never appear in the returned list.
1556+ *
1557+ * The returned List will be empty if java.lang.Throwable is in the
1558+ * given list of declared exceptions, indicating that no exceptions
1559+ * need to be caught.
1560+ */
1561+ private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) {
1562+ List<Class<?>> uniqueList = new ArrayList<Class<?>>();
1563+ // unique exceptions to catch
1564+
1565+ uniqueList.add(Error.class); // always catch/rethrow these
1566+ uniqueList.add(RuntimeException.class);
1567+
1568+ nextException:
1569+ for (int i = 0; i < exceptions.length; i++) {
1570+ Class<?> ex = exceptions[i];
1571+ if (ex.isAssignableFrom(Throwable.class)) {
1572+ /*
1573+ * If Throwable is declared to be thrown by the proxy method,
1574+ * then no catch blocks are necessary, because the invoke
1575+ * can, at most, throw Throwable anyway.
1576+ */
1577+ uniqueList.clear();
1578+ break;
1579+ } else if (!Throwable.class.isAssignableFrom(ex)) {
1580+ /*
1581+ * Ignore types that cannot be thrown by the invoke method.
1582+ */
1583+ continue;
1584+ }
1585+ /*
1586+ * Compare this exception against the current list of
1587+ * exceptions that need to be caught:
1588+ */
1589+ for (int j = 0; j < uniqueList.size();) {
1590+ Class<?> ex2 = uniqueList.get(j);
1591+ if (ex2.isAssignableFrom(ex)) {
1592+ /*
1593+ * if a superclass of this exception is already on
1594+ * the list to catch, then ignore this one and continue;
1595+ */
1596+ continue nextException;
1597+ } else if (ex.isAssignableFrom(ex2)) {
1598+ /*
1599+ * if a subclass of this exception is on the list
1600+ * to catch, then remove it;
1601+ */
1602+ uniqueList.remove(j);
1603+ } else {
1604+ j++; // else continue comparing.
1605+ }
1606+ }
1607+ // This exception is unique (so far): add it to the list to catch.
1608+ uniqueList.add(ex);
1609+ }
1610+ return uniqueList;
1611+ }
1612+
1613+ /**
1614+ * A PrimitiveTypeInfo object contains assorted information about
1615+ * a primitive type in its public fields. The struct for a particular
1616+ * primitive type can be obtained using the static "get" method.
1617+ */
1618+ private static class PrimitiveTypeInfo {
1619+
1620+ /** "base type" used in various descriptors (see JVMS section 4.3.2) */
1621+ public String baseTypeString;
1622+
1623+ /** name of corresponding wrapper class */
1624+ public String wrapperClassName;
1625+
1626+ /** method descriptor for wrapper class "valueOf" factory method */
1627+ public String wrapperValueOfDesc;
1628+
1629+ /** name of wrapper class method for retrieving primitive value */
1630+ public String unwrapMethodName;
1631+
1632+ /** descriptor of same method */
1633+ public String unwrapMethodDesc;
1634+
1635+ private static Map<Class,PrimitiveTypeInfo> table =
1636+ new HashMap<Class,PrimitiveTypeInfo>();
1637+ static {
1638+ add(byte.class, Byte.class);
1639+ add(char.class, Character.class);
1640+ add(double.class, Double.class);
1641+ add(float.class, Float.class);
1642+ add(int.class, Integer.class);
1643+ add(long.class, Long.class);
1644+ add(short.class, Short.class);
1645+ add(boolean.class, Boolean.class);
1646+ }
1647+
1648+ private static void add(Class primitiveClass, Class wrapperClass) {
1649+ table.put(primitiveClass,
1650+ new PrimitiveTypeInfo(primitiveClass, wrapperClass));
1651+ }
1652+
1653+ private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) {
1654+ assert primitiveClass.isPrimitive();
1655+
1656+ baseTypeString =
1657+ Array.newInstance(primitiveClass, 0)
1658+ .getClass().getName().substring(1);
1659+ wrapperClassName = dotToSlash(wrapperClass.getName());
1660+ wrapperValueOfDesc =
1661+ "(" + baseTypeString + ")L" + wrapperClassName + ";";
1662+ unwrapMethodName = primitiveClass.getName() + "Value";
1663+ unwrapMethodDesc = "()" + baseTypeString;
1664+ }
1665+
1666+ public static PrimitiveTypeInfo get(Class cl) {
1667+ return table.get(cl);
1668+ }
1669+ }
1670+
1671+
1672+ /**
1673+ * A ConstantPool object represents the constant pool of a class file
1674+ * being generated. This representation of a constant pool is designed
1675+ * specifically for use by ProxyGenerator; in particular, it assumes
1676+ * that constant pool entries will not need to be resorted (for example,
1677+ * by their type, as the Java compiler does), so that the final index
1678+ * value can be assigned and used when an entry is first created.
1679+ *
1680+ * Note that new entries cannot be created after the constant pool has
1681+ * been written to a class file. To prevent such logic errors, a
1682+ * ConstantPool instance can be marked "read only", so that further
1683+ * attempts to add new entries will fail with a runtime exception.
1684+ *
1685+ * See JVMS section 4.4 for more information about the constant pool
1686+ * of a class file.
1687+ */
1688+ private static class ConstantPool {
1689+
1690+ /**
1691+ * list of constant pool entries, in constant pool index order.
1692+ *
1693+ * This list is used when writing the constant pool to a stream
1694+ * and for assigning the next index value. Note that element 0
1695+ * of this list corresponds to constant pool index 1.
1696+ */
1697+ private List<Entry> pool = new ArrayList<Entry>(32);
1698+
1699+ /**
1700+ * maps constant pool data of all types to constant pool indexes.
1701+ *
1702+ * This map is used to look up the index of an existing entry for
1703+ * values of all types.
1704+ */
1705+ private Map<Object,Short> map = new HashMap<Object,Short>(16);
1706+
1707+ /** true if no new constant pool entries may be added */
1708+ private boolean readOnly = false;
1709+
1710+ /**
1711+ * Get or assign the index for a CONSTANT_Utf8 entry.
1712+ */
1713+ public short getUtf8(String s) {
1714+ if (s == null) {
1715+ throw new NullPointerException();
1716+ }
1717+ return getValue(s);
1718+ }
1719+
1720+ /**
1721+ * Get or assign the index for a CONSTANT_Integer entry.
1722+ */
1723+ public short getInteger(int i) {
1724+ return getValue(new Integer(i));
1725+ }
1726+
1727+ /**
1728+ * Get or assign the index for a CONSTANT_Float entry.
1729+ */
1730+ public short getFloat(float f) {
1731+ return getValue(new Float(f));
1732+ }
1733+
1734+ /**
1735+ * Get or assign the index for a CONSTANT_Class entry.
1736+ */
1737+ public short getClass(String name) {
1738+ short utf8Index = getUtf8(name);
1739+ return getIndirect(new IndirectEntry(
1740+ CONSTANT_CLASS, utf8Index));
1741+ }
1742+
1743+ /**
1744+ * Get or assign the index for a CONSTANT_String entry.
1745+ */
1746+ public short getString(String s) {
1747+ short utf8Index = getUtf8(s);
1748+ return getIndirect(new IndirectEntry(
1749+ CONSTANT_STRING, utf8Index));
1750+ }
1751+
1752+ /**
1753+ * Get or assign the index for a CONSTANT_FieldRef entry.
1754+ */
1755+ public short getFieldRef(String className,
1756+ String name, String descriptor)
1757+ {
1758+ short classIndex = getClass(className);
1759+ short nameAndTypeIndex = getNameAndType(name, descriptor);
1760+ return getIndirect(new IndirectEntry(
1761+ CONSTANT_FIELD, classIndex, nameAndTypeIndex));
1762+ }
1763+
1764+ /**
1765+ * Get or assign the index for a CONSTANT_MethodRef entry.
1766+ */
1767+ public short getMethodRef(String className,
1768+ String name, String descriptor)
1769+ {
1770+ short classIndex = getClass(className);
1771+ short nameAndTypeIndex = getNameAndType(name, descriptor);
1772+ return getIndirect(new IndirectEntry(
1773+ CONSTANT_METHOD, classIndex, nameAndTypeIndex));
1774+ }
1775+
1776+ /**
1777+ * Get or assign the index for a CONSTANT_InterfaceMethodRef entry.
1778+ */
1779+ public short getInterfaceMethodRef(String className, String name,
1780+ String descriptor)
1781+ {
1782+ short classIndex = getClass(className);
1783+ short nameAndTypeIndex = getNameAndType(name, descriptor);
1784+ return getIndirect(new IndirectEntry(
1785+ CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex));
1786+ }
1787+
1788+ /**
1789+ * Get or assign the index for a CONSTANT_NameAndType entry.
1790+ */
1791+ public short getNameAndType(String name, String descriptor) {
1792+ short nameIndex = getUtf8(name);
1793+ short descriptorIndex = getUtf8(descriptor);
1794+ return getIndirect(new IndirectEntry(
1795+ CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex));
1796+ }
1797+
1798+ /**
1799+ * Set this ConstantPool instance to be "read only".
1800+ *
1801+ * After this method has been called, further requests to get
1802+ * an index for a non-existent entry will cause an InternalError
1803+ * to be thrown instead of creating of the entry.
1804+ */
1805+ public void setReadOnly() {
1806+ readOnly = true;
1807+ }
1808+
1809+ /**
1810+ * Write this constant pool to a stream as part of
1811+ * the class file format.
1812+ *
1813+ * This consists of writing the "constant_pool_count" and
1814+ * "constant_pool[]" items of the "ClassFile" structure, as
1815+ * described in JVMS section 4.1.
1816+ */
1817+ public void write(OutputStream out) throws IOException {
1818+ DataOutputStream dataOut = new DataOutputStream(out);
1819+
1820+ // constant_pool_count: number of entries plus one
1821+ dataOut.writeShort(pool.size() + 1);
1822+
1823+ for (Entry e : pool) {
1824+ e.write(dataOut);
1825+ }
1826+ }
1827+
1828+ /**
1829+ * Add a new constant pool entry and return its index.
1830+ */
1831+ private short addEntry(Entry entry) {
1832+ pool.add(entry);
1833+ /*
1834+ * Note that this way of determining the index of the
1835+ * added entry is wrong if this pool supports
1836+ * CONSTANT_Long or CONSTANT_Double entries.
1837+ */
1838+ if (pool.size() >= 65535) {
1839+ throw new IllegalArgumentException(
1840+ "constant pool size limit exceeded");
1841+ }
1842+ return (short) pool.size();
1843+ }
1844+
1845+ /**
1846+ * Get or assign the index for an entry of a type that contains
1847+ * a direct value. The type of the given object determines the
1848+ * type of the desired entry as follows:
1849+ *
1850+ * java.lang.String CONSTANT_Utf8
1851+ * java.lang.Integer CONSTANT_Integer
1852+ * java.lang.Float CONSTANT_Float
1853+ * java.lang.Long CONSTANT_Long
1854+ * java.lang.Double CONSTANT_DOUBLE
1855+ */
1856+ private short getValue(Object key) {
1857+ Short index = map.get(key);
1858+ if (index != null) {
1859+ return index.shortValue();
1860+ } else {
1861+ if (readOnly) {
1862+ throw new InternalError(
1863+ "late constant pool addition: " + key);
1864+ }
1865+ short i = addEntry(new ValueEntry(key));
1866+ map.put(key, new Short(i));
1867+ return i;
1868+ }
1869+ }
1870+
1871+ /**
1872+ * Get or assign the index for an entry of a type that contains
1873+ * references to other constant pool entries.
1874+ */
1875+ private short getIndirect(IndirectEntry e) {
1876+ Short index = map.get(e);
1877+ if (index != null) {
1878+ return index.shortValue();
1879+ } else {
1880+ if (readOnly) {
1881+ throw new InternalError("late constant pool addition");
1882+ }
1883+ short i = addEntry(e);
1884+ map.put(e, new Short(i));
1885+ return i;
1886+ }
1887+ }
1888+
1889+ /**
1890+ * Entry is the abstact superclass of all constant pool entry types
1891+ * that can be stored in the "pool" list; its purpose is to define a
1892+ * common method for writing constant pool entries to a class file.
1893+ */
1894+ private static abstract class Entry {
1895+ public abstract void write(DataOutputStream out)
1896+ throws IOException;
1897+ }
1898+
1899+ /**
1900+ * ValueEntry represents a constant pool entry of a type that
1901+ * contains a direct value (see the comments for the "getValue"
1902+ * method for a list of such types).
1903+ *
1904+ * ValueEntry objects are not used as keys for their entries in the
1905+ * Map "map", so no useful hashCode or equals methods are defined.
1906+ */
1907+ private static class ValueEntry extends Entry {
1908+ private Object value;
1909+
1910+ public ValueEntry(Object value) {
1911+ this.value = value;
1912+ }
1913+
1914+ public void write(DataOutputStream out) throws IOException {
1915+ if (value instanceof String) {
1916+ out.writeByte(CONSTANT_UTF8);
1917+ out.writeUTF((String) value);
1918+ } else if (value instanceof Integer) {
1919+ out.writeByte(CONSTANT_INTEGER);
1920+ out.writeInt(((Integer) value).intValue());
1921+ } else if (value instanceof Float) {
1922+ out.writeByte(CONSTANT_FLOAT);
1923+ out.writeFloat(((Float) value).floatValue());
1924+ } else if (value instanceof Long) {
1925+ out.writeByte(CONSTANT_LONG);
1926+ out.writeLong(((Long) value).longValue());
1927+ } else if (value instanceof Double) {
1928+ out.writeDouble(CONSTANT_DOUBLE);
1929+ out.writeDouble(((Double) value).doubleValue());
1930+ } else {
1931+ throw new InternalError("bogus value entry: " + value);
1932+ }
1933+ }
1934+ }
1935+
1936+ /**
1937+ * IndirectEntry represents a constant pool entry of a type that
1938+ * references other constant pool entries, i.e., the following types:
1939+ *
1940+ * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref,
1941+ * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and
1942+ * CONSTANT_NameAndType.
1943+ *
1944+ * Each of these entry types contains either one or two indexes of
1945+ * other constant pool entries.
1946+ *
1947+ * IndirectEntry objects are used as the keys for their entries in
1948+ * the Map "map", so the hashCode and equals methods are overridden
1949+ * to allow matching.
1950+ */
1951+ private static class IndirectEntry extends Entry {
1952+ private int tag;
1953+ private short index0;
1954+ private short index1;
1955+
1956+ /**
1957+ * Construct an IndirectEntry for a constant pool entry type
1958+ * that contains one index of another entry.
1959+ */
1960+ public IndirectEntry(int tag, short index) {
1961+ this.tag = tag;
1962+ this.index0 = index;
1963+ this.index1 = 0;
1964+ }
1965+
1966+ /**
1967+ * Construct an IndirectEntry for a constant pool entry type
1968+ * that contains two indexes for other entries.
1969+ */
1970+ public IndirectEntry(int tag, short index0, short index1) {
1971+ this.tag = tag;
1972+ this.index0 = index0;
1973+ this.index1 = index1;
1974+ }
1975+
1976+ public void write(DataOutputStream out) throws IOException {
1977+ out.writeByte(tag);
1978+ out.writeShort(index0);
1979+ /*
1980+ * If this entry type contains two indexes, write
1981+ * out the second, too.
1982+ */
1983+ if (tag == CONSTANT_FIELD ||
1984+ tag == CONSTANT_METHOD ||
1985+ tag == CONSTANT_INTERFACEMETHOD ||
1986+ tag == CONSTANT_NAMEANDTYPE)
1987+ {
1988+ out.writeShort(index1);
1989+ }
1990+ }
1991+
1992+ public int hashCode() {
1993+ return tag + index0 + index1;
1994+ }
1995+
1996+ public boolean equals(Object obj) {
1997+ if (obj instanceof IndirectEntry) {
1998+ IndirectEntry other = (IndirectEntry) obj;
1999+ if (tag == other.tag &&
2000+ index0 == other.index0 && index1 == other.index1)
2001+ {
2002+ return true;
2003+ }
2004+ }
2005+ return false;
2006+ }
2007+ }
2008+ }
2009+}