ModuleNameReader.java revision 3675:50a14048e428
1/* 2 * Copyright (c) 1999, 2016, 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 */ 25package com.sun.tools.javac.jvm; 26 27import java.io.IOException; 28import java.io.InputStream; 29import java.nio.file.Files; 30import java.nio.file.Path; 31 32import javax.tools.JavaFileObject; 33 34import com.sun.tools.javac.jvm.ClassFile; 35 36import static com.sun.tools.javac.jvm.ClassFile.*; 37 38 39/** 40 * Stripped down ClassReader, just sufficient to read module names from module-info.class files 41 * while analyzing the module path. 42 * 43 * <p> 44 * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at 45 * your own risk. This code and its internal interfaces are subject to change or deletion without 46 * notice.</b> 47 */ 48public class ModuleNameReader { 49 public static class BadClassFile extends Exception { 50 private static final long serialVersionUID = 0; 51 BadClassFile(String msg) { 52 super(msg); 53 } 54 } 55 56 private static final int INITIAL_BUFFER_SIZE = 0x0fff0; 57 58 /** The buffer containing the currently read class file. 59 */ 60 private byte[] buf = new byte[INITIAL_BUFFER_SIZE]; 61 62 /** The current input pointer. 63 */ 64 private int bp; 65 66 /** For every constant pool entry, an index into buf where the 67 * defining section of the entry is found. 68 */ 69 private int[] poolIdx; 70 71 public ModuleNameReader() { 72 } 73 74 public String readModuleName(Path p) throws IOException, BadClassFile { 75 try (InputStream in = Files.newInputStream(p)) { 76 return readModuleName(in); 77 } 78 } 79 80 public String readModuleName(JavaFileObject jfo) throws IOException, BadClassFile { 81 try (InputStream in = jfo.openInputStream()) { 82 return readModuleName(in); 83 } 84 } 85 86 public String readModuleName(InputStream in) throws IOException, BadClassFile { 87 bp = 0; 88 buf = readInputStream(buf, in); 89 90 int magic = nextInt(); 91 if (magic != JAVA_MAGIC) 92 throw new BadClassFile("illegal.start.of.class.file"); 93 94 int minorVersion = nextChar(); 95 int majorVersion = nextChar(); 96 97 indexPool(); 98 99 int accessflags = nextChar(); 100 return readModuleInfoName(nextChar()); 101 } 102 103 /** Extract a character at position bp from buf. 104 */ 105 char getChar(int bp) { 106 return 107 (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF)); 108 } 109 110 /** Read a character. 111 */ 112 char nextChar() { 113 return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF)); 114 } 115 116 /** Read an integer. 117 */ 118 int nextInt() { 119 return 120 ((buf[bp++] & 0xFF) << 24) + 121 ((buf[bp++] & 0xFF) << 16) + 122 ((buf[bp++] & 0xFF) << 8) + 123 (buf[bp++] & 0xFF); 124 } 125 126 /** Index all constant pool entries, writing their start addresses into 127 * poolIdx. 128 */ 129 void indexPool() throws BadClassFile { 130 poolIdx = new int[nextChar()]; 131 int i = 1; 132 while (i < poolIdx.length) { 133 poolIdx[i++] = bp; 134 byte tag = buf[bp++]; 135 switch (tag) { 136 case CONSTANT_Utf8: case CONSTANT_Unicode: { 137 int len = nextChar(); 138 bp = bp + len; 139 break; 140 } 141 case CONSTANT_Class: 142 case CONSTANT_String: 143 case CONSTANT_MethodType: 144 bp = bp + 2; 145 break; 146 case CONSTANT_MethodHandle: 147 bp = bp + 3; 148 break; 149 case CONSTANT_Fieldref: 150 case CONSTANT_Methodref: 151 case CONSTANT_InterfaceMethodref: 152 case CONSTANT_NameandType: 153 case CONSTANT_Integer: 154 case CONSTANT_Float: 155 case CONSTANT_InvokeDynamic: 156 bp = bp + 4; 157 break; 158 case CONSTANT_Long: 159 case CONSTANT_Double: 160 bp = bp + 8; 161 i++; 162 break; 163 default: 164 throw new BadClassFile("malformed constant pool"); 165 } 166 } 167 } 168 169 /** Read the class name of a module-info.class file. 170 * The name is stored in a CONSTANT_Class entry, where the 171 * class name is of the form module-name/module-info. 172 */ 173 String readModuleInfoName(int i) throws BadClassFile { 174 int classIndex = poolIdx[i]; 175 if (buf[classIndex] == CONSTANT_Class) { 176 int utf8Index = poolIdx[getChar(classIndex + 1)]; 177 if (buf[utf8Index] == CONSTANT_Utf8) { 178 int len = getChar(utf8Index + 1); 179 int start = utf8Index + 3; 180 String suffix = "/module-info"; 181 if (endsWith(buf, start, len, suffix)) 182 return new String(ClassFile.internalize(buf, start, len - suffix.length())); 183 } 184 } 185 throw new BadClassFile("bad module-info name"); 186 } 187 188 private boolean endsWith(byte[] buf, int start, int len, String suffix) { 189 if (len <= suffix.length()) 190 return false; 191 for (int i = 0; i < suffix.length(); i++) { 192 if (buf[start + len - suffix.length() + i] != suffix.charAt(i)) 193 return false; 194 } 195 return true; 196 } 197 198 private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException { 199 try { 200 buf = ensureCapacity(buf, s.available()); 201 int r = s.read(buf); 202 int bp = 0; 203 while (r != -1) { 204 bp += r; 205 buf = ensureCapacity(buf, bp); 206 r = s.read(buf, bp, buf.length - bp); 207 } 208 return buf; 209 } finally { 210 try { 211 s.close(); 212 } catch (IOException e) { 213 /* Ignore any errors, as this stream may have already 214 * thrown a related exception which is the one that 215 * should be reported. 216 */ 217 } 218 } 219 } 220 221 /* 222 * ensureCapacity will increase the buffer as needed, taking note that 223 * the new buffer will always be greater than the needed and never 224 * exactly equal to the needed size or bp. If equal then the read (above) 225 * will infinitely loop as buf.length - bp == 0. 226 */ 227 private static byte[] ensureCapacity(byte[] buf, int needed) { 228 if (buf.length <= needed) { 229 byte[] old = buf; 230 buf = new byte[Integer.highestOneBit(needed) << 1]; 231 System.arraycopy(old, 0, buf, 0, old.length); 232 } 233 return buf; 234 } 235} 236