/* |
|
* 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); |
|
} |
|
} |
|
} |