Home | History | Annotate | Download | only in bytecode
      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 
     41   /** A table that maps constant pool entries to byte offsets in {@link #byteReader}. */
     42   private final int[] constantPool;
     43 
     44   /** The constant pool data. */
     45   private final ByteReader byteReader;
     46 
     47   private ConstantPoolReader(int[] constantPool, ByteReader byteReader) {
     48     this.constantPool = constantPool;
     49     this.byteReader = byteReader;
     50   }
     51 
     52   /**
     53    * Skips over all constant pool entries, saving the byte offset of each index so it can be read
     54    * later if it's needed.
     55    */
     56   public static ConstantPoolReader readConstantPool(ByteReader reader) {
     57     int constantPoolCount = reader.u2();
     58     int[] constantPool = new int[constantPoolCount - 1];
     59     for (int i = 0; i < constantPoolCount - 1; ) {
     60       constantPool[i] = reader.pos();
     61       i += skipConstantPool(reader);
     62     }
     63     return new ConstantPoolReader(constantPool, reader);
     64   }
     65 
     66   /** Skips over the data for a single constant pool entry and returns the size of the entry. */
     67   private static int skipConstantPool(ByteReader reader) {
     68     int tag = reader.u1();
     69     switch (tag) {
     70       case CONSTANT_CLASS:
     71       case CONSTANT_METHOD_TYPE:
     72       case CONSTANT_STRING:
     73         reader.skip(2);
     74         return 1;
     75       case CONSTANT_DOUBLE:
     76       case CONSTANT_LONG:
     77         reader.skip(8);
     78         // "In retrospect, making 8-byte constants take two constant pool entries
     79         // was a poor choice." -- JVMS 4.4.5
     80         return 2;
     81       case CONSTANT_FIELDREF:
     82       case CONSTANT_METHODREF:
     83       case CONSTANT_INTERFACE_METHODREF:
     84       case CONSTANT_INTEGER:
     85       case CONSTANT_FLOAT:
     86       case CONSTANT_NAME_AND_TYPE:
     87       case CONSTANT_INVOKE_DYNAMIC:
     88         reader.skip(4);
     89         return 1;
     90       case CONSTANT_UTF8:
     91         reader.skip(reader.u2());
     92         return 1;
     93       case CONSTANT_METHOD_HANDLE:
     94         reader.skip(3);
     95         return 1;
     96       default:
     97         throw new AssertionError(String.format("bad constant pool tag: 0x%x", tag));
     98     }
     99   }
    100 
    101   /** Reads the CONSTANT_Class_info at the given index. */
    102   public String classInfo(int index) {
    103     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
    104     byte tag = reader.readByte();
    105     if (tag != CONSTANT_CLASS) {
    106       throw new AssertionError(String.format("bad tag: %x", tag));
    107     }
    108     int nameIndex = reader.readUnsignedShort();
    109     return utf8(nameIndex);
    110   }
    111 
    112   /** Reads the CONSTANT_Utf8_info at the given index. */
    113   public String utf8(int index) {
    114     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
    115     byte tag = reader.readByte();
    116     if (tag != CONSTANT_UTF8) {
    117       throw new AssertionError(String.format("bad tag: %x", tag));
    118     }
    119     return reader.readUTF();
    120   }
    121 
    122   /**
    123    * Reads a constant value at the given index, which must be one of CONSTANT_String_info,
    124    * CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info, or CONSTANT_Double_info.
    125    */
    126   Const.Value constant(int index) {
    127     ByteArrayDataInput reader = byteReader.seek(constantPool[index - 1]);
    128     byte tag = reader.readByte();
    129     switch (tag) {
    130       case CONSTANT_LONG:
    131         return new Const.LongValue(reader.readLong());
    132       case CONSTANT_FLOAT:
    133         return new Const.FloatValue(reader.readFloat());
    134       case CONSTANT_DOUBLE:
    135         return new Const.DoubleValue(reader.readDouble());
    136       case CONSTANT_INTEGER:
    137         return new Const.IntValue(reader.readInt());
    138       case CONSTANT_STRING:
    139         return new Const.StringValue(utf8(reader.readUnsignedShort()));
    140       default:
    141         throw new AssertionError(String.format("bad tag: %x", tag));
    142     }
    143   }
    144 }
    145