/*
* ClassWriter.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.reflection.emit;
import com.strobel.core.ReadOnlyList;
import com.strobel.core.StringUtilities;
import com.strobel.core.VerifyArgument;
import com.strobel.reflection.Flags;
import com.strobel.reflection.MemberInfo;
import com.strobel.reflection.MethodBase;
import com.strobel.reflection.MethodInfo;
import com.strobel.reflection.MethodList;
import com.strobel.reflection.PrimitiveTypes;
import com.strobel.reflection.Type;
import com.strobel.reflection.TypeList;
import com.strobel.reflection.Types;
import com.strobel.util.TypeUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import static com.strobel.reflection.Flags.*;
/**
* @author Mike Strobel
*/
@SuppressWarnings("MismatchedReadAndWriteOfArray")
final class ClassWriter {
private final static int JAVA_MAGIC = 0xCAFEBABE;
private final static int DATA_BUFFER_SIZE = 0x0fff0;
private final static int POOL_BUFFER_SIZE = 0x1fff0;
/*
private final static int SAME_FRAME_SIZE = 64;
private final static int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
private final static int SAME_FRAME_EXTENDED = 251;
private final static int FULL_FRAME = 255;
private final static int MAX_LOCAL_LENGTH_DIFF = 4;
*/
private final CodeStream _dataBuffer;
private final CodeStream _poolBuffer;
private final CodeStream _signatureBuffer;
private final TypeBuilder<?> _typeBuilder;
ClassWriter(final TypeBuilder<?> typeBuilder) {
_typeBuilder = VerifyArgument.notNull(typeBuilder, "typeBuilder");
_dataBuffer = new CodeStream(DATA_BUFFER_SIZE);
_poolBuffer = new CodeStream(POOL_BUFFER_SIZE);
_signatureBuffer = new CodeStream();
}
public void writeClass(final OutputStream out) throws IOException {
writeCore();
out.write(_poolBuffer.getData(), 0, _poolBuffer.getLength());
}
public void writeClass(final CodeStream out) throws IOException {
writeCore();
out.putByteArray(_poolBuffer.getData(), 0, _poolBuffer.getLength());
}
private void writeCore() {
final TypeBuilder<?> t = _typeBuilder;
assert (t.getModifiers() & Flags.COMPOUND) == 0;
_dataBuffer.reset(DATA_BUFFER_SIZE);
_poolBuffer.reset(POOL_BUFFER_SIZE);
_signatureBuffer.reset();
final Type<?> baseType = t.getBaseType();
final TypeList interfaceTypes = t.getInterfaces();
int flags = t.getModifiers();
if ((flags & PROTECTED) != 0) {
flags |= PUBLIC;
}
flags = flags & ClassFlags & ~STRICTFP;
if ((flags & INTERFACE) == 0) {
flags |= ACC_SUPER;
}
if (t.isNested() && StringUtilities.isNullOrEmpty(t.getName())) {
flags &= ~FINAL;
}
_dataBuffer.putShort(flags);
_dataBuffer.putShort(t.getTypeToken(t));
if (baseType != null && baseType.isClass()) {
_dataBuffer.putShort(t.getTypeToken(baseType));
}
else {
_dataBuffer.putShort(t.getTypeToken(Types.Object));
}
_dataBuffer.putShort(interfaceTypes.size());
for (final Type interfaceType : interfaceTypes) {
_dataBuffer.putShort(t.getTypeToken(interfaceType));
}
final int fieldCount = t.fieldBuilders.size();
final int methodCount = t.methodBuilders.size();
_dataBuffer.putShort(fieldCount);
writeFields();
_dataBuffer.putShort(methodCount);
writeMethods();
final int attributeCountIndex = beginAttributes();
int attributeCount = 0;
boolean signatureRequired = t.isGenericType() ||
baseType != null && baseType.isGenericType();
if (!signatureRequired) {
for (final Type interfaceType : interfaceTypes) {
signatureRequired |= interfaceType.isGenericType();
}
}
if (signatureRequired) {
final int attributeLengthIndex = writeAttribute("Signature");
_dataBuffer.putShort(t.getUtf8StringToken(t.getGenericSignature()));
endAttribute(attributeLengthIndex);
attributeCount++;
}
attributeCount += writeFlagAttributes(flags);
attributeCount += writeJavaAnnotations(t.getCustomAnnotations());
attributeCount += writeEnclosingMethodAttribute(t);
attributeCount += writeInnerTypes();
_poolBuffer.putInt(JAVA_MAGIC);
_poolBuffer.putShort(CompilationTarget.JDK1_5.minorVersion);
_poolBuffer.putShort(CompilationTarget.JDK1_5.majorVersion);
t.constantPool.write(_poolBuffer);
endAttributes(attributeCountIndex, attributeCount);
_poolBuffer.putByteArray(_dataBuffer.getData(), 0, _dataBuffer.getLength());
}
private int writeEnclosingMethodAttribute(final TypeBuilder<?> t) {
final MethodBase method = t.getDeclaringMethod();
if (method == null) {
return 0;
}
final int enclosingMethodStart = writeAttribute("EnclosingMethod");
final Type declaringType = method.getDeclaringType();
_dataBuffer.putShort(declaringType != null ? t.getTypeToken(declaringType) : 0);
_dataBuffer.putShort(t.getMethodToken(method));
endAttribute(enclosingMethodStart);
return 1;
}
private int writeInnerTypes() {
final HashSet<Type<?>> innerTypeSet = _typeBuilder.constantPool.referencedInnerTypes;
if (innerTypeSet.isEmpty()) {
return 0;
}
final Type<?>[] innerTypes = innerTypeSet.toArray(new Type<?>[innerTypeSet.size()]);
final int innerClassesStart = writeAttribute("InnerClasses");
_dataBuffer.putShort(innerTypes.length);
for (final Type<?> type : innerTypes) {
writeInnerTypeInfo(type);
}
endAttribute(innerClassesStart);
return 1;
}
private void writeInnerTypeInfo(final Type<?> type) {
final Type declaringType = type.getDeclaringType();
final MethodBase declaringMethod = type.getDeclaringMethod();
_dataBuffer.putShort(_typeBuilder.getTypeToken(type));
if (declaringType != null) {
_dataBuffer.putShort(_typeBuilder.getTypeToken(declaringType));
}
else if (declaringMethod != null) {
final Type methodDeclaringType = declaringMethod.getDeclaringType();
if (methodDeclaringType != null) {
_dataBuffer.putShort(_typeBuilder.getTypeToken(methodDeclaringType));
}
else {
_dataBuffer.putShort(0);
}
}
final String shortName = type.getShortName();
if (StringUtilities.isNullOrWhitespace(shortName)) {
_dataBuffer.putShort(0);
}
else {
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(shortName));
}
_dataBuffer.putShort(type.getModifiers() & Flags.MemberClassFlags);
}
private void writeFields() {
for (final FieldBuilder field : _typeBuilder.fieldBuilders) {
writeField(field);
}
}
private void writeField(final FieldBuilder field) {
final TypeBuilder t = field.getDeclaringType();
_dataBuffer.putShort(field.getModifiers());
_dataBuffer.putShort(t.getUtf8StringToken(field.getName()));
_dataBuffer.putShort(t.getUtf8StringToken(field.getErasedSignature()));
final Object constantValue = field.getConstantValue();
final int attributeCountPosition = beginAttributes();
int attributeCount = 0;
if (constantValue != null) {
final Type constantType = TypeUtils.getUnderlyingPrimitiveOrSelf(
Type.of(constantValue.getClass())
);
final int attributeLengthIndex = writeAttribute("ConstantValue");
switch (constantType.getKind()) {
case BOOLEAN:
_dataBuffer.putShort(t.getConstantToken((Boolean) constantValue ? 1 : 0));
break;
case BYTE:
case SHORT:
case INT:
_dataBuffer.putShort(t.getConstantToken(((Number) constantValue).intValue()));
break;
case LONG:
_dataBuffer.putShort(t.getConstantToken(((Number) constantValue).longValue()));
break;
case CHAR:
_dataBuffer.putShort(t.getConstantToken((int) ((Character) constantValue).charValue()));
break;
case FLOAT:
_dataBuffer.putShort(t.getConstantToken(((Number) constantValue).floatValue()));
break;
case DOUBLE:
_dataBuffer.putShort(t.getConstantToken(((Number) constantValue).doubleValue()));
break;
default:
throw Error.valueMustBeConstant();
}
endAttribute(attributeLengthIndex);
attributeCount++;
}
attributeCount += writeMemberAttributes(field);
endAttributes(attributeCountPosition, attributeCount);
}
private void writeMethods() {
for (final MethodBuilder method : _typeBuilder.methodBuilders) {
writeMethod(method);
}
}
private void writeMethod(final MethodBuilder method) {
final TypeBuilder<?> t = _typeBuilder;
_dataBuffer.putShort(method.getModifiers());
_dataBuffer.putShort(t.getUtf8StringToken(method.getName()));
_dataBuffer.putShort(t.getUtf8StringToken(method.getErasedSignature()));
int attributeCount = 0;
final int attributeCountIndex = beginAttributes();
final byte[] body = method.getBody();
if (body != null) {
final int attributeLengthIndex = writeAttribute("Code");
writeBody(method);
endAttribute(attributeLengthIndex);
attributeCount++;
}
final TypeList exceptions = method.getThrownTypes();
if (!exceptions.isEmpty()) {
final int attributeLengthIndex = writeAttribute("Exceptions");
_dataBuffer.putShort(exceptions.size());
for (final Type exceptionType : exceptions) {
_dataBuffer.putShort(t.getTypeToken(exceptionType));
}
endAttribute(attributeLengthIndex);
attributeCount++;
}
final Object defaultValue = method.getDefaultValue();
if (defaultValue != null) {
final int attributeLengthIndex = writeAttribute("AnnotationDefault");
writeAttributeType(body);
endAttribute(attributeLengthIndex);
attributeCount++;
}
attributeCount += writeMemberAttributes(method);
attributeCount += writeParameterAttributes(method);
endAttributes(attributeCountIndex, attributeCount);
}
private void writeBody(final MethodBuilder method) {
final CodeGenerator generator = method.getCodeGenerator();
_dataBuffer.putShort(generator.getMaxStackSize());
int maxLocals = generator.localCount;
if (!method.isStatic()) {
maxLocals += 1;
}
final ParameterBuilder[] parameters = method.parameterBuilders;
maxLocals += parameters.length;
for (final ParameterBuilder parameter : parameters) {
final Type<?> parameterType = parameter.getParameterType();
if (parameterType == PrimitiveTypes.Double || parameterType == PrimitiveTypes.Long) {
++maxLocals;
}
}
_dataBuffer.putShort(maxLocals);
_dataBuffer.putInt(generator.offset());
final byte[] body = method.getBody();
_dataBuffer.putByteArray(body, 0, body.length);
final __ExceptionInfo[] exceptionsInfo = generator.getExceptions();
if (exceptionsInfo != null) {
int tryCatchTableEntries = 0;
for (final __ExceptionInfo exception : exceptionsInfo) {
if (exception.getFinallyEndAddress() != -1) {
if (exception.getNumberOfCatches() == 1) {
tryCatchTableEntries += 2;
}
else {
tryCatchTableEntries += (exception.getNumberOfCatches() - 1) * 3;
}
}
else {
tryCatchTableEntries += exception.getNumberOfCatches();
}
}
_dataBuffer.putShort(tryCatchTableEntries);
// TODO: Some of these addresses might be wide; put them in the constants table.
for (final __ExceptionInfo exception : exceptionsInfo) {
final int[] catchAddresses = exception.getCatchAddresses();
final int[] catchEndAddresses = exception.getCatchEndAddresses();
final Type[] catchTypes = exception.getCatchClass();
int finallyIndex = -1;
for (int i = 0, n = exception.getNumberOfCatches(); i < n; i++) {
if (catchTypes[i] == null) {
finallyIndex = i;
break;
}
}
for (int i = 0, n = exception.getNumberOfCatches(); i < n; i++) {
if (catchTypes[i] != null) {
// Catch
_dataBuffer.putShort(exception.getStartAddress());
_dataBuffer.putShort(exception.getEndAddress());
_dataBuffer.putShort(catchAddresses[i]);
_dataBuffer.putShort(_typeBuilder.getTypeToken(catchTypes[i]));
if (finallyIndex != -1) {
_dataBuffer.putShort(exception.getStartAddress());
_dataBuffer.putShort(exception.getEndAddress());
_dataBuffer.putShort(catchAddresses[finallyIndex]);
_dataBuffer.putShort(0);
_dataBuffer.putShort(catchAddresses[i]);
_dataBuffer.putShort(catchEndAddresses[i]);
_dataBuffer.putShort(catchAddresses[finallyIndex]);
_dataBuffer.putShort(0);
}
}
}
if (finallyIndex != -1 && exception.getNumberOfCatches() == 1) {
// No catch blocks; only finally.
_dataBuffer.putShort(exception.getStartAddress());
_dataBuffer.putShort(exception.getEndAddress());
_dataBuffer.putShort(catchAddresses[finallyIndex]);
_dataBuffer.putShort(0);
final int storeOpSize = OpCode.get(body[exception.getFinallyEndAddress()]).getSizeWithOperands();
_dataBuffer.putShort(catchAddresses[finallyIndex]);
_dataBuffer.putShort(exception.getFinallyEndAddress() + storeOpSize);
_dataBuffer.putShort(catchAddresses[finallyIndex]);
_dataBuffer.putShort(0);
}
}
}
else {
_dataBuffer.putShort(0);
}
final int attributeCountIndex = beginAttributes();
int attributeCount = 0;
int genericParameterCount = 0;
final List<LocalInfo> locals = getLocalInfo(method);
final int localCount = locals.size();
if (localCount > 0) {
final int attributeLengthIndex = writeAttribute("LocalVariableTable");
_dataBuffer.putShort(localCount);
for (final LocalInfo local : locals) {
_dataBuffer.putShort(local.start);
_dataBuffer.putShort(local.end - local.start);
final Type<?> localType = local.type;
final Type<?> erasedType = erase(localType);
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(local.name));
if (needsLocalVariableTableEntry(localType)) {
genericParameterCount++;
}
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(erasedType.getSignature()));
_dataBuffer.putShort(local.position);
}
endAttribute(attributeLengthIndex);
attributeCount++;
}
if (genericParameterCount > 0) {
final int attributeLengthIndex = writeAttribute("LocalVariableTypeTable");
_dataBuffer.putShort(genericParameterCount);
int count = 0;
for (final LocalInfo local : locals) {
if (!needsLocalVariableTableEntry(local.type)) {
continue;
}
++count;
_dataBuffer.putShort(local.start);
_dataBuffer.putShort(local.end - local.start);
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(local.name));
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(local.type.getSignature()));
_dataBuffer.putShort(local.position);
}
assert count == genericParameterCount;
endAttribute(attributeLengthIndex);
attributeCount++;
}
// TODO: Stack map?
endAttributes(attributeCountIndex, attributeCount);
}
private static Type<?> erase(final Type<?> t) {
final Type<?> def = t.isGenericType() ? t.getGenericTypeDefinition() : t;
return def.getErasedType();
}
private boolean needsLocalVariableTableEntry(final Type<?> localType) {
return !localType.isEquivalentTo(erase(localType)) &&
!localType.isCompoundType();
}
private List<LocalInfo> getLocalInfo(final MethodBuilder builder) {
final boolean hasThis = !builder.isStatic();
int position = 0;
final List<LocalInfo> localInfo = new ArrayList<>();
if (hasThis) {
final LocalInfo thisInfo = new LocalInfo(
"this",
builder.getDeclaringType(),
position++,
0,
builder.generator.offset()
);
localInfo.add(thisInfo);
}
for (final ParameterBuilder p : builder.parameterBuilders) {
final LocalInfo pInfo = new LocalInfo(
p.getName(),
p.getParameterType(),
position++,
0,
builder.generator.offset()
);
localInfo.add(pInfo);
}
final LocalBuilder[] locals = builder.generator.locals;
if (locals != null) {
for (int i = 0, n = builder.generator.localCount; i < n; i++) {
final LocalBuilder l = locals[i];
if (l == null) {
continue;
}
final LocalInfo lInfo = new LocalInfo(
l.getName(),
l.getLocalType(),
position++,
l.startOffset < 0 ? 0 : l.startOffset,
l.endOffset < 0 ? builder.generator.offset() : l.endOffset
);
localInfo.add(lInfo);
}
}
return localInfo;
}
// <editor-fold defaultstate="collapsed" desc="Attributes">
private int writeMemberAttributes(final MemberInfo member) {
final long flags = member.getModifiers();
final String signature;
final ReadOnlyList<AnnotationBuilder<? extends Annotation>> annotations;
int attributeCount = writeFlagAttributes(member.getModifiers());
switch (member.getMemberType()) {
case Field:
final FieldBuilder field = (FieldBuilder) member;
signature = field.getFieldType().getSignature();
annotations = field.getCustomAnnotations();
break;
case Method:
final MethodBuilder method = (MethodBuilder) member;
signature = method.getSignature();
annotations = method.getCustomAnnotations();
break;
case Constructor:
final ConstructorBuilder constructor = (ConstructorBuilder) member;
signature = constructor.getSignature();
annotations = constructor.getCustomAnnotations();
break;
default:
signature = member.getSignature();
if (member instanceof TypeBuilder<?>) {
annotations = ((TypeBuilder<?>) member).getCustomAnnotations();
}
else {
annotations = ReadOnlyList.emptyList();
}
break;
}
if ((flags & (SYNTHETIC | BRIDGE)) != SYNTHETIC &&
(flags & ANONCONSTR) == 0) {
// A local class with captured variables will get a signature attribute.
final int attributeIndex = writeAttribute("Signature");
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(signature));
endAttribute(attributeIndex);
attributeCount++;
}
attributeCount += writeJavaAnnotations(annotations);
return attributeCount;
}
private int writeParameterAttributes(final MethodBuilder method) {
boolean hasVisible = false;
boolean hasInvisible = false;
final ParameterBuilder[] parameters = method.parameterBuilders;
if (parameters != null) {
for (final ParameterBuilder parameter : parameters) {
for (final AnnotationBuilder<? extends Annotation> a : parameter.getCustomAnnotations()) {
switch (getAnnotationRetention(a)) {
case SOURCE:
break;
case CLASS:
hasInvisible = true;
break;
case RUNTIME:
hasVisible = true;
break;
}
}
}
}
int attributeCount = 0;
if (hasVisible) {
final int attributeIndex = writeAttribute("RuntimeVisibleParameterAnnotations");
_dataBuffer.putByte(parameters.length);
for (final ParameterBuilder p : parameters) {
final ArrayList<AnnotationBuilder<? extends Annotation>> annotations = new ArrayList<>(p.getCustomAnnotations().size());
for (final AnnotationBuilder<? extends Annotation> a : p.getCustomAnnotations()) {
if (getAnnotationRetention(a) == RetentionPolicy.RUNTIME) {
annotations.add(a);
}
}
_dataBuffer.putShort(annotations.size());
for (final AnnotationBuilder<? extends Annotation> a : annotations) {
writeAnnotation(a);
}
}
endAttribute(attributeIndex);
attributeCount++;
}
if (hasInvisible) {
final int attributeIndex = writeAttribute("RuntimeInvisibleParameterAnnotations");
_dataBuffer.putByte(parameters.length);
for (final ParameterBuilder p : parameters) {
final ArrayList<AnnotationBuilder<? extends Annotation>> annotations = new ArrayList<>(p.getCustomAnnotations().size());
for (final AnnotationBuilder<? extends Annotation> a : p.getCustomAnnotations()) {
if (getAnnotationRetention(a) == RetentionPolicy.CLASS) {
annotations.add(a);
}
}
_dataBuffer.putShort(annotations.size());
for (final AnnotationBuilder<? extends Annotation> a : annotations) {
writeAnnotation(a);
}
}
endAttribute(attributeIndex);
attributeCount++;
}
return attributeCount;
}
private RetentionPolicy getAnnotationRetention(final AnnotationBuilder<? extends Annotation> a) {
if (a.getAnnotationType().isAnnotationPresent(Retention.class)) {
return a.getAnnotationType().getAnnotation(Retention.class).value();
}
return RetentionPolicy.CLASS;
}
private int writeJavaAnnotations(final ReadOnlyList<AnnotationBuilder<? extends Annotation>> annotations) {
if (annotations == null || annotations.isEmpty()) {
return 0;
}
final ArrayList<AnnotationBuilder<? extends Annotation>> visible = new ArrayList<>();
final ArrayList<AnnotationBuilder<? extends Annotation>> invisible = new ArrayList<>();
for (final AnnotationBuilder<? extends Annotation> a : annotations) {
switch (getAnnotationRetention(a)) {
case SOURCE:
break;
case CLASS:
invisible.add(a);
break;
case RUNTIME:
visible.add(a);
break;
}
}
int attributeCount = 0;
if (!visible.isEmpty()) {
final int attributeIndex = writeAttribute("RuntimeVisibleAnnotations");
_dataBuffer.putShort(visible.size());
for (final AnnotationBuilder<? extends Annotation> a : visible) {
writeAnnotation(a);
}
endAttribute(attributeIndex);
attributeCount++;
}
if (!invisible.isEmpty()) {
final int attributeIndex = writeAttribute("RuntimeInvisibleAnnotations");
_dataBuffer.putShort(invisible.size());
for (final AnnotationBuilder<? extends Annotation> a : invisible) {
writeAnnotation(a);
}
endAttribute(attributeIndex);
attributeCount++;
}
return attributeCount;
}
private void writeAnnotation(final AnnotationBuilder<? extends Annotation> a) {
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(a.getAnnotationType().getSignature()));
_dataBuffer.putShort(a.getValues().size());
final MethodList attributes = a.getAttributes();
final ReadOnlyList<Object> values = a.getValues();
for (int i = 0, n = attributes.size(); i < n; i++) {
final MethodInfo attribute = attributes.get(i);
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(attribute.getName()));
writeAttributeType(values.get(i));
}
}
private void writeAttributeType(final Object value) {
final Type<?> valueType = TypeUtils.getUnderlyingPrimitiveOrSelf(Type.of(value.getClass()));
switch (valueType.getKind()) {
case BOOLEAN:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken((Boolean) value ? 1 : 0));
break;
case BYTE:
case SHORT:
case INT:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken(((Number) value).intValue()));
break;
case LONG:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken(((Number) value).longValue()));
break;
case CHAR:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken(((Character) value).charValue()));
break;
case FLOAT:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken(((Number) value).floatValue()));
break;
case DOUBLE:
_dataBuffer.putByte(valueType.getErasedSignature().charAt(0));
_dataBuffer.putShort(_typeBuilder.getConstantToken(((Number) value).doubleValue()));
break;
case ARRAY:
final int arrayLength = Array.getLength(value);
_dataBuffer.putByte('[');
_dataBuffer.putShort(arrayLength);
for (int i = 0; i < arrayLength; i++) {
writeAttributeType(Array.get(value, i));
}
break;
case DECLARED:
if (valueType.isEnum()) {
_dataBuffer.putByte('e');
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(valueType.getSignature()));
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(value.toString()));
}
else if (valueType == Types.String) {
_dataBuffer.putByte('s');
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(value.toString()));
}
else if (valueType == Types.Class) {
final Type<?> type = Type.of((Class<?>) value);
_dataBuffer.putByte('c');
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(type.getSignature()));
}
else if (Type.of(Type.class).isAssignableFrom(valueType)) {
_dataBuffer.putByte('c');
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(((Type<?>) value).getSignature()));
}
else {
_dataBuffer.putByte('@');
writeAnnotation((AnnotationBuilder<? extends Annotation>) value);
}
break;
}
}
private int writeAttribute(final String attributeName) {
_dataBuffer.putShort(_typeBuilder.getUtf8StringToken(attributeName));
_dataBuffer.putInt(0);
return _dataBuffer.getLength();
}
private int beginAttributes() {
_dataBuffer.putShort(0);
return _dataBuffer.getLength();
}
private void endAttributes(final int index, final int count) {
putChar(_dataBuffer, index - 2, count);
}
private void endAttribute(final int index) {
putInt(_dataBuffer, index - 4, _dataBuffer.getLength() - index);
}
private int writeFlagAttributes(final long flags) {
int count = 0;
if ((flags & DEPRECATED) != 0) {
endAttribute(writeAttribute("Deprecated"));
count++;
}
if ((flags & ENUM) != 0) {
endAttribute(writeAttribute("Enum"));
count++;
}
if ((flags & SYNTHETIC) != 0) {
endAttribute(writeAttribute("Synthetic"));
count++;
}
if ((flags & BRIDGE) != 0) {
endAttribute(writeAttribute("Bridge"));
count++;
}
if ((flags & VARARGS) != 0) {
endAttribute(writeAttribute("Varargs"));
count++;
}
if ((flags & ANNOTATION) != 0) {
endAttribute(writeAttribute("Annotation"));
count++;
}
return count;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Direct Output">
void putChar(final CodeStream buf, final int op, final int x) {
buf.ensureCapacity(op + 2);
final byte[] data = buf.getData();
data[op] = (byte) ((x >> 8) & 0xFF);
data[op + 1] = (byte) ((x) & 0xFF);
}
void putInt(final CodeStream buf, final int adr, final int x) {
buf.ensureCapacity(adr + 4);
final byte[] data = buf.getData();
data[adr] = (byte) ((x >> 24) & 0xFF);
data[adr + 1] = (byte) ((x >> 16) & 0xFF);
data[adr + 2] = (byte) ((x >> 8) & 0xFF);
data[adr + 3] = (byte) ((x) & 0xFF);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="LocalInfo Class">
@SuppressWarnings("PackageVisibleField")
private final static class LocalInfo {
final String name;
final Type<?> type;
final int position;
final int start;
final int end;
LocalInfo(final String name, final Type<?> type, final int position, final int start, final int end) {
this.name = name;
this.type = type;
this.position = position;
this.start = start;
this.end = end;
}
@Override
public String toString() {
return "LocalInfo{" +
"name='" + name + '\'' +
", type=" + type +
", position=" + position +
", start=" + start +
", end=" + end +
'}';
}
}
// </editor-fold>
}