| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * This file is available under and governed by the GNU General Public |
| * License version 2 only, as published by the Free Software Foundation. |
| * However, the following notice accompanied the original version of this |
| * file: |
| * |
| * ASM: a very small and fast Java bytecode manipulation framework |
| * Copyright (c) 2000-2011 INRIA, France Telecom |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package jdk.internal.org.objectweb.asm; |
| |
| /** |
| * An {@link AnnotationVisitor} that generates annotations in bytecode form. |
| * |
| * @author Eric Bruneton |
| * @author Eugene Kuleshov |
| */ |
| final class AnnotationWriter extends AnnotationVisitor { |
| |
| /** |
| * The class writer to which this annotation must be added. |
| */ |
| private final ClassWriter cw; |
| |
| /** |
| * The number of values in this annotation. |
| */ |
| private int size; |
| |
| /** |
| * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation |
| * writers used for annotation default and annotation arrays use unnamed |
| * values. |
| */ |
| private final boolean named; |
| |
| /** |
| * The annotation values in bytecode form. This byte vector only contains |
| * the values themselves, i.e. the number of values must be stored as a |
| * unsigned short just before these bytes. |
| */ |
| private final ByteVector bv; |
| |
| /** |
| * The byte vector to be used to store the number of values of this |
| * annotation. See {@link #bv}. |
| */ |
| private final ByteVector parent; |
| |
| /** |
| * Where the number of values of this annotation must be stored in |
| * {@link #parent}. |
| */ |
| private final int offset; |
| |
| /** |
| * Next annotation writer. This field is used to store annotation lists. |
| */ |
| AnnotationWriter next; |
| |
| /** |
| * Previous annotation writer. This field is used to store annotation lists. |
| */ |
| AnnotationWriter prev; |
| |
| // ------------------------------------------------------------------------ |
| // Constructor |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Constructs a new {@link AnnotationWriter}. |
| * |
| * @param cw |
| * the class writer to which this annotation must be added. |
| * @param named |
| * <tt>true<tt> if values are named, <tt>false</tt> otherwise. |
| * @param bv |
| * where the annotation values must be stored. |
| * @param parent |
| * where the number of annotation values must be stored. |
| * @param offset |
| * where in <tt>parent</tt> the number of annotation values must |
| * be stored. |
| */ |
| AnnotationWriter(final ClassWriter cw, final boolean named, |
| final ByteVector bv, final ByteVector parent, final int offset) { |
| super(Opcodes.ASM5); |
| this.cw = cw; |
| this.named = named; |
| this.bv = bv; |
| this.parent = parent; |
| this.offset = offset; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Implementation of the AnnotationVisitor abstract class |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public void visit(final String name, final Object value) { |
| ++size; |
| if (named) { |
| bv.putShort(cw.newUTF8(name)); |
| } |
| if (value instanceof String) { |
| bv.put12('s', cw.newUTF8((String) value)); |
| } else if (value instanceof Byte) { |
| bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); |
| } else if (value instanceof Boolean) { |
| int v = ((Boolean) value).booleanValue() ? 1 : 0; |
| bv.put12('Z', cw.newInteger(v).index); |
| } else if (value instanceof Character) { |
| bv.put12('C', cw.newInteger(((Character) value).charValue()).index); |
| } else if (value instanceof Short) { |
| bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); |
| } else if (value instanceof Type) { |
| bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); |
| } else if (value instanceof byte[]) { |
| byte[] v = (byte[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('B', cw.newInteger(v[i]).index); |
| } |
| } else if (value instanceof boolean[]) { |
| boolean[] v = (boolean[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); |
| } |
| } else if (value instanceof short[]) { |
| short[] v = (short[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('S', cw.newInteger(v[i]).index); |
| } |
| } else if (value instanceof char[]) { |
| char[] v = (char[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('C', cw.newInteger(v[i]).index); |
| } |
| } else if (value instanceof int[]) { |
| int[] v = (int[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('I', cw.newInteger(v[i]).index); |
| } |
| } else if (value instanceof long[]) { |
| long[] v = (long[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('J', cw.newLong(v[i]).index); |
| } |
| } else if (value instanceof float[]) { |
| float[] v = (float[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('F', cw.newFloat(v[i]).index); |
| } |
| } else if (value instanceof double[]) { |
| double[] v = (double[]) value; |
| bv.put12('[', v.length); |
| for (int i = 0; i < v.length; i++) { |
| bv.put12('D', cw.newDouble(v[i]).index); |
| } |
| } else { |
| Item i = cw.newConstItem(value); |
| bv.put12(".s.IFJDCS".charAt(i.type), i.index); |
| } |
| } |
| |
| @Override |
| public void visitEnum(final String name, final String desc, |
| final String value) { |
| ++size; |
| if (named) { |
| bv.putShort(cw.newUTF8(name)); |
| } |
| bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(final String name, |
| final String desc) { |
| ++size; |
| if (named) { |
| bv.putShort(cw.newUTF8(name)); |
| } |
| // write tag and type, and reserve space for values count |
| bv.put12('@', cw.newUTF8(desc)).putShort(0); |
| return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); |
| } |
| |
| @Override |
| public AnnotationVisitor visitArray(final String name) { |
| ++size; |
| if (named) { |
| bv.putShort(cw.newUTF8(name)); |
| } |
| // write tag, and reserve space for array size |
| bv.put12('[', 0); |
| return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); |
| } |
| |
| @Override |
| public void visitEnd() { |
| if (parent != null) { |
| byte[] data = parent.data; |
| data[offset] = (byte) (size >>> 8); |
| data[offset + 1] = (byte) size; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Utility methods |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Returns the size of this annotation writer list. |
| * |
| * @return the size of this annotation writer list. |
| */ |
| int getSize() { |
| int size = 0; |
| AnnotationWriter aw = this; |
| while (aw != null) { |
| size += aw.bv.length; |
| aw = aw.next; |
| } |
| return size; |
| } |
| |
| /** |
| * Puts the annotations of this annotation writer list into the given byte |
| * vector. |
| * |
| * @param out |
| * where the annotations must be put. |
| */ |
| void put(final ByteVector out) { |
| int n = 0; |
| int size = 2; |
| AnnotationWriter aw = this; |
| AnnotationWriter last = null; |
| while (aw != null) { |
| ++n; |
| size += aw.bv.length; |
| aw.visitEnd(); // in case user forgot to call visitEnd |
| aw.prev = last; |
| last = aw; |
| aw = aw.next; |
| } |
| out.putInt(size); |
| out.putShort(n); |
| aw = last; |
| while (aw != null) { |
| out.putByteArray(aw.bv.data, 0, aw.bv.length); |
| aw = aw.prev; |
| } |
| } |
| |
| /** |
| * Puts the given annotation lists into the given byte vector. |
| * |
| * @param panns |
| * an array of annotation writer lists. |
| * @param off |
| * index of the first annotation to be written. |
| * @param out |
| * where the annotations must be put. |
| */ |
| static void put(final AnnotationWriter[] panns, final int off, |
| final ByteVector out) { |
| int size = 1 + 2 * (panns.length - off); |
| for (int i = off; i < panns.length; ++i) { |
| size += panns[i] == null ? 0 : panns[i].getSize(); |
| } |
| out.putInt(size).putByte(panns.length - off); |
| for (int i = off; i < panns.length; ++i) { |
| AnnotationWriter aw = panns[i]; |
| AnnotationWriter last = null; |
| int n = 0; |
| while (aw != null) { |
| ++n; |
| aw.visitEnd(); // in case user forgot to call visitEnd |
| aw.prev = last; |
| last = aw; |
| aw = aw.next; |
| } |
| out.putShort(n); |
| aw = last; |
| while (aw != null) { |
| out.putByteArray(aw.bv.data, 0, aw.bv.length); |
| aw = aw.prev; |
| } |
| } |
| } |
| |
| /** |
| * Puts the given type reference and type path into the given bytevector. |
| * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. |
| * |
| * @param typeRef |
| * a reference to the annotated type. See {@link TypeReference}. |
| * @param typePath |
| * the path to the annotated type argument, wildcard bound, array |
| * element type, or static inner type within 'typeRef'. May be |
| * <tt>null</tt> if the annotation targets 'typeRef' as a whole. |
| * @param out |
| * where the type reference and type path must be put. |
| */ |
| static void putTarget(int typeRef, TypePath typePath, ByteVector out) { |
| switch (typeRef >>> 24) { |
| case 0x00: // CLASS_TYPE_PARAMETER |
| case 0x01: // METHOD_TYPE_PARAMETER |
| case 0x16: // METHOD_FORMAL_PARAMETER |
| out.putShort(typeRef >>> 16); |
| break; |
| case 0x13: // FIELD |
| case 0x14: // METHOD_RETURN |
| case 0x15: // METHOD_RECEIVER |
| out.putByte(typeRef >>> 24); |
| break; |
| case 0x47: // CAST |
| case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT |
| case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT |
| case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT |
| case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT |
| out.putInt(typeRef); |
| break; |
| // case 0x10: // CLASS_EXTENDS |
| // case 0x11: // CLASS_TYPE_PARAMETER_BOUND |
| // case 0x12: // METHOD_TYPE_PARAMETER_BOUND |
| // case 0x17: // THROWS |
| // case 0x42: // EXCEPTION_PARAMETER |
| // case 0x43: // INSTANCEOF |
| // case 0x44: // NEW |
| // case 0x45: // CONSTRUCTOR_REFERENCE |
| // case 0x46: // METHOD_REFERENCE |
| default: |
| out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); |
| break; |
| } |
| if (typePath == null) { |
| out.putByte(0); |
| } else { |
| int length = typePath.b[typePath.offset] * 2 + 1; |
| out.putByteArray(typePath.b, typePath.offset, length); |
| } |
| } |
| } |