Mercurial > lasercutter
comparison src/clojure/asm/commons/SerialVersionUIDAdder.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 /*** | |
2 * ASM: a very small and fast Java bytecode manipulation framework | |
3 * Copyright (c) 2000-2005 INRIA, France Telecom | |
4 * All rights reserved. | |
5 * | |
6 * Redistribution and use in source and binary forms, with or without | |
7 * modification, are permitted provided that the following conditions | |
8 * are met: | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of the copyright holders nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
28 * THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 package clojure.asm.commons; | |
31 | |
32 import java.io.ByteArrayOutputStream; | |
33 import java.io.DataOutputStream; | |
34 import java.io.IOException; | |
35 import java.security.MessageDigest; | |
36 import java.util.ArrayList; | |
37 import java.util.Arrays; | |
38 import java.util.Collection; | |
39 | |
40 import clojure.asm.ClassAdapter; | |
41 import clojure.asm.ClassVisitor; | |
42 import clojure.asm.FieldVisitor; | |
43 import clojure.asm.MethodVisitor; | |
44 import clojure.asm.Opcodes; | |
45 | |
46 /** | |
47 * A {@link ClassAdapter} that adds a serial version unique identifier to a | |
48 * class if missing. Here is typical usage of this class: | |
49 * <p/> | |
50 * <pre> | |
51 * ClassWriter cw = new ClassWriter(...); | |
52 * ClassVisitor sv = new SerialVersionUIDAdder(cw); | |
53 * ClassVisitor ca = new MyClassAdapter(sv); | |
54 * new ClassReader(orginalClass).accept(ca, false); | |
55 * </pre> | |
56 * <p/> | |
57 * The SVUID algorithm can be found <a href= | |
58 * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html" | |
59 * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>: | |
60 * <p/> | |
61 * <pre> | |
62 * The serialVersionUID is computed using the signature of a stream of bytes | |
63 * that reflect the class definition. The National Institute of Standards and | |
64 * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a | |
65 * signature for the stream. The first two 32-bit quantities are used to form a | |
66 * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data | |
67 * types to a sequence of bytes. The values input to the stream are defined by | |
68 * the Java Virtual Machine (VM) specification for classes. | |
69 * <p/> | |
70 * The sequence of items in the stream is as follows: | |
71 * <p/> | |
72 * 1. The class name written using UTF encoding. | |
73 * 2. The class modifiers written as a 32-bit integer. | |
74 * 3. The name of each interface sorted by name written using UTF encoding. | |
75 * 4. For each field of the class sorted by field name (except private static | |
76 * and private transient fields): | |
77 * 1. The name of the field in UTF encoding. | |
78 * 2. The modifiers of the field written as a 32-bit integer. | |
79 * 3. The descriptor of the field in UTF encoding | |
80 * 5. If a class initializer exists, write out the following: | |
81 * 1. The name of the method, <clinit>, in UTF encoding. | |
82 * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, | |
83 * written as a 32-bit integer. | |
84 * 3. The descriptor of the method, ()V, in UTF encoding. | |
85 * 6. For each non-private constructor sorted by method name and signature: | |
86 * 1. The name of the method, <init>, in UTF encoding. | |
87 * 2. The modifiers of the method written as a 32-bit integer. | |
88 * 3. The descriptor of the method in UTF encoding. | |
89 * 7. For each non-private method sorted by method name and signature: | |
90 * 1. The name of the method in UTF encoding. | |
91 * 2. The modifiers of the method written as a 32-bit integer. | |
92 * 3. The descriptor of the method in UTF encoding. | |
93 * 8. The SHA-1 algorithm is executed on the stream of bytes produced by | |
94 * DataOutputStream and produces five 32-bit values sha[0..4]. | |
95 * <p/> | |
96 * 9. The hash value is assembled from the first and second 32-bit values of | |
97 * the SHA-1 message digest. If the result of the message digest, the five | |
98 * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named | |
99 * sha, the hash value would be computed as follows: | |
100 * <p/> | |
101 * long hash = ((sha[0] >>> 24) & 0xFF) | | |
102 * ((sha[0] >>> 16) & 0xFF) << 8 | | |
103 * ((sha[0] >>> 8) & 0xFF) << 16 | | |
104 * ((sha[0] >>> 0) & 0xFF) << 24 | | |
105 * ((sha[1] >>> 24) & 0xFF) << 32 | | |
106 * ((sha[1] >>> 16) & 0xFF) << 40 | | |
107 * ((sha[1] >>> 8) & 0xFF) << 48 | | |
108 * ((sha[1] >>> 0) & 0xFF) << 56; | |
109 * </pre> | |
110 * | |
111 * @author Rajendra Inamdar, Vishal Vishnoi | |
112 */ | |
113 public class SerialVersionUIDAdder extends ClassAdapter{ | |
114 | |
115 /** | |
116 * Flag that indicates if we need to compute SVUID. | |
117 */ | |
118 protected boolean computeSVUID; | |
119 | |
120 /** | |
121 * Set to true if the class already has SVUID. | |
122 */ | |
123 protected boolean hasSVUID; | |
124 | |
125 /** | |
126 * Classes access flags. | |
127 */ | |
128 protected int access; | |
129 | |
130 /** | |
131 * Internal name of the class | |
132 */ | |
133 protected String name; | |
134 | |
135 /** | |
136 * Interfaces implemented by the class. | |
137 */ | |
138 protected String[] interfaces; | |
139 | |
140 /** | |
141 * Collection of fields. (except private static and private transient | |
142 * fields) | |
143 */ | |
144 protected Collection svuidFields; | |
145 | |
146 /** | |
147 * Set to true if the class has static initializer. | |
148 */ | |
149 protected boolean hasStaticInitializer; | |
150 | |
151 /** | |
152 * Collection of non-private constructors. | |
153 */ | |
154 protected Collection svuidConstructors; | |
155 | |
156 /** | |
157 * Collection of non-private methods. | |
158 */ | |
159 protected Collection svuidMethods; | |
160 | |
161 /** | |
162 * Creates a new {@link SerialVersionUIDAdder}. | |
163 * | |
164 * @param cv a {@link ClassVisitor} to which this visitor will delegate | |
165 * calls. | |
166 */ | |
167 public SerialVersionUIDAdder(final ClassVisitor cv){ | |
168 super(cv); | |
169 svuidFields = new ArrayList(); | |
170 svuidConstructors = new ArrayList(); | |
171 svuidMethods = new ArrayList(); | |
172 } | |
173 | |
174 // ------------------------------------------------------------------------ | |
175 // Overriden methods | |
176 // ------------------------------------------------------------------------ | |
177 | |
178 /* | |
179 * Visit class header and get class name, access , and intefraces | |
180 * informatoin (step 1,2, and 3) for SVUID computation. | |
181 */ | |
182 | |
183 public void visit( | |
184 final int version, | |
185 final int access, | |
186 final String name, | |
187 final String signature, | |
188 final String superName, | |
189 final String[] interfaces){ | |
190 computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0; | |
191 | |
192 if(computeSVUID) | |
193 { | |
194 this.name = name; | |
195 this.access = access; | |
196 this.interfaces = interfaces; | |
197 } | |
198 | |
199 super.visit(version, access, name, signature, superName, interfaces); | |
200 } | |
201 | |
202 /* | |
203 * Visit the methods and get constructor and method information (step 5 and | |
204 * 7). Also determince if there is a class initializer (step 6). | |
205 */ | |
206 public MethodVisitor visitMethod( | |
207 final int access, | |
208 final String name, | |
209 final String desc, | |
210 final String signature, | |
211 final String[] exceptions){ | |
212 if(computeSVUID) | |
213 { | |
214 if(name.equals("<clinit>")) | |
215 { | |
216 hasStaticInitializer = true; | |
217 } | |
218 /* | |
219 * Remembers non private constructors and methods for SVUID | |
220 * computation For constructor and method modifiers, only the | |
221 * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, | |
222 * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags | |
223 * are used. | |
224 */ | |
225 int mods = access | |
226 & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | |
227 | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC | |
228 | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED | |
229 | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); | |
230 | |
231 // all non private methods | |
232 if((access & Opcodes.ACC_PRIVATE) == 0) | |
233 { | |
234 if(name.equals("<init>")) | |
235 { | |
236 svuidConstructors.add(new Item(name, mods, desc)); | |
237 } | |
238 else if(!name.equals("<clinit>")) | |
239 { | |
240 svuidMethods.add(new Item(name, mods, desc)); | |
241 } | |
242 } | |
243 } | |
244 | |
245 return cv.visitMethod(access, name, desc, signature, exceptions); | |
246 } | |
247 | |
248 /* | |
249 * Gets class field information for step 4 of the alogrithm. Also determines | |
250 * if the class already has a SVUID. | |
251 */ | |
252 public FieldVisitor visitField( | |
253 final int access, | |
254 final String name, | |
255 final String desc, | |
256 final String signature, | |
257 final Object value){ | |
258 if(computeSVUID) | |
259 { | |
260 if(name.equals("serialVersionUID")) | |
261 { | |
262 // since the class already has SVUID, we won't be computing it. | |
263 computeSVUID = false; | |
264 hasSVUID = true; | |
265 } | |
266 /* | |
267 * Remember field for SVUID computation For field modifiers, only | |
268 * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, | |
269 * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when | |
270 * computing serialVersionUID values. | |
271 */ | |
272 int mods = access | |
273 & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | |
274 | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC | |
275 | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT); | |
276 | |
277 if((access & Opcodes.ACC_PRIVATE) == 0 | |
278 || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) | |
279 { | |
280 svuidFields.add(new Item(name, mods, desc)); | |
281 } | |
282 } | |
283 | |
284 return super.visitField(access, name, desc, signature, value); | |
285 } | |
286 | |
287 /* | |
288 * Add the SVUID if class doesn't have one | |
289 */ | |
290 public void visitEnd(){ | |
291 // compute SVUID and add it to the class | |
292 if(computeSVUID && !hasSVUID) | |
293 { | |
294 try | |
295 { | |
296 cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, | |
297 "serialVersionUID", | |
298 "J", | |
299 null, | |
300 new Long(computeSVUID())); | |
301 } | |
302 catch(Throwable e) | |
303 { | |
304 throw new RuntimeException("Error while computing SVUID for " | |
305 + name, e); | |
306 } | |
307 } | |
308 | |
309 super.visitEnd(); | |
310 } | |
311 | |
312 // ------------------------------------------------------------------------ | |
313 // Utility methods | |
314 // ------------------------------------------------------------------------ | |
315 | |
316 /** | |
317 * Returns the value of SVUID if the class doesn't have one already. Please | |
318 * note that 0 is returned if the class already has SVUID, thus use | |
319 * <code>isHasSVUID</code> to determine if the class already had an SVUID. | |
320 * | |
321 * @return Returns the serial version UID | |
322 * @throws IOException | |
323 */ | |
324 protected long computeSVUID() throws IOException{ | |
325 ByteArrayOutputStream bos = null; | |
326 DataOutputStream dos = null; | |
327 long svuid = 0; | |
328 | |
329 try | |
330 { | |
331 bos = new ByteArrayOutputStream(); | |
332 dos = new DataOutputStream(bos); | |
333 | |
334 /* | |
335 * 1. The class name written using UTF encoding. | |
336 */ | |
337 dos.writeUTF(name.replace('/', '.')); | |
338 | |
339 /* | |
340 * 2. The class modifiers written as a 32-bit integer. | |
341 */ | |
342 dos.writeInt(access | |
343 & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | |
344 | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); | |
345 | |
346 /* | |
347 * 3. The name of each interface sorted by name written using UTF | |
348 * encoding. | |
349 */ | |
350 Arrays.sort(interfaces); | |
351 for(int i = 0; i < interfaces.length; i++) | |
352 { | |
353 dos.writeUTF(interfaces[i].replace('/', '.')); | |
354 } | |
355 | |
356 /* | |
357 * 4. For each field of the class sorted by field name (except | |
358 * private static and private transient fields): | |
359 * | |
360 * 1. The name of the field in UTF encoding. 2. The modifiers of the | |
361 * field written as a 32-bit integer. 3. The descriptor of the field | |
362 * in UTF encoding | |
363 * | |
364 * Note that field signatutes are not dot separated. Method and | |
365 * constructor signatures are dot separated. Go figure... | |
366 */ | |
367 writeItems(svuidFields, dos, false); | |
368 | |
369 /* | |
370 * 5. If a class initializer exists, write out the following: 1. The | |
371 * name of the method, <clinit>, in UTF encoding. 2. The modifier of | |
372 * the method, java.lang.reflect.Modifier.STATIC, written as a | |
373 * 32-bit integer. 3. The descriptor of the method, ()V, in UTF | |
374 * encoding. | |
375 */ | |
376 if(hasStaticInitializer) | |
377 { | |
378 dos.writeUTF("<clinit>"); | |
379 dos.writeInt(Opcodes.ACC_STATIC); | |
380 dos.writeUTF("()V"); | |
381 } // if.. | |
382 | |
383 /* | |
384 * 6. For each non-private constructor sorted by method name and | |
385 * signature: 1. The name of the method, <init>, in UTF encoding. 2. | |
386 * The modifiers of the method written as a 32-bit integer. 3. The | |
387 * descriptor of the method in UTF encoding. | |
388 */ | |
389 writeItems(svuidConstructors, dos, true); | |
390 | |
391 /* | |
392 * 7. For each non-private method sorted by method name and | |
393 * signature: 1. The name of the method in UTF encoding. 2. The | |
394 * modifiers of the method written as a 32-bit integer. 3. The | |
395 * descriptor of the method in UTF encoding. | |
396 */ | |
397 writeItems(svuidMethods, dos, true); | |
398 | |
399 dos.flush(); | |
400 | |
401 /* | |
402 * 8. The SHA-1 algorithm is executed on the stream of bytes | |
403 * produced by DataOutputStream and produces five 32-bit values | |
404 * sha[0..4]. | |
405 */ | |
406 byte[] hashBytes = computeSHAdigest(bos.toByteArray()); | |
407 | |
408 /* | |
409 * 9. The hash value is assembled from the first and second 32-bit | |
410 * values of the SHA-1 message digest. If the result of the message | |
411 * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of | |
412 * five int values named sha, the hash value would be computed as | |
413 * follows: | |
414 * | |
415 * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) << | |
416 * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << | |
417 * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << | |
418 * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << | |
419 * 56; | |
420 */ | |
421 for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) | |
422 { | |
423 svuid = (svuid << 8) | (hashBytes[i] & 0xFF); | |
424 } | |
425 } | |
426 finally | |
427 { | |
428 // close the stream (if open) | |
429 if(dos != null) | |
430 { | |
431 dos.close(); | |
432 } | |
433 } | |
434 | |
435 return svuid; | |
436 } | |
437 | |
438 /** | |
439 * Returns the SHA-1 message digest of the given value. | |
440 * | |
441 * @param value the value whose SHA message digest must be computed. | |
442 * @return the SHA-1 message digest of the given value. | |
443 */ | |
444 protected byte[] computeSHAdigest(final byte[] value){ | |
445 try | |
446 { | |
447 return MessageDigest.getInstance("SHA").digest(value); | |
448 } | |
449 catch(Exception e) | |
450 { | |
451 throw new UnsupportedOperationException(e); | |
452 } | |
453 } | |
454 | |
455 /** | |
456 * Sorts the items in the collection and writes it to the data output stream | |
457 * | |
458 * @param itemCollection collection of items | |
459 * @param dos a <code>DataOutputStream</code> value | |
460 * @param dotted a <code>boolean</code> value | |
461 * @throws IOException if an error occurs | |
462 */ | |
463 private void writeItems( | |
464 final Collection itemCollection, | |
465 final DataOutputStream dos, | |
466 final boolean dotted) throws IOException{ | |
467 int size = itemCollection.size(); | |
468 Item items[] = (Item[]) itemCollection.toArray(new Item[size]); | |
469 Arrays.sort(items); | |
470 for(int i = 0; i < size; i++) | |
471 { | |
472 dos.writeUTF(items[i].name); | |
473 dos.writeInt(items[i].access); | |
474 dos.writeUTF(dotted | |
475 ? items[i].desc.replace('/', '.') | |
476 : items[i].desc); | |
477 } | |
478 } | |
479 | |
480 // ------------------------------------------------------------------------ | |
481 // Inner classes | |
482 // ------------------------------------------------------------------------ | |
483 | |
484 static class Item implements Comparable{ | |
485 | |
486 String name; | |
487 | |
488 int access; | |
489 | |
490 String desc; | |
491 | |
492 Item(final String name, final int access, final String desc){ | |
493 this.name = name; | |
494 this.access = access; | |
495 this.desc = desc; | |
496 } | |
497 | |
498 public int compareTo(final Object o){ | |
499 Item other = (Item) o; | |
500 int retVal = name.compareTo(other.name); | |
501 if(retVal == 0) | |
502 { | |
503 retVal = desc.compareTo(other.desc); | |
504 } | |
505 return retVal; | |
506 } | |
507 } | |
508 } |