Preloader: extract nested classes to top-level

This commit is contained in:
Alexander Udalov
2014-12-29 13:56:05 +03:00
parent 9273b0dfec
commit 8306ccd8ec
5 changed files with 232 additions and 159 deletions
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2014 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.jet.preloading;
import java.io.File;
@SuppressWarnings("UnusedParameters")
public abstract class ClassHandler {
public byte[] instrument(String resourceName, byte[] data) {
return data;
}
public void beforeDefineClass(String name, int sizeInBytes) {}
public void afterDefineClass(String name) {}
public void beforeLoadJar(File jarFile) {}
public void afterLoadJar(File jarFile) {}
}
@@ -17,10 +17,6 @@
package org.jetbrains.jet.preloading;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -30,19 +26,6 @@ import java.util.zip.ZipInputStream;
@SuppressWarnings("unchecked")
public class ClassPreloadingUtils {
public static abstract class ClassHandler {
public byte[] instrument(String resourceName, byte[] data) {
return data;
}
public void beforeDefineClass(String name, int sizeInBytes) {}
public void afterDefineClass(String name) {}
public void beforeLoadJar(File jarFile) {}
public void afterLoadJar(File jarFile) {}
}
/**
* Creates a class loader that loads all classes from {@code jarFiles} into memory to make loading faster (avoid skipping through zip archives).
*
@@ -70,7 +53,7 @@ public class ClassPreloadingUtils {
parentClassLoader = preloadClasses(classpath, classCountEstimation, parentClassLoader, null, handler);
}
return createMemoryBasedClassLoader(parentClassLoader, entries, handler, classesToLoadByParent);
return new MemoryBasedClassLoader(classesToLoadByParent, parentClassLoader, entries, handler);
}
public static ClassLoader preloadClasses(
@@ -116,109 +99,6 @@ public class ClassPreloadingUtils {
return classpath;
}
private static ClassLoader createMemoryBasedClassLoader(
final ClassLoader parent,
final Map<String, Object> preloadedResources,
final ClassHandler handler,
final ClassCondition classesToLoadByParent
) {
return new ClassLoader(null) {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (classesToLoadByParent != null && classesToLoadByParent.accept(name)) {
if (parent == null) {
return super.loadClass(name);
}
try {
return parent.loadClass(name);
}
catch (ClassNotFoundException e) {
return super.loadClass(name);
}
}
// Look in this class loader and then in the parent one
Class<?> aClass = super.loadClass(name);
if (aClass == null) {
if (parent == null) {
throw new ClassNotFoundException("Class not available in preloader: " + name);
}
return parent.loadClass(name);
}
return aClass;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String internalName = name.replace('.', '/').concat(".class");
Object resources = preloadedResources.get(internalName);
if (resources == null) return null;
ResourceData resourceData = resources instanceof ResourceData
? ((ResourceData) resources)
: ((List<ResourceData>) resources).get(0);
int sizeInBytes = resourceData.bytes.length;
if (handler != null) {
handler.beforeDefineClass(name, sizeInBytes);
}
Class<?> definedClass = defineClass(name, resourceData.bytes, 0, sizeInBytes);
if (handler != null) {
handler.afterDefineClass(name);
}
return definedClass;
}
@Override
public URL getResource(String name) {
URL resource = super.getResource(name);
if (resource == null && parent != null) {
return parent.getResource(name);
}
return resource;
}
@Override
protected URL findResource(String name) {
Enumeration<URL> resources = findResources(name);
return resources.hasMoreElements() ? resources.nextElement() : null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration<URL> resources = super.getResources(name);
if (!resources.hasMoreElements() && parent != null) {
return parent.getResources(name);
}
return resources;
}
@Override
protected Enumeration<URL> findResources(String name) {
Object resources = preloadedResources.get(name);
if (resources == null) {
return Collections.enumeration(Collections.<URL>emptyList());
}
else if (resources instanceof ResourceData) {
return Collections.enumeration(Collections.singletonList(((ResourceData) resources).getURL()));
}
else {
assert resources instanceof ArrayList : name;
List<ResourceData> resourceDatas = (ArrayList<ResourceData>) resources;
List<URL> urls = new ArrayList<URL>(resourceDatas.size());
for (ResourceData data : resourceDatas) {
urls.add(data.getURL());
}
return Collections.enumeration(urls);
}
}
};
}
/**
* @return a map of name to resources. Each value is either a ResourceData if there's only one instance (in the vast majority of cases)
* or a non-empty ArrayList of ResourceData if there's many
@@ -299,41 +179,4 @@ public class ClassPreloadingUtils {
return resources;
}
private static final class ResourceData {
private final File jarFile;
private final String resourceName;
private final byte[] bytes;
public ResourceData(File jarFile, String resourceName, byte[] bytes) {
this.jarFile = jarFile;
this.resourceName = resourceName;
this.bytes = bytes;
}
public URL getURL() {
try {
String path = "file:" + jarFile + "!/" + resourceName;
return new URL("jar", null, 0, path, new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
};
}
});
}
catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
}
}
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2014 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.jet.preloading;
import java.io.IOException;
import java.net.URL;
import java.util.*;
@SuppressWarnings("unchecked")
public class MemoryBasedClassLoader extends ClassLoader {
private final ClassCondition classesToLoadByParent;
private final ClassLoader parent;
private final Map<String, Object> preloadedResources;
private final ClassHandler handler;
public MemoryBasedClassLoader(
ClassCondition classesToLoadByParent,
ClassLoader parent,
Map<String, Object> preloadedResources,
ClassHandler handler
) {
super(null);
this.classesToLoadByParent = classesToLoadByParent;
this.parent = parent;
this.preloadedResources = preloadedResources;
this.handler = handler;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (classesToLoadByParent != null && classesToLoadByParent.accept(name)) {
if (parent == null) {
return super.loadClass(name);
}
try {
return parent.loadClass(name);
}
catch (ClassNotFoundException e) {
return super.loadClass(name);
}
}
// Look in this class loader and then in the parent one
Class<?> aClass = super.loadClass(name);
if (aClass == null) {
if (parent == null) {
throw new ClassNotFoundException("Class not available in preloader: " + name);
}
return parent.loadClass(name);
}
return aClass;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String internalName = name.replace('.', '/').concat(".class");
Object resources = preloadedResources.get(internalName);
if (resources == null) return null;
ResourceData resourceData = resources instanceof ResourceData
? ((ResourceData) resources)
: ((List<ResourceData>) resources).get(0);
int sizeInBytes = resourceData.bytes.length;
if (handler != null) {
handler.beforeDefineClass(name, sizeInBytes);
}
Class<?> definedClass = defineClass(name, resourceData.bytes, 0, sizeInBytes);
if (handler != null) {
handler.afterDefineClass(name);
}
return definedClass;
}
@Override
public URL getResource(String name) {
URL resource = super.getResource(name);
if (resource == null && parent != null) {
return parent.getResource(name);
}
return resource;
}
@Override
protected URL findResource(String name) {
Enumeration<URL> resources = findResources(name);
return resources.hasMoreElements() ? resources.nextElement() : null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration<URL> resources = super.getResources(name);
if (!resources.hasMoreElements() && parent != null) {
return parent.getResources(name);
}
return resources;
}
@Override
protected Enumeration<URL> findResources(String name) {
Object resources = preloadedResources.get(name);
if (resources == null) {
return Collections.enumeration(Collections.<URL>emptyList());
}
else if (resources instanceof ResourceData) {
return Collections.enumeration(Collections.singletonList(((ResourceData) resources).getURL()));
}
else {
assert resources instanceof ArrayList : "Resource map should contain ResourceData or ArrayList<ResourceData>: " + name;
List<ResourceData> resourceDatas = (ArrayList<ResourceData>) resources;
List<URL> urls = new ArrayList<URL>(resourceDatas.size());
for (ResourceData data : resourceDatas) {
urls.add(data.getURL());
}
return Collections.enumeration(urls);
}
}
}
@@ -186,7 +186,7 @@ public class Preloader {
System.exit(1);
}
private static class Handler extends ClassPreloadingUtils.ClassHandler {
private static class Handler extends ClassHandler {
public void done() {}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2014 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.jet.preloading;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public final class ResourceData {
public final File jarFile;
public final String resourceName;
public final byte[] bytes;
public ResourceData(File jarFile, String resourceName, byte[] bytes) {
this.jarFile = jarFile;
this.resourceName = resourceName;
this.bytes = bytes;
}
public URL getURL() {
try {
String path = "file:" + jarFile + "!/" + resourceName;
return new URL("jar", null, 0, path, new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
};
}
});
}
catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
}