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
17package com.google.turbine.bytecode;
18
19import com.google.common.io.ByteArrayDataOutput;
20import com.google.common.io.ByteStreams;
21import com.google.turbine.bytecode.Attribute.Annotations;
22import com.google.turbine.bytecode.Attribute.ConstantValue;
23import com.google.turbine.bytecode.Attribute.ExceptionsAttribute;
24import com.google.turbine.bytecode.Attribute.InnerClasses;
25import com.google.turbine.bytecode.Attribute.MethodParameters;
26import com.google.turbine.bytecode.Attribute.Signature;
27import com.google.turbine.bytecode.Attribute.TypeAnnotations;
28import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
29import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
30import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo;
31import com.google.turbine.model.Const;
32import java.util.List;
33
34/** Writer {@link Attribute}s to bytecode. */
35public class AttributeWriter {
36
37  private final ConstantPool pool;
38  private final ByteArrayDataOutput output;
39
40  public AttributeWriter(ConstantPool pool, ByteArrayDataOutput output) {
41    this.pool = pool;
42    this.output = output;
43  }
44
45  /** Writes a single attribute. */
46  public void write(Attribute attribute) {
47    switch (attribute.kind()) {
48      case SIGNATURE:
49        writeSignatureAttribute((Signature) attribute);
50        break;
51      case EXCEPTIONS:
52        writeExceptionsAttribute((ExceptionsAttribute) attribute);
53        break;
54      case INNER_CLASSES:
55        writeInnerClasses((InnerClasses) attribute);
56        break;
57      case CONSTANT_VALUE:
58        writeConstantValue((ConstantValue) attribute);
59        break;
60      case RUNTIME_VISIBLE_ANNOTATIONS:
61      case RUNTIME_INVISIBLE_ANNOTATIONS:
62        writeAnnotation((Attribute.Annotations) attribute);
63        break;
64      case ANNOTATION_DEFAULT:
65        writeAnnotationDefault((Attribute.AnnotationDefault) attribute);
66        break;
67      case RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
68      case RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
69        writeParameterAnnotations((Attribute.ParameterAnnotations) attribute);
70        break;
71      case DEPRECATED:
72        writeDeprecated(attribute);
73        break;
74      case RUNTIME_INVISIBLE_TYPE_ANNOTATIONS:
75      case RUNTIME_VISIBLE_TYPE_ANNOTATIONS:
76        writeTypeAnnotation((Attribute.TypeAnnotations) attribute);
77        break;
78      case METHOD_PARAMETERS:
79        writeMethodParameters((Attribute.MethodParameters) attribute);
80        break;
81      default:
82        throw new AssertionError(attribute.kind());
83    }
84  }
85
86  private void writeInnerClasses(InnerClasses attribute) {
87    output.writeShort(pool.utf8(attribute.kind().signature()));
88    output.writeInt(attribute.inners.size() * 8 + 2);
89    output.writeShort(attribute.inners.size());
90    for (ClassFile.InnerClass inner : attribute.inners) {
91      output.writeShort(pool.classInfo(inner.innerClass()));
92      output.writeShort(pool.classInfo(inner.outerClass()));
93      output.writeShort(pool.utf8(inner.innerName()));
94      output.writeShort(inner.access());
95    }
96  }
97
98  private void writeExceptionsAttribute(ExceptionsAttribute attribute) {
99    output.writeShort(pool.utf8(attribute.kind().signature()));
100    output.writeInt(2 + attribute.exceptions.size() * 2);
101    output.writeShort(attribute.exceptions.size());
102    for (String exception : attribute.exceptions) {
103      output.writeShort(pool.classInfo(exception));
104    }
105  }
106
107  private void writeSignatureAttribute(Signature attribute) {
108    output.writeShort(pool.utf8(attribute.kind().signature()));
109    output.writeInt(2);
110    output.writeShort(pool.utf8(attribute.signature));
111  }
112
113  public void writeConstantValue(ConstantValue attribute) {
114    output.writeShort(pool.utf8(attribute.kind().signature()));
115    output.writeInt(2);
116    Const.Value value = attribute.value;
117    switch (value.constantTypeKind()) {
118      case INT:
119      case CHAR:
120      case SHORT:
121      case BYTE:
122        output.writeShort(pool.integer(value.asInteger().value()));
123        break;
124      case LONG:
125        output.writeShort(pool.longInfo(value.asLong().value()));
126        break;
127      case DOUBLE:
128        output.writeShort(pool.doubleInfo(value.asDouble().value()));
129        break;
130      case FLOAT:
131        output.writeShort(pool.floatInfo(value.asFloat().value()));
132        break;
133      case BOOLEAN:
134        output.writeShort(pool.integer(value.asBoolean().value() ? 1 : 0));
135        break;
136      case STRING:
137        output.writeShort(pool.string(value.asString().value()));
138        break;
139      default:
140        throw new AssertionError(value.constantTypeKind());
141    }
142  }
143
144  public void writeAnnotation(Annotations attribute) {
145    output.writeShort(pool.utf8(attribute.kind().signature()));
146    ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
147    tmp.writeShort(attribute.annotations().size());
148    for (AnnotationInfo annotation : attribute.annotations()) {
149      new AnnotationWriter(pool, tmp).writeAnnotation(annotation);
150    }
151    byte[] data = tmp.toByteArray();
152    output.writeInt(data.length);
153    output.write(data);
154  }
155
156  public void writeAnnotationDefault(Attribute.AnnotationDefault attribute) {
157    output.writeShort(pool.utf8(attribute.kind().signature()));
158    ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
159    new AnnotationWriter(pool, tmp).writeElementValue(attribute.value());
160    byte[] data = tmp.toByteArray();
161    output.writeInt(data.length);
162    output.write(data);
163  }
164
165  public void writeParameterAnnotations(Attribute.ParameterAnnotations attribute) {
166    output.writeShort(pool.utf8(attribute.kind().signature()));
167    ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
168    tmp.writeByte(attribute.annotations().size());
169    for (List<AnnotationInfo> parameterAnnotations : attribute.annotations()) {
170      tmp.writeShort(parameterAnnotations.size());
171      for (AnnotationInfo annotation : parameterAnnotations) {
172        new AnnotationWriter(pool, tmp).writeAnnotation(annotation);
173      }
174    }
175    byte[] data = tmp.toByteArray();
176    output.writeInt(data.length);
177    output.write(data);
178  }
179
180  private void writeDeprecated(Attribute attribute) {
181    output.writeShort(pool.utf8(attribute.kind().signature()));
182    output.writeInt(0);
183  }
184
185  private void writeTypeAnnotation(TypeAnnotations attribute) {
186    output.writeShort(pool.utf8(attribute.kind().signature()));
187    ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
188    tmp.writeShort(attribute.annotations().size());
189    for (TypeAnnotationInfo annotation : attribute.annotations()) {
190      new AnnotationWriter(pool, tmp).writeTypeAnnotation(annotation);
191    }
192    byte[] data = tmp.toByteArray();
193    output.writeInt(data.length);
194    output.write(data);
195  }
196
197  private void writeMethodParameters(MethodParameters attribute) {
198    output.writeShort(pool.utf8(attribute.kind().signature()));
199    output.writeInt(attribute.parameters().size() * 4 + 1);
200    output.writeByte(attribute.parameters().size());
201    for (ParameterInfo parameter : attribute.parameters()) {
202      output.writeShort(parameter.name() != null ? pool.utf8(parameter.name()) : 0);
203      output.writeShort(parameter.access());
204    }
205  }
206}
207