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