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.Symbol.ClassSymbol;
  29 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  30 import com.sun.tools.javac.code.Symbol.PackageSymbol;
  31 import com.sun.tools.javac.code.Symtab;
  32 import com.sun.tools.javac.code.Type;
  33 import com.sun.tools.javac.jvm.PoolConstant.NameAndType;
  34 import com.sun.tools.javac.util.ByteBuffer;
  35 import com.sun.tools.javac.util.Name;
  36 import com.sun.tools.javac.util.Name.NameMapper;
  37 import com.sun.tools.javac.util.Names;
  38 
  39 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class;
  40 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Double;
  41 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Dynamic;
  42 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Fieldref;
  43 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Float;
  44 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Integer;
  45 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_InterfaceMethodref;
  46 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_InvokeDynamic;
  47 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Long;
  48 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodHandle;
  49 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Methodref;
  50 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodType;
  51 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Module;
  52 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_NameandType;
  53 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Package;
  54 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String;
  55 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Utf8;
  56 import static com.sun.tools.javac.jvm.ClassFile.internalize;
  57 
  58 /**
  59  * Pool interface towards {@code ClassReader}. Exposes methods to decode and read javac entities
  60  * from the constant pool.
  61  *
  62  *  <p><b>This is NOT part of any supported API.
  63  *  If you write code that depends on this, you do so at your own risk.
  64  *  This code and its internal interfaces are subject to change or
  65  *  deletion without notice.</b>
  66  */
  67 public class PoolReader {
  68 
  69     private final ClassReader reader;
  70     private final ByteBuffer buf;
  71     private final Names names;
  72     private final Symtab syms;
  73 
  74     private ImmutablePoolHelper pool;
  75 
  76     PoolReader(ByteBuffer buf) {
  77         this(null, buf, null, null);
  78     }
  79 
  80     PoolReader(ClassReader reader, Names names, Symtab syms) {
  81         this(reader, reader.buf, names, syms);
  82     }
  83 
  84     PoolReader(ClassReader reader, ByteBuffer buf, Names names, Symtab syms) {
  85         this.reader = reader;
  86         this.buf = buf;
  87         this.names = names;
  88         this.syms = syms;
  89     }
  90 
  91     /**
  92      * Get a class symbol from the pool at given index.
  93      */
  94     ClassSymbol getClass(int index) {
  95         return pool.readIfNeeded(index);
  96     }
  97 
  98     /**
  99      * Get class name without resolving
 100      */
 101     <Z> Z peekClassName(int index, NameMapper<Z> mapper) {
 102         return peekName(buf.getChar(pool.offset(index)), mapper);
 103     }
 104 
 105     /**
 106      * Get package name without resolving
 107      */
 108     <Z> Z peekPackageName(int index, NameMapper<Z> mapper) {
 109         return peekName(buf.getChar(pool.offset(index)), mapper);
 110     }
 111 
 112     /**
 113      * Get module name without resolving
 114      */
 115     <Z> Z peekModuleName(int index, NameMapper<Z> mapper) {
 116         return peekName(buf.getChar(pool.offset(index)), mapper);
 117     }
 118 
 119     /**
 120      * Get a module symbol from the pool at given index.
 121      */
 122     ModuleSymbol getModule(int index) {
 123         return pool.readIfNeeded(index);
 124     }
 125 
 126     /**
 127      * Get a module symbol from the pool at given index.
 128      */
 129     PackageSymbol getPackage(int index) {
 130         return pool.readIfNeeded(index);
 131     }
 132 
 133     /**
 134      * Peek a name from the pool at given index without resolving.
 135      */
 136     <Z> Z peekName(int index, Name.NameMapper<Z> mapper) {
 137         return getUtf8(index, mapper);
 138     }
 139 
 140     /**
 141      * Get a name from the pool at given index.
 142      */
 143     Name getName(int index) {
 144         return pool.readIfNeeded(index);
 145     }
 146 
 147     /**
 148      * Get a type from the pool at given index.
 149      */
 150     Type getType(int index) {
 151         return getName(index).map(reader::sigToType);
 152     }
 153 
 154     /**
 155      * Get a name and type pair from the pool at given index.
 156      */
 157     NameAndType getNameAndType(int index) {
 158         return pool.readIfNeeded(index);
 159     }
 160 
 161     /**
 162      * Get a class symbol from the pool at given index.
 163      */
 164     Object getConstant(int index) {
 165         return pool.readIfNeeded(index);
 166     }
 167 
 168     boolean hasTag(int index, int tag) {
 169         return pool.tag(index) == tag;
 170     }
 171 
 172     private <Z> Z getUtf8(int index, NameMapper<Z> mapper) {
 173         int tag = pool.tag(index);
 174         if (tag == CONSTANT_Utf8) {
 175             int offset = pool.offset(index);
 176             int len = pool.poolbuf.getChar(offset);
 177             return mapper.map(pool.poolbuf.elems, offset + 2, len);
 178         } else {
 179             throw new AssertionError("Unexpected constant tag: " + tag);
 180         }
 181     }
 182 
 183     private Object resolve(ByteBuffer poolbuf, int tag, int offset) {
 184         switch (tag) {
 185             case CONSTANT_Utf8: {
 186                 int len = poolbuf.getChar(offset);
 187                 return names.fromUtf(poolbuf.elems, offset + 2, len);
 188             }
 189             case CONSTANT_Class: {
 190                 int index = poolbuf.getChar(offset);
 191                 Name name = names.fromUtf(getName(index).map(ClassFile::internalize));
 192                 return syms.enterClass(reader.currentModule, name);
 193             }
 194             case CONSTANT_NameandType: {
 195                 Name name = getName(poolbuf.getChar(offset));
 196                 Type type = getType(poolbuf.getChar(offset + 2));
 197                 return new NameAndType(name, type);
 198             }
 199             case CONSTANT_Integer:
 200                 return poolbuf.getInt(offset);
 201             case CONSTANT_Float:
 202                 return poolbuf.getFloat(offset);
 203             case CONSTANT_Long:
 204                 return poolbuf.getLong(offset);
 205             case CONSTANT_Double:
 206                 return poolbuf.getDouble(offset);
 207             case CONSTANT_String:
 208                 return getName(poolbuf.getChar(offset)).toString();
 209             case CONSTANT_Package: {
 210                 Name name = getName(poolbuf.getChar(offset));
 211                 return syms.enterPackage(reader.currentModule, names.fromUtf(internalize(name)));
 212             }
 213             case CONSTANT_Module: {
 214                 Name name = getName(poolbuf.getChar(offset));
 215                 return syms.enterModule(name);
 216             }
 217             default:
 218                 throw new AssertionError("Unexpected constant tag: " + tag);
 219         }
 220     }
 221 
 222     /**
 223      * Parse all constant pool entries, and keep track of their offsets. For performance and laziness
 224      * reasons, it would be unwise to eagerly turn all pool entries into corresponding javac
 225      * entities. First, not all entries are actually going to be read/used by javac; secondly,
 226      * there are cases where creating a symbol too early might result in issues (hence methods like
 227      * {@link PoolReader#peekClassName(int, NameMapper)}.
 228      */
 229     int readPool(ByteBuffer poolbuf, int offset) {
 230         int poolSize = poolbuf.getChar(offset);
 231         int index = 1;
 232         offset += 2;
 233         int[] offsets = new int[poolSize];
 234         while (index < poolSize) {
 235             byte tag = poolbuf.getByte(offset++);
 236             offsets[index] = offset;
 237             switch (tag) {
 238                 case CONSTANT_Utf8: {
 239                     int len = poolbuf.getChar(offset);
 240                     offset += 2 + len;
 241                     break;
 242                 }
 243                 case CONSTANT_Class:
 244                 case CONSTANT_String:
 245                 case CONSTANT_Module:
 246                 case CONSTANT_Package:
 247                 case CONSTANT_MethodType:
 248                     offset += 2;
 249                     break;
 250                 case CONSTANT_MethodHandle:
 251                     offset += 3;
 252                     break;
 253                 case CONSTANT_Fieldref:
 254                 case CONSTANT_Methodref:
 255                 case CONSTANT_InterfaceMethodref:
 256                 case CONSTANT_NameandType:
 257                 case CONSTANT_Integer:
 258                 case CONSTANT_Float:
 259                 case CONSTANT_Dynamic:
 260                 case CONSTANT_InvokeDynamic:
 261                     offset += 4;
 262                     break;
 263                 case CONSTANT_Long:
 264                 case CONSTANT_Double:
 265                     offset += 8;
 266                     break;
 267                 default:
 268                     throw reader.badClassFile("bad.const.pool.tag.at",
 269                             Byte.toString(tag),
 270                             Integer.toString(offset - 1));
 271             }
 272             index += sizeof(tag);
 273         }
 274         pool = new ImmutablePoolHelper(poolbuf, offsets);
 275         return offset;
 276     }
 277 
 278     private int sizeof(int tag) {
 279         switch (tag) {
 280             case ClassFile.CONSTANT_Double: case ClassFile.CONSTANT_Long:
 281                 return 2;
 282             default:
 283                 return 1;
 284         }
 285     }
 286 
 287     class ImmutablePoolHelper {
 288 
 289         final Object[] values;
 290         final int[] offsets;
 291         final ByteBuffer poolbuf;
 292 
 293         public ImmutablePoolHelper(ByteBuffer poolbuf, int[] offsets) {
 294             this.offsets = offsets;
 295             this.values = new Object[offsets.length];
 296             this.poolbuf = poolbuf;
 297         }
 298 
 299         private int checkIndex(int index) {
 300             if (index <= 0 || index >= offsets.length) {
 301                 //pool index is outside valid range.
 302                 throw reader.badClassFile("bad.const.pool.index", reader.currentClassFile,
 303                         index, offsets.length);
 304             } else {
 305                 return index;
 306             }
 307         }
 308 
 309         int offset(int index) {
 310             return offsets[checkIndex(index)];
 311         }
 312 
 313         @SuppressWarnings("unchecked")
 314         <P> P readIfNeeded(int index) {
 315             Object v = values[checkIndex(index)];
 316             if (v != null) {
 317                 return (P)v;
 318             } else {
 319                 P p = (P)resolve(poolbuf, tag(index), offset(index));
 320                 values[index] = p;
 321                 return p;
 322             }
 323         }
 324 
 325         int tag(int index) {
 326             return poolbuf.elems[offset(index) - 1];
 327         }
 328     }
 329 }