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