1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.jvm; 27 28 import com.sun.tools.javac.code.Kinds.Kind; 29 import com.sun.tools.javac.code.Symbol; 30 import com.sun.tools.javac.code.Symbol.ClassSymbol; 31 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; 32 import com.sun.tools.javac.code.Symbol.MethodHandleSymbol; 33 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 34 import com.sun.tools.javac.code.Symbol.PackageSymbol; 35 import com.sun.tools.javac.code.Type; 36 import com.sun.tools.javac.code.Types; 37 import com.sun.tools.javac.jvm.ClassWriter.PoolOverflow; 38 import com.sun.tools.javac.jvm.ClassWriter.StringOverflow; 39 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; 40 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant.BasicConstant; 41 import com.sun.tools.javac.jvm.PoolConstant.Dynamic; 42 import com.sun.tools.javac.jvm.PoolConstant.Dynamic.BsmKey; 43 import com.sun.tools.javac.jvm.PoolConstant.NameAndType; 44 import com.sun.tools.javac.util.ByteBuffer; 45 import com.sun.tools.javac.util.List; 46 import com.sun.tools.javac.util.Name; 47 import com.sun.tools.javac.util.Names; 48 49 import java.io.IOException; 50 import java.io.OutputStream; 51 import java.util.ArrayDeque; 52 import java.util.HashMap; 53 import java.util.LinkedHashMap; 54 import java.util.LinkedHashSet; 55 import java.util.Map; 56 57 import static com.sun.tools.javac.code.Kinds.Kind.TYP; 58 import static com.sun.tools.javac.code.TypeTag.ARRAY; 59 import static com.sun.tools.javac.code.TypeTag.CLASS; 60 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class; 61 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodType; 62 import static com.sun.tools.javac.jvm.ClassFile.externalize; 63 64 /** 65 * Pool interface towards {@code ClassWriter}. Exposes methods to encode and write javac entities 66 * into the constant pool. 67 * 68 * <p><b>This is NOT part of any supported API. 69 * If you write code that depends on this, you do so at your own risk. 70 * This code and its internal interfaces are subject to change or 71 * deletion without notice.</b> 72 */ 73 public class PoolWriter { 74 75 /** Max number of constant pool entries. */ 76 public static final int MAX_ENTRIES = 0xFFFF; 77 78 /** Max number of char in a string constant. */ 79 public static final int MAX_STRING_LENGTH = 0xFFFF; 80 81 private static final int POOL_BUF_SIZE = 0x7fff; 82 83 private final Types types; 84 85 private final Names names; 86 87 /** Pool helper **/ 88 final WriteablePoolHelper pool; 89 90 /** Sole signature generator */ 91 final SharedSignatureGenerator signatureGen; 92 93 /** The inner classes to be written, as an ordered set (enclosing first). */ 94 LinkedHashSet<ClassSymbol> innerClasses = new LinkedHashSet<>(); 95 96 /** The list of entries in the BootstrapMethods attribute. */ 97 Map<BsmKey, Integer> bootstrapMethods = new LinkedHashMap<>(); 98 99 public PoolWriter(Types types, Names names) { 100 this.types = types; 101 this.names = names; 102 this.signatureGen = new SharedSignatureGenerator(types); 103 this.pool = new WriteablePoolHelper(); 104 } 105 106 /** 107 * Puts a class symbol into the pool and return its index. 108 */ 109 int putClass(ClassSymbol csym) { 110 return putClass(csym.type); 111 } 112 113 /** 114 * Puts a type into the pool and return its index. The type could be either a class, a type variable 115 * or an array type. 116 */ 117 int putClass(Type t) { 118 return pool.writeIfNeeded(types.erasure(t)); 119 } 120 121 /** 122 * Puts a member reference into the constant pool. Valid members are either field or method symbols. 123 */ 124 int putMember(Symbol s) { 125 return pool.writeIfNeeded(s); 126 } 127 128 /** 129 * Puts a dynamic reference into the constant pool and return its index. 130 */ 131 int putDynamic(Dynamic d) { 132 return pool.writeIfNeeded(d); 133 } 134 135 /** 136 * Puts a field or method descriptor into the constant pool and return its index. 137 */ 138 int putDescriptor(Type t) { 139 return putName(typeSig(types.erasure(t))); 140 } 141 142 /** 143 * Puts a field or method descriptor into the constant pool and return its index. 144 */ 145 int putDescriptor(Symbol s) { 146 return putDescriptor(descriptorType(s)); 147 } 148 149 /** 150 * Puts a signature (see {@code Signature} attribute in JVMS 4.4) into the constant pool and 151 * return its index. 152 */ 153 int putSignature(Symbol s) { 154 if (s.kind == TYP) { 155 return putName(classSig(s.type)); 156 } else { 157 return putName(typeSig(s.type)); 158 } 159 } 160 161 /** 162 * Puts a constant value into the pool and return its index. Supported values are int, float, long, 163 * double and String. 164 */ 165 int putConstant(Object o) { 166 if (o instanceof Integer) { 167 return putConstant(LoadableConstant.Int((int)o)); 168 } else if (o instanceof Float) { 169 return putConstant(LoadableConstant.Float((float)o)); 170 } else if (o instanceof Long) { 171 return putConstant(LoadableConstant.Long((long)o)); 172 } else if (o instanceof Double) { 173 return putConstant(LoadableConstant.Double((double)o)); 174 } else if (o instanceof String) { 175 return putConstant(LoadableConstant.String((String)o)); 176 } else { 177 throw new AssertionError("unexpected constant: " + o); 178 } 179 } 180 181 /** 182 * Puts a constant into the pool and return its index. 183 */ 184 int putConstant(LoadableConstant c) { 185 switch (c.poolTag()) { 186 case CONSTANT_Class: 187 return putClass((Type)c); 188 case CONSTANT_MethodType: 189 return pool.writeIfNeeded(types.erasure((Type)c)); 190 default: 191 return pool.writeIfNeeded(c); 192 } 193 } 194 195 int putName(Name name) { 196 return pool.writeIfNeeded(name); 197 } 198 199 /** 200 * Puts a name and type pair into the pool and returns its index. 201 */ 202 int putNameAndType(Symbol s) { 203 return pool.writeIfNeeded(new NameAndType(s.name, descriptorType(s))); 204 } 205 206 /** 207 * Puts a package entry into the pool and returns its index. 208 */ 209 int putPackage(PackageSymbol pkg) { 210 return pool.writeIfNeeded(pkg); 211 } 212 213 /** 214 * Puts a module entry into the pool and returns its index. 215 */ 216 int putModule(ModuleSymbol mod) { 217 return pool.writeIfNeeded(mod); 218 } 219 220 /** 221 * Enter an inner class into the `innerClasses' set. 222 */ 223 void enterInner(ClassSymbol c) { 224 if (c.type.isCompound()) { 225 throw new AssertionError("Unexpected intersection type: " + c.type); 226 } 227 c.complete(); 228 if (c.owner.enclClass() != null && !innerClasses.contains(c)) { 229 enterInner(c.owner.enclClass()); 230 innerClasses.add(c); 231 } 232 } 233 234 /** 235 * Create a new Utf8 entry representing a descriptor for given (member) symbol. 236 */ 237 private Type descriptorType(Symbol s) { 238 return s.kind == Kind.MTH ? s.externalType(types) : s.erasure(types); 239 } 240 241 private int makeBoostrapEntry(Dynamic dynamic) { 242 BsmKey bsmKey = dynamic.bsmKey(types); 243 244 // Figure out the index for existing BSM; create a new BSM if no key 245 Integer index = bootstrapMethods.get(bsmKey); 246 if (index == null) { 247 index = bootstrapMethods.size(); 248 bootstrapMethods.put(bsmKey, index); 249 } 250 251 return index; 252 } 253 254 /** 255 * Write pool contents into given byte buffer. 256 */ 257 void writePool(OutputStream out) throws IOException, PoolOverflow { 258 if (pool.overflowString != null) { 259 throw new StringOverflow(pool.overflowString); 260 } 261 int size = size(); 262 if (size > MAX_ENTRIES) { 263 throw new PoolOverflow(); 264 } 265 out.write(size >> 8); 266 out.write(size); 267 out.write(pool.poolbuf.elems, 0, pool.poolbuf.length); 268 } 269 270 /** 271 * Signature Generation 272 */ 273 class SharedSignatureGenerator extends Types.SignatureGenerator { 274 275 /** 276 * An output buffer for type signatures. 277 */ 278 ByteBuffer sigbuf = new ByteBuffer(); 279 280 SharedSignatureGenerator(Types types) { 281 super(types); 282 } 283 284 /** 285 * Assemble signature of given type in string buffer. 286 * Check for uninitialized types before calling the general case. 287 */ 288 @Override 289 public void assembleSig(Type type) { 290 switch (type.getTag()) { 291 case UNINITIALIZED_THIS: 292 case UNINITIALIZED_OBJECT: 293 // we don't yet have a spec for uninitialized types in the 294 // local variable table 295 assembleSig(types.erasure(((UninitializedType)type).qtype)); 296 break; 297 default: 298 super.assembleSig(type); 299 } 300 } 301 302 @Override 303 protected void append(char ch) { 304 sigbuf.appendByte(ch); 305 } 306 307 @Override 308 protected void append(byte[] ba) { 309 sigbuf.appendBytes(ba); 310 } 311 312 @Override 313 protected void append(Name name) { 314 sigbuf.appendName(name); 315 } 316 317 @Override 318 protected void classReference(ClassSymbol c) { 319 enterInner(c); 320 } 321 322 protected void reset() { 323 sigbuf.reset(); 324 } 325 326 protected Name toName() { 327 return sigbuf.toName(names); 328 } 329 } 330 331 class WriteablePoolHelper { 332 333 /** Pool entries. */ 334 private final Map<Object, Integer> keysToPos = new HashMap<>(64); 335 336 final ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE); 337 338 int currentIndex = 1; 339 340 ArrayDeque<PoolConstant> todo = new ArrayDeque<>(); 341 342 String overflowString = null; 343 344 private <P extends PoolConstant> int writeIfNeeded(P p) { 345 Object key = p.poolKey(types); 346 Integer index = keysToPos.get(key); 347 if (index == null) { 348 keysToPos.put(key, index = currentIndex++); 349 boolean first = todo.isEmpty(); 350 todo.addLast(p); 351 if (first) { 352 while (!todo.isEmpty()) { 353 writeConstant(todo.peekFirst()); 354 todo.removeFirst(); 355 } 356 } 357 } 358 return index; 359 } 360 361 void writeConstant(PoolConstant c) { 362 int tag = c.poolTag(); 363 switch (tag) { 364 case ClassFile.CONSTANT_Class: { 365 Type ct = (Type)c; 366 Name name = ct.hasTag(ARRAY) ? 367 typeSig(ct) : 368 names.fromUtf(externalize(ct.tsym.flatName())); 369 poolbuf.appendByte(tag); 370 poolbuf.appendChar(putName(name)); 371 if (ct.hasTag(CLASS)) { 372 enterInner((ClassSymbol)ct.tsym); 373 } 374 break; 375 } 376 case ClassFile.CONSTANT_Utf8: { 377 Name name = (Name)c; 378 poolbuf.appendByte(tag); 379 byte[] bs = name.toUtf(); 380 poolbuf.appendChar(bs.length); 381 poolbuf.appendBytes(bs, 0, bs.length); 382 if (overflowString == null && bs.length > MAX_STRING_LENGTH) { 383 //report error only once 384 overflowString = new String(bs); 385 } 386 break; 387 } 388 case ClassFile.CONSTANT_InterfaceMethodref: 389 case ClassFile.CONSTANT_Methodref: 390 case ClassFile.CONSTANT_Fieldref: { 391 Symbol sym = (Symbol)c; 392 poolbuf.appendByte(tag); 393 poolbuf.appendChar(putClass((ClassSymbol)sym.owner)); 394 poolbuf.appendChar(putNameAndType(sym)); 395 break; 396 } 397 case ClassFile.CONSTANT_Package: { 398 PackageSymbol pkg = (PackageSymbol)c; 399 Name pkgName = names.fromUtf(externalize(pkg.flatName())); 400 poolbuf.appendByte(tag); 401 poolbuf.appendChar(putName(pkgName)); 402 break; 403 } 404 case ClassFile.CONSTANT_Module: { 405 ModuleSymbol mod = (ModuleSymbol)c; 406 int modName = putName(mod.name); 407 poolbuf.appendByte(mod.poolTag()); 408 poolbuf.appendChar(modName); 409 break; 410 } 411 case ClassFile.CONSTANT_Integer: 412 poolbuf.appendByte(tag); 413 poolbuf.appendInt((int)((BasicConstant)c).data); 414 break; 415 case ClassFile.CONSTANT_Float: 416 poolbuf.appendByte(tag); 417 poolbuf.appendFloat((float)((BasicConstant)c).data); 418 break; 419 case ClassFile.CONSTANT_Long: 420 currentIndex++; 421 poolbuf.appendByte(tag); 422 poolbuf.appendLong((long)((BasicConstant)c).data); 423 break; 424 case ClassFile.CONSTANT_Double: 425 currentIndex++; 426 poolbuf.appendByte(tag); 427 poolbuf.appendDouble((double)((BasicConstant)c).data); 428 break; 429 case ClassFile.CONSTANT_MethodHandle: { 430 MethodHandleSymbol h = (MethodHandleSymbol)c; 431 poolbuf.appendByte(tag); 432 poolbuf.appendByte(h.referenceKind()); 433 poolbuf.appendChar(putMember(h.baseSymbol())); 434 break; 435 } 436 case ClassFile.CONSTANT_MethodType: { 437 Type.MethodType mt = (Type.MethodType)c; 438 poolbuf.appendByte(tag); 439 poolbuf.appendChar(putDescriptor(mt.baseType())); 440 break; 441 } 442 case ClassFile.CONSTANT_String: { 443 Name utf = names.fromString((String)((BasicConstant)c).data); 444 poolbuf.appendByte(tag); 445 poolbuf.appendChar(putName(utf)); 446 break; 447 } 448 case ClassFile.CONSTANT_NameandType: { 449 NameAndType nt = (NameAndType)c; 450 poolbuf.appendByte(tag); 451 poolbuf.appendChar(putName(nt.name)); 452 poolbuf.appendChar(putDescriptor(nt.type)); 453 break; 454 } 455 case ClassFile.CONSTANT_InvokeDynamic: { 456 DynamicMethodSymbol d = (DynamicMethodSymbol)c; 457 poolbuf.appendByte(tag); 458 poolbuf.appendChar(makeBoostrapEntry(d)); 459 poolbuf.appendChar(putNameAndType(d)); 460 break; 461 } 462 case ClassFile.CONSTANT_Dynamic: { 463 Symbol.DynamicVarSymbol d = (Symbol.DynamicVarSymbol)c; 464 poolbuf.appendByte(tag); 465 poolbuf.appendChar(makeBoostrapEntry(d)); 466 poolbuf.appendChar(putNameAndType(d)); 467 break; 468 } 469 default: 470 throw new AssertionError("Unexpected constant tag: " + tag); 471 } 472 } 473 474 void reset() { 475 keysToPos.clear(); 476 currentIndex = 1; 477 todo.clear(); 478 overflowString = null; 479 poolbuf.reset(); 480 } 481 } 482 483 int size() { 484 return pool.currentIndex; 485 } 486 487 /** 488 * Return signature of given type 489 */ 490 private Name typeSig(Type type) { 491 signatureGen.reset(); 492 signatureGen.assembleSig(type); 493 return signatureGen.toName(); 494 } 495 496 private Name classSig(Type t) { 497 signatureGen.reset(); 498 List<Type> typarams = t.getTypeArguments(); 499 if (typarams.nonEmpty()) { 500 signatureGen.assembleParamsSig(typarams); 501 } 502 signatureGen.assembleSig(types.supertype(t)); 503 for (Type i : types.interfaces(t)) 504 signatureGen.assembleSig(i); 505 return signatureGen.toName(); 506 } 507 508 void reset() { 509 innerClasses.clear(); 510 bootstrapMethods.clear(); 511 pool.reset(); 512 } 513 }