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.io.ByteArrayDataInput;
20 import com.google.turbine.model.Const;
21 
22 /** A JVMS §4.4 constant pool reader. */
23 public class ConstantPoolReader {
24 
25   // JVMS table 4.3
26   static final int CONSTANT_CLASS = 7;
27   static final int CONSTANT_FIELDREF = 9;
28   static final int CONSTANT_METHODREF = 10;
29   static final int CONSTANT_INTERFACE_METHODREF = 11;
30   static final int CONSTANT_STRING = 8;
31   static final int CONSTANT_INTEGER = 3;
32   static final int CONSTANT_FLOAT = 4;
33   static final int CONSTANT_LONG = 5;
34   static final int CONSTANT_DOUBLE = 6;
35   static final int CONSTANT_NAME_AND_TYPE = 12;
36   static final int CONSTANT_UTF8 = 1;
37   static final int CONSTANT_METHOD_HANDLE = 15;
38   static final int CONSTANT_METHOD_TYPE = 16;
39   static final int CONSTANT_INVOKE_DYNAMIC = 18;
40   static final int CONSTANT_MODULE = 19;
41   static final int CONSTANT_PACKAGE = 20;
42 
43   /** A table that maps constant pool entries to byte offsets in {@link #byteReader}. */
44   private final int[] constantPool;
45 
46   /** The constant pool data. */
47   private final ByteReader byteReader;
48 
ConstantPoolReader(int[] constantPool, ByteReader byteReader)49   private ConstantPoolReader(int[] constantPool, ByteReader byteReader) {
50     this.constantPool = constantPool;
51     this.byteReader = byteReader;
52   }
53 
54   /**
55    * Skips over all constant pool entries, saving the byte offset of each index so it can be read
56    * later if it's needed.
57    */
readConstantPool(ByteReader reader)58   public static ConstantPoolReader readConstantPool(ByteReader reader) {
59     int constantPoolCount = reader.u2();
60     int[] constantPool = new int[constantPoolCount - 1];
61     for (int i = 0; i < constantPoolCount - 1; ) {
62       constantPool[i] = reader.pos();
63       i += skipConstantPool(reader);
64     }
65     return new ConstantPoolReader(constantPool, reader);
66   }
67 
68   /** Skips over the data for a single constant pool entry and returns the size of the entry. */
skipConstantPool(ByteReader reader)69   private static int skipConstantPool(ByteReader reader) {
70     int tag = reader.u1();
71     switch (tag) {
72       case CONSTANT_CLASS:
73       case CONSTANT_METHOD_TYPE:
74       case CONSTANT_STRING:
75       case CONSTANT_MODULE:
76       case CONSTANT_PACKAGE:
77         reader.skip(2);
78         return 1;
79       case CONSTANT_DOUBLE:
80       case CONSTANT_LONG:
81         reader.skip(8);
82         // "In retrospect, making 8-byte constants take two constant pool entries
83         // was a poor choice." -- JVMS 4.4.5
84         return 2;
85       case CONSTANT_FIELDREF:
86       case CONSTANT_METHODREF:
87       case CONSTANT_INTERFACE_METHODREF:
88       case CONSTANT_INTEGER:
89       case CONSTANT_FLOAT:
90       case CONSTANT_NAME_AND_TYPE:
91       case CONSTANT_INVOKE_DYNAMIC:
92         reader.skip(4);
93         return 1;
94       case CONSTANT_UTF8:
95         reader.skip(reader.u2());
96         return 1;
97       case CONSTANT_METHOD_HANDLE:
98         reader.skip(3);
99         return 1;
100       default:
101         throw new AssertionError(String.format("bad constant pool tag: 0x%x", tag));
102     }
103   }
104 
105   /** Reads the CONSTANT_Class_info at the given index. */
classInfo(int index)106   public String classInfo(int index) {
107     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
108     byte tag = reader.readByte();
109     if (tag != CONSTANT_CLASS) {
110       throw new AssertionError(String.format("bad tag: %x", tag));
111     }
112     int nameIndex = reader.readUnsignedShort();
113     return utf8(nameIndex);
114   }
115 
116   /** Reads the CONSTANT_Utf8_info at the given index. */
utf8(int index)117   public String utf8(int index) {
118     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
119     byte tag = reader.readByte();
120     if (tag != CONSTANT_UTF8) {
121       throw new AssertionError(String.format("bad tag: %x", tag));
122     }
123     return reader.readUTF();
124   }
125 
126   /** Reads the CONSTANT_Module_info at the given index. */
moduleInfo(int index)127   public String moduleInfo(int index) {
128     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
129     byte tag = reader.readByte();
130     if (tag != CONSTANT_MODULE) {
131       throw new AssertionError(String.format("bad tag: %x", tag));
132     }
133     int nameIndex = reader.readUnsignedShort();
134     return utf8(nameIndex);
135   }
136 
137   /** Reads the CONSTANT_Package_info at the given index. */
packageInfo(int index)138   public String packageInfo(int index) {
139     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
140     byte tag = reader.readByte();
141     if (tag != CONSTANT_PACKAGE) {
142       throw new AssertionError(String.format("bad tag: %x", tag));
143     }
144     int nameIndex = reader.readUnsignedShort();
145     return utf8(nameIndex);
146   }
147 
148   /**
149    * Reads a constant value at the given index, which must be one of CONSTANT_String_info,
150    * CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info, or CONSTANT_Double_info.
151    */
constant(int index)152   Const.Value constant(int index) {
153     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
154     byte tag = reader.readByte();
155     switch (tag) {
156       case CONSTANT_LONG:
157         return new Const.LongValue(reader.readLong());
158       case CONSTANT_FLOAT:
159         return new Const.FloatValue(reader.readFloat());
160       case CONSTANT_DOUBLE:
161         return new Const.DoubleValue(reader.readDouble());
162       case CONSTANT_INTEGER:
163         return new Const.IntValue(reader.readInt());
164       case CONSTANT_STRING:
165         return new Const.StringValue(utf8(reader.readUnsignedShort()));
166       case CONSTANT_UTF8:
167         return new Const.StringValue(reader.readUTF());
168       default:
169         throw new AssertionError(String.format("bad tag: %x", tag));
170     }
171   }
172 }
173