ClassWriter.java revision bd2b29bb7fd05aed8481ef8342c09c4e88492c88
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.model.Const.DoubleValue;
22import com.google.turbine.model.Const.FloatValue;
23import com.google.turbine.model.Const.IntValue;
24import com.google.turbine.model.Const.LongValue;
25import com.google.turbine.model.Const.ShortValue;
26import com.google.turbine.model.Const.StringValue;
27import com.google.turbine.model.Const.Value;
28import java.util.List;
29
30/** Class file writing. */
31public class ClassWriter {
32
33  private static final int MAGIC = 0xcafebabe;
34  private static final int MINOR_VERSION = 0;
35  // TODO(cushon): configuration?
36  private static final int MAJOR_VERSION = 52;
37
38  /** Writes a {@link ClassFile} to bytecode. */
39  public static byte[] writeClass(ClassFile classfile) {
40    ConstantPool pool = new ConstantPool();
41    ByteArrayDataOutput output = ByteStreams.newDataOutput();
42    output.writeShort(classfile.access());
43    output.writeShort(pool.classInfo(classfile.name()));
44    output.writeShort(pool.classInfo(classfile.superName()));
45    output.writeShort(classfile.interfaces().size());
46    for (String i : classfile.interfaces()) {
47      output.writeShort(pool.classInfo(i));
48    }
49    output.writeShort(classfile.fields().size());
50    for (ClassFile.FieldInfo f : classfile.fields()) {
51      writeField(pool, output, f);
52    }
53    output.writeShort(classfile.methods().size());
54    for (ClassFile.MethodInfo m : classfile.methods()) {
55      writeMethod(pool, output, m);
56    }
57    writeAttributes(pool, output, LowerAttributes.classAttributes(classfile));
58    return finishClass(pool, output);
59  }
60
61  private static void writeMethod(
62      ConstantPool pool, ByteArrayDataOutput output, ClassFile.MethodInfo method) {
63    output.writeShort(method.access());
64    output.writeShort(pool.utf8(method.name()));
65    output.writeShort(pool.utf8(method.descriptor()));
66    writeAttributes(pool, output, LowerAttributes.methodAttributes(method));
67  }
68
69  private static void writeField(
70      ConstantPool pool, ByteArrayDataOutput output, ClassFile.FieldInfo field) {
71    output.writeShort(field.access());
72    output.writeShort(pool.utf8(field.name()));
73    output.writeShort(pool.utf8(field.descriptor()));
74    writeAttributes(pool, output, LowerAttributes.fieldAttributes(field));
75  }
76
77  private static void writeAttributes(
78      ConstantPool pool, ByteArrayDataOutput body, List<Attribute> attributes) {
79    body.writeShort(attributes.size());
80    for (Attribute attribute : attributes) {
81      new AttributeWriter(pool, body).write(attribute);
82    }
83  }
84
85  static void writeConstantPool(ConstantPool constantPool, ByteArrayDataOutput output) {
86    output.writeShort(constantPool.nextEntry);
87    for (ConstantPool.Entry e : constantPool.constants()) {
88      output.writeByte(e.kind().tag());
89      Value value = e.value();
90      switch (value.constantTypeKind()) {
91        case STRING:
92          output.writeUTF(((StringValue) value).value());
93          break;
94        case SHORT:
95          output.writeShort(((ShortValue) value).value());
96          break;
97        case INT:
98          output.writeInt(((IntValue) value).value());
99          break;
100        case LONG:
101          output.writeLong(((LongValue) value).value());
102          break;
103        case FLOAT:
104          output.writeFloat(((FloatValue) value).value());
105          break;
106        case DOUBLE:
107          output.writeDouble(((DoubleValue) value).value());
108          break;
109        default:
110          throw new AssertionError(value.constantTypeKind());
111      }
112    }
113  }
114
115  private static byte[] finishClass(ConstantPool pool, ByteArrayDataOutput body) {
116    ByteArrayDataOutput result = ByteStreams.newDataOutput();
117    result.writeInt(MAGIC);
118    result.writeShort(MINOR_VERSION);
119    result.writeShort(MAJOR_VERSION);
120    writeConstantPool(pool, result);
121    result.write(body.toByteArray());
122    return result.toByteArray();
123  }
124}
125