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.collect.ImmutableList; 20 import com.google.turbine.model.Const; 21 import com.google.turbine.model.Const.IntValue; 22 import com.google.turbine.model.Const.StringValue; 23 import com.google.turbine.model.Const.Value; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.Objects; 29 30 /** A constant pool builder, used when writing class files. */ 31 public class ConstantPool { 32 33 /** The next available constant pool entry. */ 34 int nextEntry = 1; 35 36 private final Map<String, Integer> utf8Pool = new HashMap<>(); 37 private final Map<Integer, Integer> classInfoPool = new HashMap<>(); 38 private final Map<Integer, Integer> stringPool = new HashMap<>(); 39 private final Map<Integer, Integer> integerPool = new HashMap<>(); 40 private final Map<Double, Integer> doublePool = new HashMap<>(); 41 private final Map<Float, Integer> floatPool = new HashMap<>(); 42 private final Map<Long, Integer> longPool = new HashMap<>(); 43 44 private final List<Entry> constants = new ArrayList<>(); 45 46 /** The ordered list of constant pool entries. */ constants()47 public ImmutableList<Entry> constants() { 48 return ImmutableList.copyOf(constants); 49 } 50 51 /** The number of constant pool entries the given kind takes up. */ width(Kind kind)52 private static short width(Kind kind) { 53 switch (kind) { 54 case CLASS_INFO: 55 case STRING: 56 case INTEGER: 57 case UTF8: 58 case FLOAT: 59 return 1; 60 case LONG: 61 case DOUBLE: 62 // "In retrospect, making 8-byte constants take two constant pool entries 63 // was a poor choice." -- JVMS 4.4.5 64 return 2; 65 default: 66 throw new AssertionError(kind); 67 } 68 } 69 70 /** A constant pool entry. */ 71 static class Entry { 72 private final Kind kind; 73 private final Value value; 74 Entry(Kind kind, Value value)75 Entry(Kind kind, Value value) { 76 this.kind = kind; 77 this.value = value; 78 } 79 80 /** The entry kind. */ kind()81 public Kind kind() { 82 return kind; 83 } 84 85 /** The entry's value. */ value()86 public Value value() { 87 return value; 88 } 89 } 90 91 /** Adds a CONSTANT_Class_info entry to the pool. */ classInfo(String value)92 int classInfo(String value) { 93 Objects.requireNonNull(value); 94 int utf8 = utf8(value); 95 if (classInfoPool.containsKey(utf8)) { 96 return classInfoPool.get(utf8); 97 } 98 int index = insert(new Entry(Kind.CLASS_INFO, new IntValue(utf8))); 99 classInfoPool.put(utf8, index); 100 return index; 101 } 102 103 /** Adds a CONSTANT_Utf8_info entry to the pool. */ utf8(String value)104 int utf8(String value) { 105 Objects.requireNonNull(value); 106 if (utf8Pool.containsKey(value)) { 107 return utf8Pool.get(value); 108 } 109 int index = insert(new Entry(Kind.UTF8, new StringValue(value))); 110 utf8Pool.put(value, index); 111 return index; 112 } 113 integer(int value)114 int integer(int value) { 115 if (integerPool.containsKey(value)) { 116 return integerPool.get(value); 117 } 118 int index = insert(new Entry(Kind.INTEGER, new Const.IntValue(value))); 119 integerPool.put(value, index); 120 return index; 121 } 122 longInfo(long value)123 int longInfo(long value) { 124 if (longPool.containsKey(value)) { 125 return longPool.get(value); 126 } 127 int index = insert(new Entry(Kind.LONG, new Const.LongValue(value))); 128 longPool.put(value, index); 129 return index; 130 } 131 doubleInfo(double value)132 int doubleInfo(double value) { 133 if (doublePool.containsKey(value)) { 134 return doublePool.get(value); 135 } 136 int index = insert(new Entry(Kind.DOUBLE, new Const.DoubleValue(value))); 137 doublePool.put(value, index); 138 return index; 139 } 140 floatInfo(float value)141 int floatInfo(float value) { 142 if (floatPool.containsKey(value)) { 143 return floatPool.get(value); 144 } 145 int index = insert(new Entry(Kind.FLOAT, new Const.FloatValue(value))); 146 floatPool.put(value, index); 147 return index; 148 } 149 string(String value)150 int string(String value) { 151 Objects.requireNonNull(value); 152 int utf8 = utf8(value); 153 if (stringPool.containsKey(utf8)) { 154 return stringPool.get(utf8); 155 } 156 int index = insert(new Entry(Kind.STRING, new IntValue(utf8))); 157 stringPool.put(utf8, index); 158 return index; 159 } 160 insert(Entry key)161 private int insert(Entry key) { 162 int entry = nextEntry; 163 constants.add(key); 164 nextEntry += width(key.kind()); 165 if ((nextEntry & 0xffff) != nextEntry) { 166 throw new AssertionError("constant pool has more than 2^16 entries"); 167 } 168 return entry; 169 } 170 171 /** Constant pool entry kinds. */ 172 enum Kind { 173 CLASS_INFO(7), 174 STRING(8), 175 INTEGER(3), 176 DOUBLE(6), 177 FLOAT(4), 178 LONG(5), 179 UTF8(1); 180 181 private final short tag; 182 Kind(int tag)183 Kind(int tag) { 184 this.tag = (short) tag; 185 } 186 187 /** The JVMS Table 4.4-A tag. */ tag()188 public short tag() { 189 return tag; 190 } 191 } 192 } 193