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