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