1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25/*
26 * This file is available under and governed by the GNU General Public
27 * License version 2 only, as published by the Free Software Foundation.
28 * However, the following notice accompanied the original version of this
29 * file:
30 *
31 * ASM: a very small and fast Java bytecode manipulation framework
32 * Copyright (c) 2000-2011 INRIA, France Telecom
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. Neither the name of the copyright holders nor the names of its
44 *    contributors may be used to endorse or promote products derived from
45 *    this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59package jdk.internal.org.objectweb.asm.commons;
60
61import java.io.ByteArrayOutputStream;
62import java.io.DataOutput;
63import java.io.DataOutputStream;
64import java.io.IOException;
65import java.security.MessageDigest;
66import java.util.ArrayList;
67import java.util.Arrays;
68import java.util.Collection;
69
70import jdk.internal.org.objectweb.asm.ClassVisitor;
71import jdk.internal.org.objectweb.asm.FieldVisitor;
72import jdk.internal.org.objectweb.asm.MethodVisitor;
73import jdk.internal.org.objectweb.asm.Opcodes;
74
75/**
76 * A {@link ClassVisitor} that adds a serial version unique identifier to a
77 * class if missing. Here is typical usage of this class:
78 *
79 * <pre>
80 *   ClassWriter cw = new ClassWriter(...);
81 *   ClassVisitor sv = new SerialVersionUIDAdder(cw);
82 *   ClassVisitor ca = new MyClassAdapter(sv);
83 *   new ClassReader(originalClass).accept(ca, false);
84 * </pre>
85 *
86 * The SVUID algorithm can be found <a href=
87 * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
88 * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
89 *
90 * <pre>
91 * The serialVersionUID is computed using the signature of a stream of bytes
92 * that reflect the class definition. The National Institute of Standards and
93 * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
94 * signature for the stream. The first two 32-bit quantities are used to form a
95 * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
96 * types to a sequence of bytes. The values input to the stream are defined by
97 * the Java Virtual Machine (VM) specification for classes.
98 *
99 * The sequence of items in the stream is as follows:
100 *
101 * 1. The class name written using UTF encoding.
102 * 2. The class modifiers written as a 32-bit integer.
103 * 3. The name of each interface sorted by name written using UTF encoding.
104 * 4. For each field of the class sorted by field name (except private static
105 * and private transient fields):
106 * 1. The name of the field in UTF encoding.
107 * 2. The modifiers of the field written as a 32-bit integer.
108 * 3. The descriptor of the field in UTF encoding
109 * 5. If a class initializer exists, write out the following:
110 * 1. The name of the method, &lt;clinit&gt;, in UTF encoding.
111 * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
112 * written as a 32-bit integer.
113 * 3. The descriptor of the method, ()V, in UTF encoding.
114 * 6. For each non-private constructor sorted by method name and signature:
115 * 1. The name of the method, &lt;init&gt;, in UTF encoding.
116 * 2. The modifiers of the method written as a 32-bit integer.
117 * 3. The descriptor of the method in UTF encoding.
118 * 7. For each non-private method sorted by method name and signature:
119 * 1. The name of the method in UTF encoding.
120 * 2. The modifiers of the method written as a 32-bit integer.
121 * 3. The descriptor of the method in UTF encoding.
122 * 8. The SHA-1 algorithm is executed on the stream of bytes produced by
123 * DataOutputStream and produces five 32-bit values sha[0..4].
124 *
125 * 9. The hash value is assembled from the first and second 32-bit values of
126 * the SHA-1 message digest. If the result of the message digest, the five
127 * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
128 * sha, the hash value would be computed as follows:
129 *
130 * long hash = ((sha[0] &gt;&gt;&gt; 24) &amp; 0xFF) |
131 * ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
132 * ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
133 * ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
134 * ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
135 * ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
136 * ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
137 * ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
138 * </pre>
139 *
140 * @author Rajendra Inamdar, Vishal Vishnoi
141 */
142public class SerialVersionUIDAdder extends ClassVisitor {
143
144    /**
145     * Flag that indicates if we need to compute SVUID.
146     */
147    private boolean computeSVUID;
148
149    /**
150     * Set to true if the class already has SVUID.
151     */
152    private boolean hasSVUID;
153
154    /**
155     * Classes access flags.
156     */
157    private int access;
158
159    /**
160     * Internal name of the class
161     */
162    private String name;
163
164    /**
165     * Interfaces implemented by the class.
166     */
167    private String[] interfaces;
168
169    /**
170     * Collection of fields. (except private static and private transient
171     * fields)
172     */
173    private Collection<Item> svuidFields;
174
175    /**
176     * Set to true if the class has static initializer.
177     */
178    private boolean hasStaticInitializer;
179
180    /**
181     * Collection of non-private constructors.
182     */
183    private Collection<Item> svuidConstructors;
184
185    /**
186     * Collection of non-private methods.
187     */
188    private Collection<Item> svuidMethods;
189
190    /**
191     * Creates a new {@link SerialVersionUIDAdder}. <i>Subclasses must not use
192     * this constructor</i>. Instead, they must use the
193     * {@link #SerialVersionUIDAdder(int, ClassVisitor)} version.
194     *
195     * @param cv
196     *            a {@link ClassVisitor} to which this visitor will delegate
197     *            calls.
198     * @throws IllegalStateException
199     *             If a subclass calls this constructor.
200     */
201    public SerialVersionUIDAdder(final ClassVisitor cv) {
202        this(Opcodes.ASM5, cv);
203        if (getClass() != SerialVersionUIDAdder.class) {
204            throw new IllegalStateException();
205        }
206    }
207
208    /**
209     * Creates a new {@link SerialVersionUIDAdder}.
210     *
211     * @param api
212     *            the ASM API version implemented by this visitor. Must be one
213     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
214     * @param cv
215     *            a {@link ClassVisitor} to which this visitor will delegate
216     *            calls.
217     */
218    protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
219        super(api, cv);
220        svuidFields = new ArrayList<Item>();
221        svuidConstructors = new ArrayList<Item>();
222        svuidMethods = new ArrayList<Item>();
223    }
224
225    // ------------------------------------------------------------------------
226    // Overridden methods
227    // ------------------------------------------------------------------------
228
229    /*
230     * Visit class header and get class name, access , and interfaces
231     * information (step 1,2, and 3) for SVUID computation.
232     */
233    @Override
234    public void visit(final int version, final int access, final String name,
235            final String signature, final String superName,
236            final String[] interfaces) {
237        computeSVUID = (access & Opcodes.ACC_ENUM) == 0;
238
239        if (computeSVUID) {
240            this.name = name;
241            this.access = access;
242            this.interfaces = new String[interfaces.length];
243            System.arraycopy(interfaces, 0, this.interfaces, 0,
244                    interfaces.length);
245        }
246
247        super.visit(version, access, name, signature, superName, interfaces);
248    }
249
250    /*
251     * Visit the methods and get constructor and method information (step 5 and
252     * 7). Also determine if there is a class initializer (step 6).
253     */
254    @Override
255    public MethodVisitor visitMethod(final int access, final String name,
256            final String desc, final String signature, final String[] exceptions) {
257        if (computeSVUID) {
258            if ("<clinit>".equals(name)) {
259                hasStaticInitializer = true;
260            }
261            /*
262             * Remembers non private constructors and methods for SVUID
263             * computation For constructor and method modifiers, only the
264             * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
265             * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
266             * are used.
267             */
268            int mods = access
269                    & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
270                            | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
271                            | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
272                            | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
273
274            // all non private methods
275            if ((access & Opcodes.ACC_PRIVATE) == 0) {
276                if ("<init>".equals(name)) {
277                    svuidConstructors.add(new Item(name, mods, desc));
278                } else if (!"<clinit>".equals(name)) {
279                    svuidMethods.add(new Item(name, mods, desc));
280                }
281            }
282        }
283
284        return super.visitMethod(access, name, desc, signature, exceptions);
285    }
286
287    /*
288     * Gets class field information for step 4 of the algorithm. Also determines
289     * if the class already has a SVUID.
290     */
291    @Override
292    public FieldVisitor visitField(final int access, final String name,
293            final String desc, final String signature, final Object value) {
294        if (computeSVUID) {
295            if ("serialVersionUID".equals(name)) {
296                // since the class already has SVUID, we won't be computing it.
297                computeSVUID = false;
298                hasSVUID = true;
299            }
300            /*
301             * Remember field for SVUID computation For field modifiers, only
302             * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
303             * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
304             * computing serialVersionUID values.
305             */
306            if ((access & Opcodes.ACC_PRIVATE) == 0
307                    || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) {
308                int mods = access
309                        & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
310                                | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
311                                | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
312                svuidFields.add(new Item(name, mods, desc));
313            }
314        }
315
316        return super.visitField(access, name, desc, signature, value);
317    }
318
319    /**
320     * Handle a bizarre special case. Nested classes (static classes declared
321     * inside another class) that are protected have their access bit set to
322     * public in their class files to deal with some odd reflection situation.
323     * Our SVUID computation must do as the JVM does and ignore access bits in
324     * the class file in favor of the access bits InnerClass attribute.
325     */
326    @Override
327    public void visitInnerClass(final String aname, final String outerName,
328            final String innerName, final int attr_access) {
329        if ((name != null) && name.equals(aname)) {
330            this.access = attr_access;
331        }
332        super.visitInnerClass(aname, outerName, innerName, attr_access);
333    }
334
335    /*
336     * Add the SVUID if class doesn't have one
337     */
338    @Override
339    public void visitEnd() {
340        // compute SVUID and add it to the class
341        if (computeSVUID && !hasSVUID) {
342            try {
343                addSVUID(computeSVUID());
344            } catch (Throwable e) {
345                throw new RuntimeException("Error while computing SVUID for "
346                        + name, e);
347            }
348        }
349
350        super.visitEnd();
351    }
352
353    // ------------------------------------------------------------------------
354    // Utility methods
355    // ------------------------------------------------------------------------
356
357    /**
358     * Returns true if the class already has a SVUID field. The result of this
359     * method is only valid when visitEnd is or has been called.
360     *
361     * @return true if the class already has a SVUID field.
362     */
363    public boolean hasSVUID() {
364        return hasSVUID;
365    }
366
367    protected void addSVUID(long svuid) {
368        FieldVisitor fv = super.visitField(Opcodes.ACC_FINAL
369                + Opcodes.ACC_STATIC, "serialVersionUID", "J", null, svuid);
370        if (fv != null) {
371            fv.visitEnd();
372        }
373    }
374
375    /**
376     * Computes and returns the value of SVUID.
377     *
378     * @return Returns the serial version UID
379     * @throws IOException
380     *             if an I/O error occurs
381     */
382    protected long computeSVUID() throws IOException {
383        ByteArrayOutputStream bos;
384        DataOutputStream dos = null;
385        long svuid = 0;
386
387        try {
388            bos = new ByteArrayOutputStream();
389            dos = new DataOutputStream(bos);
390
391            /*
392             * 1. The class name written using UTF encoding.
393             */
394            dos.writeUTF(name.replace('/', '.'));
395
396            /*
397             * 2. The class modifiers written as a 32-bit integer.
398             */
399            int access = this.access;
400            if ((access & Opcodes.ACC_INTERFACE) != 0) {
401                access = (svuidMethods.size() > 0) ? (access | Opcodes.ACC_ABSTRACT)
402                        : (access & ~Opcodes.ACC_ABSTRACT);
403            }
404            dos.writeInt(access
405                    & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
406                            | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
407
408            /*
409             * 3. The name of each interface sorted by name written using UTF
410             * encoding.
411             */
412            Arrays.sort(interfaces);
413            for (int i = 0; i < interfaces.length; i++) {
414                dos.writeUTF(interfaces[i].replace('/', '.'));
415            }
416
417            /*
418             * 4. For each field of the class sorted by field name (except
419             * private static and private transient fields):
420             *
421             * 1. The name of the field in UTF encoding. 2. The modifiers of the
422             * field written as a 32-bit integer. 3. The descriptor of the field
423             * in UTF encoding
424             *
425             * Note that field signatures are not dot separated. Method and
426             * constructor signatures are dot separated. Go figure...
427             */
428            writeItems(svuidFields, dos, false);
429
430            /*
431             * 5. If a class initializer exists, write out the following: 1. The
432             * name of the method, <clinit>, in UTF encoding. 2. The modifier of
433             * the method, java.lang.reflect.Modifier.STATIC, written as a
434             * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
435             * encoding.
436             */
437            if (hasStaticInitializer) {
438                dos.writeUTF("<clinit>");
439                dos.writeInt(Opcodes.ACC_STATIC);
440                dos.writeUTF("()V");
441            } // if..
442
443            /*
444             * 6. For each non-private constructor sorted by method name and
445             * signature: 1. The name of the method, <init>, in UTF encoding. 2.
446             * The modifiers of the method written as a 32-bit integer. 3. The
447             * descriptor of the method in UTF encoding.
448             */
449            writeItems(svuidConstructors, dos, true);
450
451            /*
452             * 7. For each non-private method sorted by method name and
453             * signature: 1. The name of the method in UTF encoding. 2. The
454             * modifiers of the method written as a 32-bit integer. 3. The
455             * descriptor of the method in UTF encoding.
456             */
457            writeItems(svuidMethods, dos, true);
458
459            dos.flush();
460
461            /*
462             * 8. The SHA-1 algorithm is executed on the stream of bytes
463             * produced by DataOutputStream and produces five 32-bit values
464             * sha[0..4].
465             */
466            byte[] hashBytes = computeSHAdigest(bos.toByteArray());
467
468            /*
469             * 9. The hash value is assembled from the first and second 32-bit
470             * values of the SHA-1 message digest. If the result of the message
471             * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
472             * five int values named sha, the hash value would be computed as
473             * follows:
474             *
475             * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF)
476             * << 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
477             * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
478             * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
479             * 56;
480             */
481            for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
482                svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
483            }
484        } finally {
485            // close the stream (if open)
486            if (dos != null) {
487                dos.close();
488            }
489        }
490
491        return svuid;
492    }
493
494    /**
495     * Returns the SHA-1 message digest of the given value.
496     *
497     * @param value
498     *            the value whose SHA message digest must be computed.
499     * @return the SHA-1 message digest of the given value.
500     */
501    protected byte[] computeSHAdigest(final byte[] value) {
502        try {
503            return MessageDigest.getInstance("SHA").digest(value);
504        } catch (Exception e) {
505            throw new UnsupportedOperationException(e.toString());
506        }
507    }
508
509    /**
510     * Sorts the items in the collection and writes it to the data output stream
511     *
512     * @param itemCollection
513     *            collection of items
514     * @param dos
515     *            a <code>DataOutputStream</code> value
516     * @param dotted
517     *            a <code>boolean</code> value
518     * @exception IOException
519     *                if an error occurs
520     */
521    private static void writeItems(final Collection<Item> itemCollection,
522            final DataOutput dos, final boolean dotted) throws IOException {
523        int size = itemCollection.size();
524        Item[] items = itemCollection.toArray(new Item[size]);
525        Arrays.sort(items);
526        for (int i = 0; i < size; i++) {
527            dos.writeUTF(items[i].name);
528            dos.writeInt(items[i].access);
529            dos.writeUTF(dotted ? items[i].desc.replace('/', '.')
530                    : items[i].desc);
531        }
532    }
533
534    // ------------------------------------------------------------------------
535    // Inner classes
536    // ------------------------------------------------------------------------
537
538    private static class Item implements Comparable<Item> {
539
540        final String name;
541
542        final int access;
543
544        final String desc;
545
546        Item(final String name, final int access, final String desc) {
547            this.name = name;
548            this.access = access;
549            this.desc = desc;
550        }
551
552        public int compareTo(final Item other) {
553            int retVal = name.compareTo(other.name);
554            if (retVal == 0) {
555                retVal = desc.compareTo(other.desc);
556            }
557            return retVal;
558        }
559
560        @Override
561        public boolean equals(final Object o) {
562            if (o instanceof Item) {
563                return compareTo((Item) o) == 0;
564            }
565            return false;
566        }
567
568        @Override
569        public int hashCode() {
570            return (name + desc).hashCode();
571        }
572    }
573}
574