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