InstrumentationClassFinder to depend only on jdk classes

This commit is contained in:
Eugene Zhuravlev
2012-03-27 14:28:20 +02:00
parent b71edf4851
commit 6c9931df49
3 changed files with 422 additions and 382 deletions
@@ -1,365 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.java;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import sun.misc.Resource;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
class ClassFinderClasspath {
private static final String FILE_PROTOCOL = "file";
private final Stack<URL> myUrls = new Stack<URL>();
private final List<Loader> myLoaders = new ArrayList<Loader>();
private final Map<URL,Loader> myLoadersMap = new HashMap<URL, Loader>();
public ClassFinderClasspath(URL[] urls) {
push(urls);
}
@Nullable
public Resource getResource(String s, boolean flag) {
int i = 0;
for (Loader loader; (loader = getLoader(i)) != null; i++) {
Resource resource = loader.getResource(s, flag);
if (resource != null) {
return resource;
}
}
return null;
}
public void releaseResources() {
for (Loader loader : myLoaders) {
loader.releaseResources();
}
myLoaders.clear();
myLoadersMap.clear();
myUrls.clear();
}
@Nullable
private synchronized Loader getLoader(int i) {
while (myLoaders.size() < i + 1) {
URL url;
synchronized (myUrls) {
if (myUrls.empty()) {
return null;
}
url = myUrls.pop();
}
if (myLoadersMap.containsKey(url)) {
continue;
}
Loader loader;
try {
loader = getLoader(url, myLoaders.size());
if (loader == null) {
continue;
}
}
catch (IOException ioexception) {
continue;
}
myLoaders.add(loader);
myLoadersMap.put(url, loader);
}
return myLoaders.get(i);
}
@Nullable
private Loader getLoader(final URL url, int index) throws IOException {
String s;
try {
s = url.toURI().getSchemeSpecificPart();
}
catch (URISyntaxException thisShouldNotHappen) {
thisShouldNotHappen.printStackTrace();
s = url.getFile();
}
Loader loader = null;
if (s != null && new File(s).isDirectory()) {
if (FILE_PROTOCOL.equals(url.getProtocol())) {
loader = new FileLoader(url, index);
}
}
else {
loader = new JarLoader(url, index);
}
return loader;
}
private void push(URL[] urls) {
if (urls.length == 0) return;
synchronized (myUrls) {
for (int i = urls.length - 1; i >= 0; i--) {
myUrls.push(urls[i]);
}
}
}
private abstract static class Loader {
protected static final String JAR_PROTOCOL = "jar";
protected static final String FILE_PROTOCOL = "file";
private final URL myURL;
private final int myIndex;
protected Loader(URL url, int index) {
myURL = url;
myIndex = index;
}
protected URL getBaseURL() {
return myURL;
}
@Nullable
public abstract Resource getResource(final String name, boolean flag);
public abstract void releaseResources();
public int getIndex() {
return myIndex;
}
}
private static class FileLoader extends Loader {
private final File myRootDir;
@SuppressWarnings({"HardCodedStringLiteral"})
FileLoader(URL url, int index) throws IOException {
super(url, index);
if (!FILE_PROTOCOL.equals(url.getProtocol())) {
throw new IllegalArgumentException("url");
}
else {
final String s = FileUtil.unquote(url.getFile());
myRootDir = new File(s);
}
}
public void releaseResources() {
}
@Nullable
public Resource getResource(final String name, boolean check) {
URL url = null;
File file = null;
try {
url = new URL(getBaseURL(), name);
if (!url.getFile().startsWith(getBaseURL().getFile())) {
return null;
}
file = new File(myRootDir, name.replace('/', File.separatorChar));
if (!check || file.exists()) { // check means we load or process resource so we check its existence via old way
return new FileResource(name, url, file, !check);
}
}
catch (Exception exception) {
if (!check && file != null && file.exists()) {
try { // we can not open the file if it is directory, Resource still can be created
return new FileResource(name, url, file, false);
}
catch (IOException ex) {}
}
}
return null;
}
private class FileResource extends Resource {
private final String myName;
private final URL myUrl;
private final File myFile;
public FileResource(String name, URL url, File file, boolean willLoadBytes) throws IOException {
myName = name;
myUrl = url;
myFile = file;
if (willLoadBytes) getByteBuffer(); // check for existence by creating cached file input stream
}
public String getName() {
return myName;
}
public URL getURL() {
return myUrl;
}
public URL getCodeSourceURL() {
return getBaseURL();
}
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(myFile));
}
public int getContentLength() throws IOException {
return -1;
}
public String toString() {
return myFile.getAbsolutePath();
}
}
@NonNls
public String toString() {
return "FileLoader [" + myRootDir + "]";
}
}
private class JarLoader extends Loader {
private final URL myURL;
private ZipFile myZipFile;
JarLoader(URL url, int index) throws IOException {
super(new URL(JAR_PROTOCOL, "", -1, url + "!/"), index);
myURL = url;
}
public void releaseResources() {
final ZipFile zipFile = myZipFile;
if (zipFile != null) {
myZipFile = null;
try {
zipFile.close();
}
catch (IOException e) {
throw new RuntimeException();
}
}
}
@Nullable
private ZipFile acquireZipFile() throws IOException {
ZipFile zipFile = myZipFile;
if (zipFile == null) {
zipFile = doGetZipFile();
myZipFile = zipFile;
}
return zipFile;
}
@Nullable
private ZipFile doGetZipFile() throws IOException {
if (FILE_PROTOCOL.equals(myURL.getProtocol())) {
String s = FileUtil.unquote(myURL.getFile());
if (!new File(s).exists()) {
throw new FileNotFoundException(s);
}
else {
return new ZipFile(s);
}
}
return null;
}
@Nullable
public Resource getResource(String name, boolean flag) {
try {
final ZipFile file = acquireZipFile();
if (file != null) {
final ZipEntry entry = file.getEntry(name);
if (entry != null) {
return new JarResource(entry, new URL(getBaseURL(), name));
}
}
}
catch (Exception e) {
return null;
}
return null;
}
private class JarResource extends Resource {
private final ZipEntry myEntry;
private final URL myUrl;
public JarResource(ZipEntry name, URL url) {
myEntry = name;
myUrl = url;
}
public String getName() {
return myEntry.getName();
}
public URL getURL() {
return myUrl;
}
public URL getCodeSourceURL() {
return myURL;
}
@Nullable
public InputStream getInputStream() throws IOException {
ZipFile file = null;
try {
file = acquireZipFile();
if (file == null) {
return null;
}
final InputStream inputStream = file.getInputStream(myEntry);
if (inputStream == null) {
return null; // if entry was not found
}
return new FilterInputStream(inputStream) {};
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
public int getContentLength() {
return (int)myEntry.getSize();
}
}
@NonNls
public String toString() {
return "JarLoader [" + myURL + "]";
}
}
}
@@ -1,33 +1,33 @@
package org.jetbrains.jps.incremental.java;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.EmptyVisitor;
import sun.misc.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* @author Eugene Zhuravlev
* Date: 2/16/12
*/
class InstrumentationClassFinder {
public class InstrumentationClassFinder {
private static final PseudoClass[] EMPTY_PSEUDOCLASS_ARRAY = new PseudoClass[0];
private static final String CLASS_RESOURCE_EXTENSION = ".class";
private final Map<String, PseudoClass> myLoaded = new HashMap<String, PseudoClass>(); // className -> class object
private final ClassFinderClasspath myPlatformClasspath;
private final ClassFinderClasspath myClasspath;
private final OutputFilesSink myCompiled;
public InstrumentationClassFinder(final URL[] platformClasspath, final URL[] classpath, OutputFilesSink compiled) {
myCompiled = compiled;
public InstrumentationClassFinder(final URL[] platformClasspath, final URL[] classpath) {
myPlatformClasspath = new ClassFinderClasspath(platformClasspath);
myClasspath = new ClassFinderClasspath(classpath);
}
@@ -53,10 +53,7 @@ class InstrumentationClassFinder {
}
// second look into memory and classspath
if (is == null) {
final OutputFileObject.Content content = myCompiled.lookupClassBytes(internalName.replace("/", "."));
if (content != null) {
is = new ByteArrayInputStream(content.getBuffer(), content.getOffset(), content.getLength());
}
is = lookupClassBeforeClasspath(internalName);
}
if (is == null) {
@@ -65,7 +62,11 @@ class InstrumentationClassFinder {
is = resource.getInputStream();
}
}
if (is == null) {
is = lookupClassAfterClasspath(internalName);
}
if (is == null) {
throw new ClassNotFoundException("Class not found: " + internalName);
}
@@ -80,6 +81,16 @@ class InstrumentationClassFinder {
}
}
@Nullable
protected InputStream lookupClassBeforeClasspath(final String internalClassName) {
return null;
}
@Nullable
protected InputStream lookupClassAfterClasspath(final String internalClassName) {
return null;
}
private PseudoClass loadPseudoClass(InputStream is) throws IOException {
final ClassReader reader = new ClassReader(is);
final V visitor = new V();
@@ -194,4 +205,390 @@ class InstrumentationClassFinder {
myIsInterface = (access & Opcodes.ACC_INTERFACE) > 0;
}
}
static class ClassFinderClasspath {
private static final String FILE_PROTOCOL = "file";
private final Stack<URL> myUrls = new Stack<URL>();
private final List<Loader> myLoaders = new ArrayList<Loader>();
private final Map<URL,Loader> myLoadersMap = new HashMap<URL, Loader>();
public ClassFinderClasspath(URL[] urls) {
if (urls.length > 0) {
for (int i = urls.length - 1; i >= 0; i--) {
myUrls.push(urls[i]);
}
}
}
@Nullable
public Resource getResource(String s, boolean flag) {
int i = 0;
for (Loader loader; (loader = getLoader(i)) != null; i++) {
Resource resource = loader.getResource(s, flag);
if (resource != null) {
return resource;
}
}
return null;
}
public void releaseResources() {
for (Loader loader : myLoaders) {
loader.releaseResources();
}
myLoaders.clear();
myLoadersMap.clear();
}
@Nullable
private synchronized Loader getLoader(int i) {
while (myLoaders.size() < i + 1) {
URL url;
synchronized (myUrls) {
if (myUrls.empty()) {
return null;
}
url = myUrls.pop();
}
if (myLoadersMap.containsKey(url)) {
continue;
}
Loader loader;
try {
loader = getLoader(url, myLoaders.size());
if (loader == null) {
continue;
}
}
catch (IOException ioexception) {
continue;
}
myLoaders.add(loader);
myLoadersMap.put(url, loader);
}
return myLoaders.get(i);
}
@Nullable
private Loader getLoader(final URL url, int index) throws IOException {
String s;
try {
s = url.toURI().getSchemeSpecificPart();
}
catch (URISyntaxException thisShouldNotHappen) {
thisShouldNotHappen.printStackTrace();
s = url.getFile();
}
Loader loader = null;
if (s != null && new File(s).isDirectory()) {
if (FILE_PROTOCOL.equals(url.getProtocol())) {
loader = new FileLoader(url, index);
}
}
else {
loader = new JarLoader(url, index);
}
return loader;
}
private abstract static class Loader {
protected static final String JAR_PROTOCOL = "jar";
protected static final String FILE_PROTOCOL = "file";
private final URL myURL;
private final int myIndex;
protected Loader(URL url, int index) {
myURL = url;
myIndex = index;
}
protected URL getBaseURL() {
return myURL;
}
@Nullable
public abstract Resource getResource(final String name, boolean flag);
public abstract void releaseResources();
public int getIndex() {
return myIndex;
}
}
private static class FileLoader extends Loader {
private final File myRootDir;
@SuppressWarnings({"HardCodedStringLiteral"})
FileLoader(URL url, int index) throws IOException {
super(url, index);
if (!FILE_PROTOCOL.equals(url.getProtocol())) {
throw new IllegalArgumentException("url");
}
else {
final String s = unescapePercentSequences(url.getFile().replace('/', File.separatorChar));
myRootDir = new File(s);
}
}
public void releaseResources() {
}
@Nullable
public Resource getResource(final String name, boolean check) {
URL url = null;
File file = null;
try {
url = new URL(getBaseURL(), name);
if (!url.getFile().startsWith(getBaseURL().getFile())) {
return null;
}
file = new File(myRootDir, name.replace('/', File.separatorChar));
if (!check || file.exists()) { // check means we load or process resource so we check its existence via old way
return new FileResource(name, url, file, !check);
}
}
catch (Exception exception) {
if (!check && file != null && file.exists()) {
try { // we can not open the file if it is directory, Resource still can be created
return new FileResource(name, url, file, false);
}
catch (IOException ex) {
}
}
}
return null;
}
private class FileResource extends Resource {
private final String myName;
private final URL myUrl;
private final File myFile;
public FileResource(String name, URL url, File file, boolean willLoadBytes) throws IOException {
myName = name;
myUrl = url;
myFile = file;
if (willLoadBytes) getByteBuffer(); // check for existence by creating cached file input stream
}
public String getName() {
return myName;
}
public URL getURL() {
return myUrl;
}
public URL getCodeSourceURL() {
return getBaseURL();
}
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(myFile));
}
public int getContentLength() throws IOException {
return -1;
}
public String toString() {
return myFile.getAbsolutePath();
}
}
@NonNls
public String toString() {
return "FileLoader [" + myRootDir + "]";
}
}
private class JarLoader extends Loader {
private final URL myURL;
private ZipFile myZipFile;
JarLoader(URL url, int index) throws IOException {
super(new URL(JAR_PROTOCOL, "", -1, url + "!/"), index);
myURL = url;
}
public void releaseResources() {
final ZipFile zipFile = myZipFile;
if (zipFile != null) {
myZipFile = null;
try {
zipFile.close();
}
catch (IOException e) {
throw new RuntimeException();
}
}
}
@Nullable
private ZipFile acquireZipFile() throws IOException {
ZipFile zipFile = myZipFile;
if (zipFile == null) {
zipFile = doGetZipFile();
myZipFile = zipFile;
}
return zipFile;
}
@Nullable
private ZipFile doGetZipFile() throws IOException {
if (FILE_PROTOCOL.equals(myURL.getProtocol())) {
String s = unescapePercentSequences(myURL.getFile().replace('/', File.separatorChar));
if (!new File(s).exists()) {
throw new FileNotFoundException(s);
}
else {
return new ZipFile(s);
}
}
return null;
}
@Nullable
public Resource getResource(String name, boolean flag) {
try {
final ZipFile file = acquireZipFile();
if (file != null) {
final ZipEntry entry = file.getEntry(name);
if (entry != null) {
return new JarResource(entry, new URL(getBaseURL(), name));
}
}
}
catch (Exception e) {
return null;
}
return null;
}
private class JarResource extends Resource {
private final ZipEntry myEntry;
private final URL myUrl;
public JarResource(ZipEntry name, URL url) {
myEntry = name;
myUrl = url;
}
public String getName() {
return myEntry.getName();
}
public URL getURL() {
return myUrl;
}
public URL getCodeSourceURL() {
return myURL;
}
@Nullable
public InputStream getInputStream() throws IOException {
ZipFile file = null;
try {
file = acquireZipFile();
if (file == null) {
return null;
}
final InputStream inputStream = file.getInputStream(myEntry);
if (inputStream == null) {
return null; // if entry was not found
}
return new FilterInputStream(inputStream) {};
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
public int getContentLength() {
return (int)myEntry.getSize();
}
}
@NonNls
public String toString() {
return "JarLoader [" + myURL + "]";
}
}
}
@NotNull
private static String unescapePercentSequences(@NotNull String s) {
if (s.indexOf('%') == -1) {
return s;
}
StringBuilder decoded = new StringBuilder();
final int len = s.length();
int i = 0;
while (i < len) {
char c = s.charAt(i);
if (c == '%') {
List<Integer> bytes = new ArrayList<Integer>();
while (i + 2 < len && s.charAt(i) == '%') {
final int d1 = decode(s.charAt(i + 1));
final int d2 = decode(s.charAt(i + 2));
if (d1 != -1 && d2 != -1) {
bytes.add(((d1 & 0xf) << 4 | d2 & 0xf));
i += 3;
}
else {
break;
}
}
if (!bytes.isEmpty()) {
final byte[] bytesArray = new byte[bytes.size()];
for (int j = 0; j < bytes.size(); j++) {
bytesArray[j] = (byte)bytes.get(j).intValue();
}
try {
decoded.append(new String(bytesArray, "UTF-8"));
continue;
}
catch (UnsupportedEncodingException ignored) {
}
}
}
decoded.append(c);
i++;
}
return decoded.toString();
}
private static int decode(char c) {
if ((c >= '0') && (c <= '9')){
return c - '0';
}
if ((c >= 'a') && (c <= 'f')){
return c - 'a' + 10;
}
if ((c >= 'A') && (c <= 'F')){
return c - 'A' + 10;
}
return -1;
}
}
@@ -529,7 +529,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
private static InstrumentationClassFinder createInstrumentationClassFinder(Collection<File> platformCp,
Collection<File> classpath,
OutputFilesSink outputSink) throws MalformedURLException {
final OutputFilesSink outputSink) throws MalformedURLException {
final URL[] platformUrls = new URL[platformCp.size()];
int index = 0;
for (File file : platformCp) {
@@ -544,7 +544,15 @@ public class JavaBuilder extends ModuleLevelBuilder {
urls[index++] = getResourcePath(GridConstraints.class).toURI().toURL(); // forms_rt.jar
//urls.add(getResourcePath(CellConstraints.class).toURI().toURL()); // jgoodies-forms
return new InstrumentationClassFinder(platformUrls, urls, outputSink);
return new InstrumentationClassFinder(platformUrls, urls) {
protected InputStream lookupClassBeforeClasspath(String internalClassName) {
final OutputFileObject.Content content = outputSink.lookupClassBytes(internalClassName.replace("/", "."));
if (content != null) {
return new ByteArrayInputStream(content.getBuffer(), content.getOffset(), content.getLength());
}
return null;
}
};
}
private static ClassLoader createInstrumentationClassLoader(Collection<File> platformCp, Collection<File> classpath,