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.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.InvalidUtfException; 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<Symbol> loadableDescriptors = 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(); 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 return pool.writeIfNeeded(types.erasure(t)); 124 } 125 126 /** 127 * Puts a member reference into the constant pool. Valid members are either field or method symbols. 128 */ 129 int putMember(Symbol s) { 130 return pool.writeIfNeeded(s); 131 } 132 133 /** 134 * Puts a dynamic reference into the constant pool and return its index. 135 */ 136 int putDynamic(Dynamic d) { 137 return pool.writeIfNeeded(d); 138 } 139 140 /** 141 * Puts a field or method descriptor into the constant pool and return its index. 142 */ 143 int putDescriptor(Type t) { 144 return putName(typeSig(types.erasure(t))); 145 } 146 147 /** 148 * Puts a field or method descriptor into the constant pool and return its index. 149 */ 150 int putDescriptor(Symbol s) { 151 return putDescriptor(descriptorType(s)); 152 } 153 154 /** 155 * Puts a signature (see {@code Signature} attribute in JVMS 4.4) into the constant pool and 156 * return its index. 157 */ 158 int putSignature(Symbol s) { 159 if (s.kind == TYP) { 160 return putName(classSig(s.type)); 161 } else { 162 return putName(typeSig(s.type)); 163 } 164 } 165 166 /** 167 * Puts a constant value into the pool and return its index. Supported values are int, float, long, 168 * double and String. 169 */ 170 int putConstant(Object o) { 171 if (o instanceof Integer intVal) { 172 return putConstant(LoadableConstant.Int(intVal)); 173 } else if (o instanceof Float floatVal) { 174 return putConstant(LoadableConstant.Float(floatVal)); 175 } else if (o instanceof Long longVal) { 176 return putConstant(LoadableConstant.Long(longVal)); 177 } else if (o instanceof Double doubleVal) { 178 return putConstant(LoadableConstant.Double(doubleVal)); 179 } else if (o instanceof String strVal) { 180 return putConstant(LoadableConstant.String(strVal)); 181 } else { 182 throw new AssertionError("unexpected constant: " + o); 183 } 184 } 185 186 /** 187 * Puts a constant into the pool and return its index. 188 */ 189 int putConstant(LoadableConstant c) { 190 switch (c.poolTag()) { 191 case CONSTANT_Class: 192 return putClass((Type)c); 193 case CONSTANT_MethodType: 194 return pool.writeIfNeeded(types.erasure((Type)c)); 195 default: 196 return pool.writeIfNeeded(c); 197 } 198 } 199 200 int putName(Name name) { 201 return pool.writeIfNeeded(name); 202 } 203 204 /** 205 * Puts a name and type pair into the pool and returns its index. 206 */ 207 int putNameAndType(Symbol s) { 208 return pool.writeIfNeeded(new NameAndType(s.name, descriptorType(s))); 209 } 210 211 /** 212 * Puts a package entry into the pool and returns its index. 213 */ 214 int putPackage(PackageSymbol pkg) { 215 return pool.writeIfNeeded(pkg); 216 } 217 218 /** 219 * Puts a module entry into the pool and returns its index. 220 */ 221 int putModule(ModuleSymbol mod) { 222 return pool.writeIfNeeded(mod); 223 } 224 225 /** 226 * Enter an inner class into the `innerClasses' set. 227 */ 228 void enterInner(ClassSymbol c) { 229 if (c.type.isCompound()) { 230 throw new AssertionError("Unexpected intersection type: " + c.type); 231 } 232 c.complete(); 233 if (c.owner.enclClass() != null && !innerClasses.contains(c)) { 234 enterInner(c.owner.enclClass()); 235 innerClasses.add(c); 236 } 237 } 238 239 /** Enter a value class into the `loadableDescriptorsClasses' set. 240 */ 241 void enterLoadableDescriptorsClass(Symbol c) { 242 if (c.type.isCompound()) { 243 throw new AssertionError("Unexpected intersection type: " + c.type); 244 } 245 c.complete(); 246 loadableDescriptors.add(c); 247 } 248 249 /** 250 * Create a new Utf8 entry representing a descriptor for given (member) symbol. 251 */ 252 private Type descriptorType(Symbol s) { 253 return s.kind == Kind.MTH ? s.externalType(types) : s.erasure(types); 254 } 255 256 private int makeBootstrapEntry(Dynamic dynamic) { 257 BsmKey bsmKey = dynamic.bsmKey(types); 258 259 // Figure out the index for existing BSM; create a new BSM if no key 260 Integer index = bootstrapMethods.get(bsmKey); 261 if (index == null) { 262 index = bootstrapMethods.size(); 263 bootstrapMethods.put(bsmKey, index); 264 } 265 266 return index; 267 } 268 269 /** 270 * Write pool contents into given byte buffer. 271 */ 272 void writePool(OutputStream out) throws IOException, PoolOverflow { 273 if (pool.overflowString != null) { 274 throw new StringOverflow(pool.overflowString); 275 } 276 int size = size(); 277 if (size > MAX_ENTRIES) { 278 throw new PoolOverflow(); 279 } 280 out.write(size >> 8); 281 out.write(size); 282 out.write(pool.poolbuf.elems, 0, pool.poolbuf.length); 283 } 284 285 /** 286 * Signature Generation 287 */ 288 class SharedSignatureGenerator extends Types.SignatureGenerator { 289 290 /** 291 * An output buffer for type signatures. 292 */ 293 ByteBuffer sigbuf = new ByteBuffer(); 294 295 SharedSignatureGenerator() { 296 types.super(); 297 } 298 299 /** 300 * Assemble signature of given type in string buffer. 301 * Check for uninitialized types before calling the general case. 302 */ 303 @Override 304 public void assembleSig(Type type) { 305 switch (type.getTag()) { 306 case UNINITIALIZED_THIS: 307 case UNINITIALIZED_OBJECT: 308 // we don't yet have a spec for uninitialized types in the 309 // local variable table 310 assembleSig(types.erasure(((UninitializedType)type).qtype)); 311 break; 312 default: 313 super.assembleSig(type); 314 } 315 } 316 317 @Override 318 protected void append(char ch) { 319 sigbuf.appendByte(ch); 320 } 321 322 @Override 323 protected void append(byte[] ba) { 324 sigbuf.appendBytes(ba); 325 } 326 327 @Override 328 protected void append(Name name) { 329 sigbuf.appendName(name); 330 } 331 332 @Override 333 protected void classReference(ClassSymbol c) { 334 enterInner(c); 335 } 336 337 protected void reset() { 338 sigbuf.reset(); 339 } 340 341 protected Name toName() { 342 try { 343 return sigbuf.toName(names); 344 } catch (InvalidUtfException e) { 345 throw new AssertionError(e); 346 } 347 } 348 } 349 350 class WriteablePoolHelper { 351 352 /** Pool entries. */ 353 private final Map<Object, Integer> keysToPos = new HashMap<>(64); 354 355 final ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE); 356 357 int currentIndex = 1; 358 359 ArrayDeque<PoolConstant> todo = new ArrayDeque<>(); 360 361 String overflowString = null; 362 363 private <P extends PoolConstant> int writeIfNeeded(P p) { 364 Object key = p.poolKey(types); 365 Integer index = keysToPos.get(key); 366 if (index == null) { 367 keysToPos.put(key, index = currentIndex++); 368 boolean first = todo.isEmpty(); 369 todo.addLast(p); 370 if (first) { 371 while (!todo.isEmpty()) { 372 writeConstant(todo.peekFirst()); 373 todo.removeFirst(); 374 } 375 } 376 } 377 return index; 378 } 379 380 void writeConstant(PoolConstant c) { 381 int tag = c.poolTag(); 382 switch (tag) { 383 case ClassFile.CONSTANT_Class: { 384 Type ct = (Type)c; 385 Name name = ct.hasTag(ARRAY) ? 386 typeSig(ct) : 387 externalize(ct.tsym.flatName()); 388 poolbuf.appendByte(tag); 389 poolbuf.appendChar(putName(name)); 390 if (ct.hasTag(CLASS)) { 391 enterInner((ClassSymbol)ct.tsym); 392 } 393 break; 394 } 395 case ClassFile.CONSTANT_Utf8: { 396 Name name = (Name)c; 397 poolbuf.appendByte(tag); 398 byte[] bs = name.toUtf(); 399 poolbuf.appendChar(bs.length); 400 poolbuf.appendBytes(bs, 0, bs.length); 401 if (overflowString == null && bs.length > MAX_STRING_LENGTH) { 402 //report error only once 403 overflowString = new String(bs); 404 } 405 break; 406 } 407 case ClassFile.CONSTANT_InterfaceMethodref: 408 case ClassFile.CONSTANT_Methodref: 409 case ClassFile.CONSTANT_Fieldref: { 410 Symbol sym = (Symbol)c; 411 poolbuf.appendByte(tag); 412 poolbuf.appendChar(putClass((ClassSymbol)sym.owner)); 413 poolbuf.appendChar(putNameAndType(sym)); 414 break; 415 } 416 case ClassFile.CONSTANT_Package: { 417 PackageSymbol pkg = (PackageSymbol)c; 418 Name pkgName = externalize(pkg.flatName()); 419 poolbuf.appendByte(tag); 420 poolbuf.appendChar(putName(pkgName)); 421 break; 422 } 423 case ClassFile.CONSTANT_Module: { 424 ModuleSymbol mod = (ModuleSymbol)c; 425 int modName = putName(mod.name); 426 poolbuf.appendByte(mod.poolTag()); 427 poolbuf.appendChar(modName); 428 break; 429 } 430 case ClassFile.CONSTANT_Integer: 431 poolbuf.appendByte(tag); 432 poolbuf.appendInt((int)((BasicConstant)c).data); 433 break; 434 case ClassFile.CONSTANT_Float: 435 poolbuf.appendByte(tag); 436 poolbuf.appendFloat((float)((BasicConstant)c).data); 437 break; 438 case ClassFile.CONSTANT_Long: 439 currentIndex++; 440 poolbuf.appendByte(tag); 441 poolbuf.appendLong((long)((BasicConstant)c).data); 442 break; 443 case ClassFile.CONSTANT_Double: 444 currentIndex++; 445 poolbuf.appendByte(tag); 446 poolbuf.appendDouble((double)((BasicConstant)c).data); 447 break; 448 case ClassFile.CONSTANT_MethodHandle: { 449 MethodHandleSymbol h = (MethodHandleSymbol)c; 450 poolbuf.appendByte(tag); 451 poolbuf.appendByte(h.referenceKind()); 452 poolbuf.appendChar(putMember(h.baseSymbol())); 453 break; 454 } 455 case ClassFile.CONSTANT_MethodType: { 456 Type.MethodType mt = (Type.MethodType)c; 457 poolbuf.appendByte(tag); 458 poolbuf.appendChar(putDescriptor(mt.baseType())); 459 break; 460 } 461 case ClassFile.CONSTANT_String: { 462 Name utf = names.fromString((String)((BasicConstant)c).data); 463 poolbuf.appendByte(tag); 464 poolbuf.appendChar(putName(utf)); 465 break; 466 } 467 case ClassFile.CONSTANT_NameandType: { 468 NameAndType nt = (NameAndType)c; 469 poolbuf.appendByte(tag); 470 poolbuf.appendChar(putName(nt.name)); 471 poolbuf.appendChar(putDescriptor(nt.type)); 472 break; 473 } 474 case ClassFile.CONSTANT_InvokeDynamic: { 475 DynamicMethodSymbol d = (DynamicMethodSymbol)c; 476 poolbuf.appendByte(tag); 477 poolbuf.appendChar(makeBootstrapEntry(d)); 478 poolbuf.appendChar(putNameAndType(d)); 479 break; 480 } 481 case ClassFile.CONSTANT_Dynamic: { 482 Symbol.DynamicVarSymbol d = (Symbol.DynamicVarSymbol)c; 483 poolbuf.appendByte(tag); 484 poolbuf.appendChar(makeBootstrapEntry(d)); 485 poolbuf.appendChar(putNameAndType(d)); 486 break; 487 } 488 default: 489 throw new AssertionError("Unexpected constant tag: " + tag); 490 } 491 } 492 493 void reset() { 494 keysToPos.clear(); 495 currentIndex = 1; 496 todo.clear(); 497 overflowString = null; 498 poolbuf.reset(); 499 } 500 } 501 502 int size() { 503 return pool.currentIndex; 504 } 505 506 /** 507 * Return signature of given type 508 */ 509 private Name typeSig(Type type) { 510 signatureGen.reset(); 511 signatureGen.assembleSig(type); 512 return signatureGen.toName(); 513 } 514 515 private Name classSig(Type t) { 516 signatureGen.reset(); 517 List<Type> typarams = t.getTypeArguments(); 518 if (typarams.nonEmpty()) { 519 signatureGen.assembleParamsSig(typarams); 520 } 521 signatureGen.assembleSig(types.supertype(t)); 522 for (Type i : types.interfaces(t)) 523 signatureGen.assembleSig(i); 524 return signatureGen.toName(); 525 } 526 527 void reset() { 528 innerClasses.clear(); 529 loadableDescriptors.clear(); 530 bootstrapMethods.clear(); 531 pool.reset(); 532 } 533 }