instrumenters: support resolution against java9 platform classes (IDEA-153804)

This commit is contained in:
Eugene Zhuravlev
2016-04-18 14:43:24 +02:00
parent 2e7032ff0d
commit c570e61f3b
6 changed files with 244 additions and 137 deletions
+2 -1
View File
@@ -114,6 +114,7 @@
<module fileurl="file://$PROJECT_DIR$/platform/indexing-api/indexing-api.iml" filepath="$PROJECT_DIR$/platform/indexing-api/indexing-api.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/indexing-impl/indexing-impl.iml" filepath="$PROJECT_DIR$/platform/indexing-impl/indexing-impl.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/java/compiler/instrumentation-util/instrumentation-util.iml" filepath="$PROJECT_DIR$/java/compiler/instrumentation-util/instrumentation-util.iml" group="java/compiler" />
<module fileurl="file://$PROJECT_DIR$/java/compiler/instrumentation-util-8/instrumentation-util-8.iml" filepath="$PROJECT_DIR$/java/compiler/instrumentation-util-8/instrumentation-util-8.iml" group="java/compiler" />
<module fileurl="file://$PROJECT_DIR$/plugins/IntelliLang/intellilang-jps-plugin/intellilang-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/IntelliLang/intellilang-jps-plugin/intellilang-jps-plugin.iml" group="jps" />
<module fileurl="file://$PROJECT_DIR$/python/ipnb/ipnb.iml" filepath="$PROJECT_DIR$/python/ipnb/ipnb.iml" group="python" />
<module fileurl="file://$PROJECT_DIR$/java/java-analysis-api/java-analysis-api.iml" filepath="$PROJECT_DIR$/java/java-analysis-api/java-analysis-api.iml" group="java" />
@@ -262,11 +263,11 @@
<module fileurl="file://$PROJECT_DIR$/platform/vcs-api/vcs-api.iml" filepath="$PROJECT_DIR$/platform/vcs-api/vcs-api.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-api/vcs-api-core/vcs-api-core.iml" filepath="$PROJECT_DIR$/platform/vcs-api/vcs-api-core/vcs-api-core.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-impl/vcs-impl.iml" filepath="$PROJECT_DIR$/platform/vcs-impl/vcs-impl.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-tests/vcs-tests.iml" filepath="$PROJECT_DIR$/platform/vcs-tests/vcs-tests.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-log/api/vcs-log-api.iml" filepath="$PROJECT_DIR$/platform/vcs-log/api/vcs-log-api.iml" group="platform/vcs-log" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-log/graph/vcs-log-graph.iml" filepath="$PROJECT_DIR$/platform/vcs-log/graph/vcs-log-graph.iml" group="platform/vcs-log" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-log/graph-api/vcs-log-graph-api.iml" filepath="$PROJECT_DIR$/platform/vcs-log/graph-api/vcs-log-graph-api.iml" group="platform/vcs-log" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-log/impl/vcs-log-impl.iml" filepath="$PROJECT_DIR$/platform/vcs-log/impl/vcs-log-impl.iml" group="platform/vcs-log" />
<module fileurl="file://$PROJECT_DIR$/platform/vcs-tests/vcs-tests.iml" filepath="$PROJECT_DIR$/platform/vcs-tests/vcs-tests.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/xdebugger-api/xdebugger-api.iml" filepath="$PROJECT_DIR$/platform/xdebugger-api/xdebugger-api.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/xdebugger-impl/xdebugger-impl.iml" filepath="$PROJECT_DIR$/platform/xdebugger-impl/xdebugger-impl.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/xml/impl/xml.iml" filepath="$PROJECT_DIR$/xml/impl/xml.iml" group="xml" />
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/../../../../out/classes/production/instrumentation-util" />
<output-test url="file://$MODULE_DIR$/../../../../out/classes/test/instrumentation-util" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="instrumentation-util" />
</component>
</module>
@@ -0,0 +1,102 @@
/*
* Copyright 2000-2016 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 com.intellij.compiler.instrumentation;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
/**
* Invoked via reflection from InstrumentationClassFinder
*/
public class JrtLoader extends InstrumentationClassFinder.ClassFinderClasspath.Loader{
private static final URI ROOT_URI = URI.create("jrt:/");
private static final InstrumentationClassFinder.Resource NULL_RESOURCE = ()-> null;
private List<Path> myRoots;
private final Map<String, InstrumentationClassFinder.Resource> myCache = new HashMap<>();
public JrtLoader(URL url, int index) {
super(url, index);
}
@Override
public InstrumentationClassFinder.Resource getResource(String name) {
final InstrumentationClassFinder.Resource cached = myCache.get(name);
if (cached != null) {
return cached != NULL_RESOURCE ? cached : null;
}
try {
for (Path root : getRoots()) {
final Path path = root.resolve(name);
if (Files.exists(path)) {
final InstrumentationClassFinder.Resource res = () -> Files.newInputStream(path, StandardOpenOption.READ);
myCache.put(name, res);
return res;
}
}
}
catch (IOException e) {
e.printStackTrace();
}
myCache.put(name, NULL_RESOURCE);
return null;
}
@Override
public synchronized void releaseResources() {
myCache.clear();
final List<Path> roots = myRoots;
if (roots != null) {
try {
if (!roots.isEmpty()) {
roots.iterator().next().getFileSystem().close();
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
roots.clear();
myRoots = null;
}
}
}
private synchronized List<Path> getRoots() throws IOException {
final List<Path> cached = myRoots;
if (cached != null) {
return cached;
}
final FileSystem fs = FileSystems.newFileSystem(ROOT_URI, Collections.singletonMap("java.home", getBaseURL().getPath()));
final List<Path> roots = new ArrayList<>();
final Path modulesDir = fs.getPath("modules");
Files.walkFileTree(modulesDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!modulesDir.equals(dir)) {
roots.add(dir);
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
});
myRoots = roots;
return roots;
}
}
@@ -4,9 +4,9 @@ import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import sun.misc.Resource;
import java.io.*;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -152,9 +152,9 @@ public class InstrumentationClassFinder {
InputStream is = null;
// first look into platformCp
final String resourceName = internalName + CLASS_RESOURCE_EXTENSION;
Resource resource = myPlatformClasspath.getResource(resourceName, false);
Resource resource = myPlatformClasspath.getResource(resourceName);
if (resource != null) {
is = new ByteArrayInputStream(resource.getBytes());
is = resource.getInputStream();
}
// second look into memory and classspath
if (is == null) {
@@ -162,9 +162,9 @@ public class InstrumentationClassFinder {
}
if (is == null) {
resource = myClasspath.getResource(resourceName, false);
resource = myClasspath.getResource(resourceName);
if (resource != null) {
is = new ByteArrayInputStream(resource.getBytes());
is = resource.getInputStream();
}
}
@@ -177,15 +177,15 @@ public class InstrumentationClassFinder {
public InputStream getResourceAsStream(String resourceName) throws IOException {
InputStream is = null;
Resource resource = myPlatformClasspath.getResource(resourceName, false);
Resource resource = myPlatformClasspath.getResource(resourceName);
if (resource != null) {
is = new ByteArrayInputStream(resource.getBytes());
is = resource.getInputStream();
}
if (is == null) {
resource = myClasspath.getResource(resourceName, false);
resource = myClasspath.getResource(resourceName);
if (resource != null) {
is = new ByteArrayInputStream(resource.getBytes());
is = resource.getInputStream();
}
}
@@ -433,8 +433,11 @@ public class InstrumentationClassFinder {
}
}
public interface Resource {
InputStream getInputStream() throws IOException;
}
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>();
@@ -448,10 +451,10 @@ public class InstrumentationClassFinder {
}
}
public Resource getResource(String s, boolean flag) {
public Resource getResource(String s) {
int i = 0;
for (Loader loader; (loader = getLoader(i)) != null; i++) {
Resource resource = loader.getResource(s, flag);
Resource resource = loader.getResource(s);
if (resource != null) {
return resource;
}
@@ -500,7 +503,7 @@ public class InstrumentationClassFinder {
return myLoaders.get(i);
}
private Loader getLoader(final URL url, int index) throws IOException {
private static Loader getLoader(final URL url, int index) throws IOException {
String s;
try {
s = url.toURI().getSchemeSpecificPart();
@@ -510,23 +513,28 @@ public class InstrumentationClassFinder {
s = url.getFile();
}
Loader loader = null;
if (s != null && new File(s).isDirectory()) {
if (FILE_PROTOCOL.equals(url.getProtocol())) {
loader = new FileLoader(url, index);
if (s != null && s.length() > 0) {
final String protocol = url.getProtocol();
if (Loader.JRT_PROTOCOL.equals(protocol)) {
final Loader jrtLoader = JrtClassHolder.create(url, index);
if (jrtLoader != null) {
return jrtLoader;
}
}
if (new File(s).isDirectory()) {
return Loader.FILE_PROTOCOL.equals(protocol) ? new FileLoader(url, index) : null;
}
}
else {
loader = new JarLoader(url, index);
}
return loader;
// by default treat the url as a jar archive
return new JarLoader(url, index);
}
private abstract static class Loader {
abstract static class Loader {
protected static final String JAR_PROTOCOL = "jar";
protected static final String FILE_PROTOCOL = "file";
protected static final String JRT_PROTOCOL = "jrt";
private final URL myURL;
private final int myIndex;
@@ -541,7 +549,7 @@ public class InstrumentationClassFinder {
return myURL;
}
public abstract Resource getResource(final String name, boolean flag);
public abstract Resource getResource(final String name);
public abstract void releaseResources();
@@ -568,76 +576,38 @@ public class InstrumentationClassFinder {
public void releaseResources() {
}
public Resource getResource(final String name, boolean check) {
URL url = null;
File file = null;
public Resource getResource(final String name) {
try {
url = new URL(getBaseURL(), name);
final URL 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);
final File file = new File(myRootDir, name.replace('/', File.separatorChar));
if (file.exists()) {
return new Resource() {
@Override
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new FileInputStream(file));
}
public String toString() {
return file.getAbsolutePath();
}
};
}
}
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) {
}
}
catch (Exception ignored) {
}
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();
}
}
public String toString() {
return "FileLoader [" + myRootDir + "]";
}
}
private class JarLoader extends Loader {
private static class JarLoader extends Loader {
private final URL myURL;
private ZipFile myZipFile;
@@ -674,21 +644,40 @@ public class InstrumentationClassFinder {
if (!new File(s).exists()) {
throw new FileNotFoundException(s);
}
else {
return new ZipFile(s);
}
return new ZipFile(s);
}
return null;
}
public Resource getResource(String name, boolean flag) {
public Resource getResource(String name) {
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));
return new Resource() {
@Override
public InputStream getInputStream() throws IOException {
try {
final ZipFile file = acquireZipFile();
if (file != null) {
final InputStream inputStream = file.getInputStream(entry);
if (inputStream != null) {
return new FilterInputStream(inputStream) {};
}
}
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String toString() {
return "JarLoader [" + myURL + "!/" + entry.getName() + "]";
}
};
}
}
}
@@ -698,54 +687,6 @@ public class InstrumentationClassFinder {
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;
}
public InputStream getInputStream() throws IOException {
try {
final ZipFile 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();
}
}
public String toString() {
return "JarLoader [" + myURL + "]";
}
}
}
@@ -828,4 +769,34 @@ public class InstrumentationClassFinder {
return result.toByteArray();
}
private static final class JrtClassHolder {
public static final Class<? extends ClassFinderClasspath.Loader> ourClass;
public static final Constructor<? extends ClassFinderClasspath.Loader> ourConstructor;
static {
Class<? extends ClassFinderClasspath.Loader> aClass = null;
Constructor<? extends ClassFinderClasspath.Loader> constructor = null;
try {
aClass = (Class<? extends ClassFinderClasspath.Loader>)Class.forName("com.intellij.compiler.instrumentation.JrtLoader");
constructor = aClass.getDeclaredConstructor(URL.class, int.class);
constructor.setAccessible(true);
}
catch (Throwable ignored) {
}
ourClass = aClass;
ourConstructor = constructor;
}
public static ClassFinderClasspath.Loader create(URL url, int index) {
if (ourConstructor != null) {
try {
return ourConstructor.newInstance(url, index);
}
catch (Throwable ignored) {
}
}
return null;
}
}
}
@@ -18,13 +18,18 @@ package org.jetbrains.jps.incremental.instrumentation;
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.ClassWriter;
@@ -85,8 +90,8 @@ public abstract class ClassProcessingBuilder extends ModuleLevelBuilder {
final Collection<File> classpath = new ArrayList<File>();
classpath.addAll(ProjectPaths.getCompilationClasspath(chunk, false));
classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());
finder = createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
final JpsSdk<JpsDummyElement> sdk = chunk.representativeTarget().getModule().getSdk(JpsJavaSdkType.INSTANCE);
finder = createInstrumentationClassFinder(sdk, platformCp, classpath, outputConsumer);
CLASS_FINDER.set(context, finder);
}
@@ -109,10 +114,21 @@ public abstract class ClassProcessingBuilder extends ModuleLevelBuilder {
// utility methods
public static InstrumentationClassFinder createInstrumentationClassFinder(Collection<File> platformCp, Collection<File> cp, final OutputConsumer outputConsumer) throws
public static InstrumentationClassFinder createInstrumentationClassFinder(@Nullable JpsSdk<?> sdk,
Collection<File> platformCp,
Collection<File> cp,
final OutputConsumer outputConsumer) throws
MalformedURLException {
final URL[] platformUrls = new URL[platformCp.size()];
final URL[] platformUrls;
int index = 0;
if (sdk != null && JpsJavaSdkType.getJavaVersion(sdk) >= 9) {
platformUrls = new URL[1 + platformCp.size()];
platformUrls[index++] = new URL("jrt", null, FileUtil.toSystemIndependentName(sdk.getHomePath()));
}
else {
platformUrls = new URL[platformCp.size()];
}
for (File file : platformCp) {
platformUrls[index++] = file.toURI().toURL();
}
@@ -40,7 +40,10 @@ import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.incremental.storage.OneToManyPathsMapping;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsProject;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.uiDesigner.model.JpsUiDesignerConfiguration;
import org.jetbrains.jps.uiDesigner.model.JpsUiDesignerExtensionService;
import org.jetbrains.org.objectweb.asm.ClassReader;
@@ -94,8 +97,8 @@ public class FormsInstrumenter extends FormsBuilder {
classpath.add(getResourcePath(GridConstraints.class)); // forms_rt.jar
final Map<File, String> chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk);
classpath.addAll(chunkSourcePath.keySet()); // sourcepath for loading forms resources
final InstrumentationClassFinder finder = ClassProcessingBuilder.createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
final JpsSdk<JpsDummyElement> sdk = chunk.representativeTarget().getModule().getSdk(JpsJavaSdkType.INSTANCE);
final InstrumentationClassFinder finder = ClassProcessingBuilder.createInstrumentationClassFinder(sdk, platformCp, classpath, outputConsumer);
try {
final Map<File, Collection<File>> processed = instrumentForms(context, chunk, chunkSourcePath, finder, formsToCompile, outputConsumer);