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