Wed, 30 Apr 2014 23:26:43 +0100
8038023: Compiler crash ClassCastException
Summary: Add additional checks on results of ClassReader.readPool
Reviewed-by: vromero
jjg@450 | 1 | /* |
jjg@1357 | 2 | * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
jjg@450 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@450 | 4 | * |
jjg@450 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@450 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@554 | 7 | * published by the Free Software Foundation. Oracle designates this |
jjg@450 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@554 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jjg@450 | 10 | * |
jjg@450 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@450 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@450 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@450 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@450 | 15 | * accompanied this code). |
jjg@450 | 16 | * |
jjg@450 | 17 | * You should have received a copy of the GNU General Public License version |
jjg@450 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@450 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@450 | 20 | * |
ohair@554 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@554 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@554 | 23 | * questions. |
jjg@450 | 24 | */ |
jjg@450 | 25 | |
jjg@450 | 26 | package com.sun.tools.javac.nio; |
jjg@450 | 27 | |
jjg@450 | 28 | import java.io.IOException; |
jjg@450 | 29 | import java.io.InputStream; |
jjg@450 | 30 | import java.io.InputStreamReader; |
jjg@450 | 31 | import java.io.OutputStream; |
jjg@450 | 32 | import java.io.OutputStreamWriter; |
jjg@450 | 33 | import java.io.Reader; |
jjg@450 | 34 | import java.io.Writer; |
jjg@450 | 35 | import java.net.URI; |
jjg@450 | 36 | import java.nio.ByteBuffer; |
jjg@450 | 37 | import java.nio.CharBuffer; |
jjg@450 | 38 | import java.nio.charset.CharsetDecoder; |
jjg@450 | 39 | import java.nio.file.Files; |
alanb@959 | 40 | import java.nio.file.LinkOption; |
jjg@450 | 41 | import java.nio.file.Path; |
jjg@450 | 42 | import javax.lang.model.element.Modifier; |
jjg@450 | 43 | import javax.lang.model.element.NestingKind; |
jjg@450 | 44 | import javax.tools.JavaFileObject; |
jjg@450 | 45 | |
jjg@450 | 46 | import com.sun.tools.javac.util.BaseFileManager; |
jjg@450 | 47 | |
jjg@450 | 48 | |
jjg@450 | 49 | /** |
jjg@450 | 50 | * Implementation of JavaFileObject using java.nio.file API. |
jjg@450 | 51 | * |
jjg@450 | 52 | * <p>PathFileObjects are, for the most part, straightforward wrappers around |
jjg@450 | 53 | * Path objects. The primary complexity is the support for "inferBinaryName". |
jjg@450 | 54 | * This is left as an abstract method, implemented by each of a number of |
jjg@450 | 55 | * different factory methods, which compute the binary name based on |
jjg@450 | 56 | * information available at the time the file object is created. |
jjg@450 | 57 | * |
jjg@581 | 58 | * <p><b>This is NOT part of any supported API. |
jjg@581 | 59 | * If you write code that depends on this, you do so at your own risk. |
jjg@450 | 60 | * This code and its internal interfaces are subject to change or |
jjg@450 | 61 | * deletion without notice.</b> |
jjg@450 | 62 | */ |
jjg@450 | 63 | abstract class PathFileObject implements JavaFileObject { |
jjg@450 | 64 | private JavacPathFileManager fileManager; |
jjg@450 | 65 | private Path path; |
jjg@450 | 66 | |
jjg@450 | 67 | /** |
jjg@450 | 68 | * Create a PathFileObject within a directory, such that the binary name |
jjg@450 | 69 | * can be inferred from the relationship to the parent directory. |
jjg@450 | 70 | */ |
jjg@450 | 71 | static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, |
jjg@450 | 72 | final Path path, final Path dir) { |
jjg@450 | 73 | return new PathFileObject(fileManager, path) { |
jjg@450 | 74 | @Override |
jjg@450 | 75 | String inferBinaryName(Iterable<? extends Path> paths) { |
jjg@450 | 76 | return toBinaryName(dir.relativize(path)); |
jjg@450 | 77 | } |
jjg@450 | 78 | }; |
jjg@450 | 79 | } |
jjg@450 | 80 | |
jjg@450 | 81 | /** |
jjg@450 | 82 | * Create a PathFileObject in a file system such as a jar file, such that |
jjg@450 | 83 | * the binary name can be inferred from its position within the filesystem. |
jjg@450 | 84 | */ |
jjg@450 | 85 | static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, |
jjg@450 | 86 | final Path path) { |
jjg@450 | 87 | return new PathFileObject(fileManager, path) { |
jjg@450 | 88 | @Override |
jjg@450 | 89 | String inferBinaryName(Iterable<? extends Path> paths) { |
jjg@450 | 90 | return toBinaryName(path); |
jjg@450 | 91 | } |
jjg@450 | 92 | }; |
jjg@450 | 93 | } |
jjg@450 | 94 | |
jjg@450 | 95 | /** |
jjg@450 | 96 | * Create a PathFileObject whose binary name can be inferred from the |
jjg@450 | 97 | * relative path to a sibling. |
jjg@450 | 98 | */ |
jjg@450 | 99 | static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, |
jjg@450 | 100 | final Path path, final String relativePath) { |
jjg@450 | 101 | return new PathFileObject(fileManager, path) { |
jjg@450 | 102 | @Override |
jjg@450 | 103 | String inferBinaryName(Iterable<? extends Path> paths) { |
jjg@450 | 104 | return toBinaryName(relativePath, "/"); |
jjg@450 | 105 | } |
jjg@450 | 106 | }; |
jjg@450 | 107 | } |
jjg@450 | 108 | |
jjg@450 | 109 | /** |
jjg@450 | 110 | * Create a PathFileObject whose binary name might be inferred from its |
jjg@450 | 111 | * position on a search path. |
jjg@450 | 112 | */ |
jjg@450 | 113 | static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, |
jjg@450 | 114 | final Path path) { |
jjg@450 | 115 | return new PathFileObject(fileManager, path) { |
jjg@450 | 116 | @Override |
jjg@450 | 117 | String inferBinaryName(Iterable<? extends Path> paths) { |
jjg@450 | 118 | Path absPath = path.toAbsolutePath(); |
jjg@450 | 119 | for (Path p: paths) { |
jjg@450 | 120 | Path ap = p.toAbsolutePath(); |
jjg@450 | 121 | if (absPath.startsWith(ap)) { |
jjg@450 | 122 | try { |
jjg@450 | 123 | Path rp = ap.relativize(absPath); |
jjg@450 | 124 | if (rp != null) // maybe null if absPath same as ap |
jjg@450 | 125 | return toBinaryName(rp); |
jjg@450 | 126 | } catch (IllegalArgumentException e) { |
jjg@450 | 127 | // ignore this p if cannot relativize path to p |
jjg@450 | 128 | } |
jjg@450 | 129 | } |
jjg@450 | 130 | } |
jjg@450 | 131 | return null; |
jjg@450 | 132 | } |
jjg@450 | 133 | }; |
jjg@450 | 134 | } |
jjg@450 | 135 | |
jjg@450 | 136 | protected PathFileObject(JavacPathFileManager fileManager, Path path) { |
jjg@450 | 137 | fileManager.getClass(); // null check |
jjg@450 | 138 | path.getClass(); // null check |
jjg@450 | 139 | this.fileManager = fileManager; |
jjg@450 | 140 | this.path = path; |
jjg@450 | 141 | } |
jjg@450 | 142 | |
jjg@450 | 143 | abstract String inferBinaryName(Iterable<? extends Path> paths); |
jjg@450 | 144 | |
jjg@450 | 145 | /** |
jjg@450 | 146 | * Return the Path for this object. |
jjg@450 | 147 | * @return the Path for this object. |
jjg@450 | 148 | */ |
jjg@450 | 149 | Path getPath() { |
jjg@450 | 150 | return path; |
jjg@450 | 151 | } |
jjg@450 | 152 | |
jjg@450 | 153 | @Override |
jjg@450 | 154 | public Kind getKind() { |
alanb@847 | 155 | return BaseFileManager.getKind(path.getFileName().toString()); |
jjg@450 | 156 | } |
jjg@450 | 157 | |
jjg@450 | 158 | @Override |
jjg@450 | 159 | public boolean isNameCompatible(String simpleName, Kind kind) { |
jjg@450 | 160 | simpleName.getClass(); |
jjg@450 | 161 | // null check |
jjg@450 | 162 | if (kind == Kind.OTHER && getKind() != kind) { |
jjg@450 | 163 | return false; |
jjg@450 | 164 | } |
jjg@450 | 165 | String sn = simpleName + kind.extension; |
alanb@847 | 166 | String pn = path.getFileName().toString(); |
jjg@450 | 167 | if (pn.equals(sn)) { |
jjg@450 | 168 | return true; |
jjg@450 | 169 | } |
jjg@450 | 170 | if (pn.equalsIgnoreCase(sn)) { |
jjg@450 | 171 | try { |
jjg@450 | 172 | // allow for Windows |
alanb@959 | 173 | return path.toRealPath(LinkOption.NOFOLLOW_LINKS).getFileName().toString().equals(sn); |
jjg@450 | 174 | } catch (IOException e) { |
jjg@450 | 175 | } |
jjg@450 | 176 | } |
jjg@450 | 177 | return false; |
jjg@450 | 178 | } |
jjg@450 | 179 | |
jjg@450 | 180 | @Override |
jjg@450 | 181 | public NestingKind getNestingKind() { |
jjg@450 | 182 | return null; |
jjg@450 | 183 | } |
jjg@450 | 184 | |
jjg@450 | 185 | @Override |
jjg@450 | 186 | public Modifier getAccessLevel() { |
jjg@450 | 187 | return null; |
jjg@450 | 188 | } |
jjg@450 | 189 | |
jjg@450 | 190 | @Override |
jjg@450 | 191 | public URI toUri() { |
jjg@450 | 192 | return path.toUri(); |
jjg@450 | 193 | } |
jjg@450 | 194 | |
jjg@450 | 195 | @Override |
jjg@450 | 196 | public String getName() { |
jjg@450 | 197 | return path.toString(); |
jjg@450 | 198 | } |
jjg@450 | 199 | |
jjg@450 | 200 | @Override |
jjg@450 | 201 | public InputStream openInputStream() throws IOException { |
alanb@847 | 202 | return Files.newInputStream(path); |
jjg@450 | 203 | } |
jjg@450 | 204 | |
jjg@450 | 205 | @Override |
jjg@450 | 206 | public OutputStream openOutputStream() throws IOException { |
jjg@1080 | 207 | fileManager.flushCache(this); |
jjg@450 | 208 | ensureParentDirectoriesExist(); |
alanb@847 | 209 | return Files.newOutputStream(path); |
jjg@450 | 210 | } |
jjg@450 | 211 | |
jjg@450 | 212 | @Override |
jjg@450 | 213 | public Reader openReader(boolean ignoreEncodingErrors) throws IOException { |
jjg@450 | 214 | CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); |
jjg@450 | 215 | return new InputStreamReader(openInputStream(), decoder); |
jjg@450 | 216 | } |
jjg@450 | 217 | |
jjg@450 | 218 | @Override |
jjg@450 | 219 | public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { |
jjg@450 | 220 | CharBuffer cb = fileManager.getCachedContent(this); |
jjg@450 | 221 | if (cb == null) { |
jjg@450 | 222 | InputStream in = openInputStream(); |
jjg@450 | 223 | try { |
jjg@450 | 224 | ByteBuffer bb = fileManager.makeByteBuffer(in); |
jjg@450 | 225 | JavaFileObject prev = fileManager.log.useSource(this); |
jjg@450 | 226 | try { |
jjg@450 | 227 | cb = fileManager.decode(bb, ignoreEncodingErrors); |
jjg@450 | 228 | } finally { |
jjg@450 | 229 | fileManager.log.useSource(prev); |
jjg@450 | 230 | } |
jjg@450 | 231 | fileManager.recycleByteBuffer(bb); |
jjg@450 | 232 | if (!ignoreEncodingErrors) { |
jjg@450 | 233 | fileManager.cache(this, cb); |
jjg@450 | 234 | } |
jjg@450 | 235 | } finally { |
jjg@450 | 236 | in.close(); |
jjg@450 | 237 | } |
jjg@450 | 238 | } |
jjg@450 | 239 | return cb; |
jjg@450 | 240 | } |
jjg@450 | 241 | |
jjg@450 | 242 | @Override |
jjg@450 | 243 | public Writer openWriter() throws IOException { |
jjg@1080 | 244 | fileManager.flushCache(this); |
jjg@450 | 245 | ensureParentDirectoriesExist(); |
alanb@847 | 246 | return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); |
jjg@450 | 247 | } |
jjg@450 | 248 | |
jjg@450 | 249 | @Override |
jjg@450 | 250 | public long getLastModified() { |
jjg@450 | 251 | try { |
alanb@847 | 252 | return Files.getLastModifiedTime(path).toMillis(); |
jjg@450 | 253 | } catch (IOException e) { |
jjg@450 | 254 | return -1; |
jjg@450 | 255 | } |
jjg@450 | 256 | } |
jjg@450 | 257 | |
jjg@450 | 258 | @Override |
jjg@450 | 259 | public boolean delete() { |
jjg@450 | 260 | try { |
alanb@847 | 261 | Files.delete(path); |
jjg@450 | 262 | return true; |
jjg@450 | 263 | } catch (IOException e) { |
jjg@450 | 264 | return false; |
jjg@450 | 265 | } |
jjg@450 | 266 | } |
jjg@450 | 267 | |
jjg@450 | 268 | public boolean isSameFile(PathFileObject other) { |
jjg@450 | 269 | try { |
alanb@847 | 270 | return Files.isSameFile(path, other.path); |
jjg@450 | 271 | } catch (IOException e) { |
jjg@450 | 272 | return false; |
jjg@450 | 273 | } |
jjg@450 | 274 | } |
jjg@450 | 275 | |
jjg@450 | 276 | @Override |
jjg@450 | 277 | public boolean equals(Object other) { |
jjg@450 | 278 | return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); |
jjg@450 | 279 | } |
jjg@450 | 280 | |
jjg@450 | 281 | @Override |
jjg@450 | 282 | public int hashCode() { |
jjg@450 | 283 | return path.hashCode(); |
jjg@450 | 284 | } |
jjg@450 | 285 | |
jjg@450 | 286 | @Override |
jjg@450 | 287 | public String toString() { |
jjg@450 | 288 | return getClass().getSimpleName() + "[" + path + "]"; |
jjg@450 | 289 | } |
jjg@450 | 290 | |
jjg@450 | 291 | private void ensureParentDirectoriesExist() throws IOException { |
jjg@450 | 292 | Path parent = path.getParent(); |
jjg@450 | 293 | if (parent != null) |
jjg@450 | 294 | Files.createDirectories(parent); |
jjg@450 | 295 | } |
jjg@450 | 296 | |
jjg@450 | 297 | private long size() { |
jjg@450 | 298 | try { |
alanb@847 | 299 | return Files.size(path); |
jjg@450 | 300 | } catch (IOException e) { |
jjg@450 | 301 | return -1; |
jjg@450 | 302 | } |
jjg@450 | 303 | } |
jjg@450 | 304 | |
jjg@450 | 305 | protected static String toBinaryName(Path relativePath) { |
jjg@450 | 306 | return toBinaryName(relativePath.toString(), |
jjg@450 | 307 | relativePath.getFileSystem().getSeparator()); |
jjg@450 | 308 | } |
jjg@450 | 309 | |
jjg@450 | 310 | protected static String toBinaryName(String relativePath, String sep) { |
jjg@466 | 311 | return removeExtension(relativePath).replace(sep, "."); |
jjg@450 | 312 | } |
jjg@450 | 313 | |
jjg@450 | 314 | protected static String removeExtension(String fileName) { |
jjg@450 | 315 | int lastDot = fileName.lastIndexOf("."); |
jjg@450 | 316 | return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); |
jjg@450 | 317 | } |
jjg@450 | 318 | } |