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 }