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 */ 25 package com.sun.tools.javac.jvm; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 32 import javax.tools.JavaFileObject; 33 34 import 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 */ 46 public 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 int this_class = nextChar(); 104 // could, should, check this_class == CONSTANT_Class("mdoule-info") 105 checkZero(nextChar(), "super_class"); 106 checkZero(nextChar(), "interface_count"); 107 checkZero(nextChar(), "fields_count"); 108 checkZero(nextChar(), "methods_count"); 109 int attributes_count = nextChar(); 110 for (int i = 0; i < attributes_count; i++) { 111 int attr_name = nextChar(); 112 int attr_length = nextInt(); 113 if (getUtf8Value(attr_name, false).equals("Module") && attr_length > 2) { 114 return getModuleName(nextChar()); 115 } else { 116 // skip over unknown attributes 117 bp += attr_length; 118 } 119 } 120 throw new BadClassFile("no Module attribute"); 121 } 122 123 void checkZero(int count, String name) throws BadClassFile { 124 if (count != 0) 125 throw new BadClassFile("invalid " + name + " for module: " + count); 126 } 127 128 /** Extract a character at position bp from buf. 129 */ 130 char getChar(int bp) { 131 return 132 (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF)); 133 } 134 135 /** Read a character. 136 */ 137 char nextChar() { 138 return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF)); 139 } 140 141 /** Read an integer. 142 */ 143 int nextInt() { 144 return 145 ((buf[bp++] & 0xFF) << 24) + 146 ((buf[bp++] & 0xFF) << 16) + 147 ((buf[bp++] & 0xFF) << 8) + 148 (buf[bp++] & 0xFF); 149 } 150 151 /** Index all constant pool entries, writing their start addresses into 152 * poolIdx. 153 */ 154 void indexPool() throws BadClassFile { 155 poolIdx = new int[nextChar()]; 156 int i = 1; 157 while (i < poolIdx.length) { 158 poolIdx[i++] = bp; 159 byte tag = buf[bp++]; 160 switch (tag) { 161 case CONSTANT_Utf8: case CONSTANT_Unicode: { 162 int len = nextChar(); 163 bp = bp + len; 164 break; 165 } 166 case CONSTANT_Class: 167 case CONSTANT_String: 168 case CONSTANT_MethodType: 169 case CONSTANT_Module: 170 case CONSTANT_Package: 171 bp = bp + 2; 172 break; 173 case CONSTANT_MethodHandle: 174 bp = bp + 3; 175 break; 176 case CONSTANT_Fieldref: 177 case CONSTANT_Methodref: 178 case CONSTANT_InterfaceMethodref: 179 case CONSTANT_NameandType: 180 case CONSTANT_Integer: 181 case CONSTANT_Float: 182 case CONSTANT_InvokeDynamic: 183 bp = bp + 4; 184 break; 185 case CONSTANT_Long: 186 case CONSTANT_Double: 187 bp = bp + 8; 188 i++; 189 break; 190 default: 191 throw new BadClassFile("malformed constant pool"); 192 } 193 } 194 } 195 196 String getUtf8Value(int index, boolean internalize) throws BadClassFile { 197 int utf8Index = poolIdx[index]; 198 if (buf[utf8Index] == CONSTANT_Utf8) { 199 int len = getChar(utf8Index + 1); 200 int start = utf8Index + 3; 201 if (internalize) { 202 return new String(ClassFile.internalize(buf, start, len)); 203 } else { 204 return new String(buf, start, len); 205 } 206 } 207 throw new BadClassFile("bad name at index " + index); 208 } 209 210 String getModuleName(int index) throws BadClassFile { 211 int infoIndex = poolIdx[index]; 212 if (buf[infoIndex] == CONSTANT_Module) { 213 return getUtf8Value(getChar(infoIndex + 1), true); 214 } else { 215 throw new BadClassFile("bad module name at index " + index); 216 } 217 } 218 219 private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException { 220 try { 221 buf = ensureCapacity(buf, s.available()); 222 int r = s.read(buf); 223 int bp = 0; 224 while (r != -1) { 225 bp += r; 226 buf = ensureCapacity(buf, bp); 227 r = s.read(buf, bp, buf.length - bp); 228 } 229 return buf; 230 } finally { 231 try { 232 s.close(); 233 } catch (IOException e) { 234 /* Ignore any errors, as this stream may have already 235 * thrown a related exception which is the one that 236 * should be reported. 237 */ 238 } 239 } 240 } 241 242 /* 243 * ensureCapacity will increase the buffer as needed, taking note that 244 * the new buffer will always be greater than the needed and never 245 * exactly equal to the needed size or bp. If equal then the read (above) 246 * will infinitely loop as buf.length - bp == 0. 247 */ 248 private static byte[] ensureCapacity(byte[] buf, int needed) { 249 if (buf.length <= needed) { 250 byte[] old = buf; 251 buf = new byte[Integer.highestOneBit(needed) << 1]; 252 System.arraycopy(old, 0, buf, 0, old.length); 253 } 254 return buf; 255 } 256 }