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 private final Map<Integer, Integer> modulePool = new HashMap<>(); 44 private final Map<Integer, Integer> packagePool = new HashMap<>(); 45 46 private final List<Entry> constants = new ArrayList<>(); 47 48 /** The ordered list of constant pool entries. */ constants()49 public ImmutableList<Entry> constants() { 50 return ImmutableList.copyOf(constants); 51 } 52 53 /** The number of constant pool entries the given kind takes up. */ width(Kind kind)54 private static short width(Kind kind) { 55 switch (kind) { 56 case CLASS_INFO: 57 case STRING: 58 case INTEGER: 59 case UTF8: 60 case FLOAT: 61 case MODULE: 62 case PACKAGE: 63 return 1; 64 case LONG: 65 case DOUBLE: 66 // "In retrospect, making 8-byte constants take two constant pool entries 67 // was a poor choice." -- JVMS 4.4.5 68 return 2; 69 } 70 throw new AssertionError(kind); 71 } 72 73 /** A constant pool entry. */ 74 static class Entry { 75 private final Kind kind; 76 private final Value value; 77 Entry(Kind kind, Value value)78 Entry(Kind kind, Value value) { 79 this.kind = kind; 80 this.value = value; 81 } 82 83 /** The entry kind. */ kind()84 public Kind kind() { 85 return kind; 86 } 87 88 /** The entry's value. */ value()89 public Value value() { 90 return value; 91 } 92 } 93 94 /** Adds a CONSTANT_Class_info entry to the pool. */ classInfo(String value)95 int classInfo(String value) { 96 Objects.requireNonNull(value); 97 int utf8 = utf8(value); 98 if (classInfoPool.containsKey(utf8)) { 99 return classInfoPool.get(utf8); 100 } 101 int index = insert(new Entry(Kind.CLASS_INFO, new IntValue(utf8))); 102 classInfoPool.put(utf8, index); 103 return index; 104 } 105 106 /** Adds a CONSTANT_Utf8_info entry to the pool. */ utf8(String value)107 int utf8(String value) { 108 Objects.requireNonNull(value); 109 if (utf8Pool.containsKey(value)) { 110 return utf8Pool.get(value); 111 } 112 int index = insert(new Entry(Kind.UTF8, new StringValue(value))); 113 utf8Pool.put(value, index); 114 return index; 115 } 116 integer(int value)117 int integer(int value) { 118 if (integerPool.containsKey(value)) { 119 return integerPool.get(value); 120 } 121 int index = insert(new Entry(Kind.INTEGER, new Const.IntValue(value))); 122 integerPool.put(value, index); 123 return index; 124 } 125 longInfo(long value)126 int longInfo(long value) { 127 if (longPool.containsKey(value)) { 128 return longPool.get(value); 129 } 130 int index = insert(new Entry(Kind.LONG, new Const.LongValue(value))); 131 longPool.put(value, index); 132 return index; 133 } 134 doubleInfo(double value)135 int doubleInfo(double value) { 136 if (doublePool.containsKey(value)) { 137 return doublePool.get(value); 138 } 139 int index = insert(new Entry(Kind.DOUBLE, new Const.DoubleValue(value))); 140 doublePool.put(value, index); 141 return index; 142 } 143 floatInfo(float value)144 int floatInfo(float value) { 145 if (floatPool.containsKey(value)) { 146 return floatPool.get(value); 147 } 148 int index = insert(new Entry(Kind.FLOAT, new Const.FloatValue(value))); 149 floatPool.put(value, index); 150 return index; 151 } 152 string(String value)153 int string(String value) { 154 Objects.requireNonNull(value); 155 int utf8 = utf8(value); 156 if (stringPool.containsKey(utf8)) { 157 return stringPool.get(utf8); 158 } 159 int index = insert(new Entry(Kind.STRING, new IntValue(utf8))); 160 stringPool.put(utf8, index); 161 return index; 162 } 163 164 /** Adds a CONSTANT_Module_info entry to the pool. */ moduleInfo(String value)165 int moduleInfo(String value) { 166 Objects.requireNonNull(value); 167 int utf8 = utf8(value); 168 if (modulePool.containsKey(utf8)) { 169 return modulePool.get(utf8); 170 } 171 int index = insert(new Entry(Kind.MODULE, new IntValue(utf8))); 172 modulePool.put(utf8, index); 173 return index; 174 } 175 176 /** Adds a CONSTANT_Package_info entry to the pool. */ packageInfo(String value)177 int packageInfo(String value) { 178 Objects.requireNonNull(value); 179 int utf8 = utf8(value); 180 if (packagePool.containsKey(utf8)) { 181 return packagePool.get(utf8); 182 } 183 int index = insert(new Entry(Kind.PACKAGE, new IntValue(utf8))); 184 packagePool.put(utf8, index); 185 return index; 186 } 187 insert(Entry key)188 private int insert(Entry key) { 189 int entry = nextEntry; 190 constants.add(key); 191 nextEntry += width(key.kind()); 192 if ((nextEntry & 0xffff) != nextEntry) { 193 throw new AssertionError("constant pool has more than 2^16 entries"); 194 } 195 return entry; 196 } 197 198 /** Constant pool entry kinds. */ 199 enum Kind { 200 CLASS_INFO(7), 201 STRING(8), 202 INTEGER(3), 203 DOUBLE(6), 204 FLOAT(4), 205 LONG(5), 206 UTF8(1), 207 MODULE(19), 208 PACKAGE(20); 209 210 private final short tag; 211 Kind(int tag)212 Kind(int tag) { 213 this.tag = (short) tag; 214 } 215 216 /** The JVMS Table 4.4-A tag. */ tag()217 public short tag() { 218 return tag; 219 } 220 } 221 } 222