1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.bytecode;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.io.ByteArrayDataOutput;
21 import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
22 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
23 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
24 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
25 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue;
26 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
27 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
28 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo;
29 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.FormalParameterTarget;
30 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.SuperTypeTarget;
31 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.Target;
32 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.ThrowsTarget;
33 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterBoundTarget;
34 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterTarget;
35 import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypePath;
36 import com.google.turbine.model.Const.Value;
37 import java.util.Map.Entry;
38 
39 /** Writes an {@link AnnotationInfo} to a class file. */
40 public class AnnotationWriter {
41 
42   final ConstantPool pool;
43   final ByteArrayDataOutput output;
44 
AnnotationWriter(ConstantPool pool, ByteArrayDataOutput output)45   public AnnotationWriter(ConstantPool pool, ByteArrayDataOutput output) {
46     this.pool = pool;
47     this.output = output;
48   }
49 
writeAnnotation(AnnotationInfo annotation)50   public void writeAnnotation(AnnotationInfo annotation) {
51     output.writeShort(pool.utf8(annotation.typeName()));
52     output.writeShort(annotation.elementValuePairs().size());
53     for (Entry<String, ElementValue> entry : annotation.elementValuePairs().entrySet()) {
54       output.writeShort(pool.utf8(entry.getKey()));
55       writeElementValue(entry.getValue());
56     }
57   }
58 
writeElementValue(ElementValue value)59   void writeElementValue(ElementValue value) {
60     switch (value.kind()) {
61       case CONST:
62         writeConstElementValue(((ConstValue) value).value());
63         break;
64       case ENUM:
65         writeEnumElementValue((EnumConstValue) value);
66         break;
67       case CLASS:
68         writeClassElementValue((ConstClassValue) value);
69         break;
70       case ARRAY:
71         writeArrayElementValue((ArrayValue) value);
72         break;
73       case ANNOTATION:
74         writeAnnotationElementValue((AnnotationValue) value);
75         break;
76       default:
77         throw new AssertionError(value.kind());
78     }
79   }
80 
writeConstElementValue(Value value)81   private void writeConstElementValue(Value value) {
82     switch (value.constantTypeKind()) {
83       case BYTE:
84         writeConst('B', pool.integer(value.asInteger().value()));
85         break;
86       case CHAR:
87         writeConst('C', pool.integer(value.asInteger().value()));
88         break;
89       case SHORT:
90         writeConst('S', pool.integer(value.asInteger().value()));
91         break;
92       case DOUBLE:
93         writeConst('D', pool.doubleInfo(value.asDouble().value()));
94         break;
95       case FLOAT:
96         writeConst('F', pool.floatInfo(value.asFloat().value()));
97         break;
98       case INT:
99         writeConst('I', pool.integer(value.asInteger().value()));
100         break;
101       case LONG:
102         writeConst('J', pool.longInfo(value.asLong().value()));
103         break;
104       case STRING:
105         writeConst('s', pool.utf8(value.asString().value()));
106         break;
107       case BOOLEAN:
108         writeConst('Z', pool.integer(value.asBoolean().value() ? 1 : 0));
109         break;
110       default:
111         throw new AssertionError(value.constantTypeKind());
112     }
113   }
114 
writeConst(char tag, int index)115   private void writeConst(char tag, int index) {
116     output.writeByte(tag);
117     output.writeShort(index);
118   }
119 
writeEnumElementValue(EnumConstValue value)120   private void writeEnumElementValue(EnumConstValue value) {
121     output.writeByte('e');
122     output.writeShort(pool.utf8(value.typeName()));
123     output.writeShort(pool.utf8(value.constName()));
124   }
125 
writeClassElementValue(ConstClassValue value)126   private void writeClassElementValue(ConstClassValue value) {
127     output.writeByte('c');
128     output.writeShort(pool.utf8(value.className()));
129   }
130 
writeArrayElementValue(ArrayValue value)131   private void writeArrayElementValue(ArrayValue value) {
132     output.writeByte('[');
133     output.writeShort(value.elements().size());
134     for (ElementValue elementValue : value.elements()) {
135       writeElementValue(elementValue);
136     }
137   }
138 
writeAnnotationElementValue(AnnotationValue value)139   private void writeAnnotationElementValue(AnnotationValue value) {
140     output.writeByte('@');
141     writeAnnotation(value.annotation());
142   }
143 
writeTypeAnnotation(TypeAnnotationInfo annotation)144   public void writeTypeAnnotation(TypeAnnotationInfo annotation) {
145     output.writeByte(annotation.targetType().tag());
146     writeTypeAnnotationTarget(annotation.target());
147     writePath(annotation.path());
148     writeAnnotation(annotation.anno());
149   }
150 
writePath(TypePath path)151   private void writePath(TypePath path) {
152     ImmutableList<TypePath> flat = path.flatten();
153     output.writeByte(flat.size());
154     for (TypePath curr : flat) {
155       output.writeByte(curr.tag());
156       output.writeByte(curr.typeArgumentIndex());
157     }
158   }
159 
writeTypeAnnotationTarget(Target target)160   private void writeTypeAnnotationTarget(Target target) {
161     switch (target.kind()) {
162       case EMPTY:
163         break;
164       case TYPE_PARAMETER:
165         output.writeByte(((TypeParameterTarget) target).index());
166         break;
167       case FORMAL_PARAMETER:
168         output.writeByte(((FormalParameterTarget) target).index());
169         break;
170       case THROWS:
171         output.writeShort(((ThrowsTarget) target).index());
172         break;
173       case SUPERTYPE:
174         output.writeShort(((SuperTypeTarget) target).index());
175         break;
176       case TYPE_PARAMETER_BOUND:
177         TypeParameterBoundTarget typeParameterBoundTarget = (TypeParameterBoundTarget) target;
178         output.writeByte(typeParameterBoundTarget.typeParameterIndex());
179         output.writeByte(typeParameterBoundTarget.boundIndex());
180         break;
181       default:
182         throw new AssertionError(target.kind());
183     }
184   }
185 }
186