aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/turbine/bytecode/ClassWriter.java
blob: de975f2296e2e17a67e8e4d6f4f0156e3443daf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.turbine.bytecode;

import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.turbine.model.Const.DoubleValue;
import com.google.turbine.model.Const.FloatValue;
import com.google.turbine.model.Const.IntValue;
import com.google.turbine.model.Const.LongValue;
import com.google.turbine.model.Const.StringValue;
import com.google.turbine.model.Const.Value;
import java.util.List;

/** Class file writing. */
public final class ClassWriter {

  private static final int MAGIC = 0xcafebabe;
  private static final int MINOR_VERSION = 0;
  // use the lowest classfile version possible given the class file features
  // TODO(cushon): is there a reason to support --release?
  private static final int MAJOR_VERSION = 52;
  private static final int MODULE_MAJOR_VERSION = 53;

  /** Writes a {@link ClassFile} to bytecode. */
  public static byte[] writeClass(ClassFile classfile) {
    ConstantPool pool = new ConstantPool();
    ByteArrayDataOutput output = ByteStreams.newDataOutput();
    output.writeShort(classfile.access());
    output.writeShort(pool.classInfo(classfile.name()));
    output.writeShort(classfile.superName() != null ? pool.classInfo(classfile.superName()) : 0);
    output.writeShort(classfile.interfaces().size());
    for (String i : classfile.interfaces()) {
      output.writeShort(pool.classInfo(i));
    }
    output.writeShort(classfile.fields().size());
    for (ClassFile.FieldInfo f : classfile.fields()) {
      writeField(pool, output, f);
    }
    output.writeShort(classfile.methods().size());
    for (ClassFile.MethodInfo m : classfile.methods()) {
      writeMethod(pool, output, m);
    }
    writeAttributes(pool, output, LowerAttributes.classAttributes(classfile));
    return finishClass(pool, output, classfile);
  }

  private static void writeMethod(
      ConstantPool pool, ByteArrayDataOutput output, ClassFile.MethodInfo method) {
    output.writeShort(method.access());
    output.writeShort(pool.utf8(method.name()));
    output.writeShort(pool.utf8(method.descriptor()));
    writeAttributes(pool, output, LowerAttributes.methodAttributes(method));
  }

  private static void writeField(
      ConstantPool pool, ByteArrayDataOutput output, ClassFile.FieldInfo field) {
    output.writeShort(field.access());
    output.writeShort(pool.utf8(field.name()));
    output.writeShort(pool.utf8(field.descriptor()));
    writeAttributes(pool, output, LowerAttributes.fieldAttributes(field));
  }

  private static void writeAttributes(
      ConstantPool pool, ByteArrayDataOutput body, List<Attribute> attributes) {
    body.writeShort(attributes.size());
    for (Attribute attribute : attributes) {
      new AttributeWriter(pool, body).write(attribute);
    }
  }

  static void writeConstantPool(ConstantPool constantPool, ByteArrayDataOutput output) {
    output.writeShort(constantPool.nextEntry);
    for (ConstantPool.Entry e : constantPool.constants()) {
      output.writeByte(e.kind().tag());
      Value value = e.value();
      switch (e.kind()) {
        case CLASS_INFO:
        case STRING:
        case MODULE:
        case PACKAGE:
          output.writeShort(((IntValue) value).value());
          break;
        case INTEGER:
          output.writeInt(((IntValue) value).value());
          break;
        case DOUBLE:
          output.writeDouble(((DoubleValue) value).value());
          break;
        case FLOAT:
          output.writeFloat(((FloatValue) value).value());
          break;
        case LONG:
          output.writeLong(((LongValue) value).value());
          break;
        case UTF8:
          output.writeUTF(((StringValue) value).value());
          break;
      }
    }
  }

  private static byte[] finishClass(
      ConstantPool pool, ByteArrayDataOutput body, ClassFile classfile) {
    ByteArrayDataOutput result = ByteStreams.newDataOutput();
    result.writeInt(MAGIC);
    result.writeShort(MINOR_VERSION);
    result.writeShort(classfile.module() != null ? MODULE_MAJOR_VERSION : MAJOR_VERSION);
    writeConstantPool(pool, result);
    result.write(body.toByteArray());
    return result.toByteArray();
  }

  private ClassWriter() {}
}