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