Skip to content

Commit

Permalink
Clean up classloading and allow classes without a resource url to be …
Browse files Browse the repository at this point in the history
…loaded (#153)
  • Loading branch information
vectrixdevelops committed Feb 23, 2024
1 parent 32586e5 commit 75100ee
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 61 deletions.
21 changes: 17 additions & 4 deletions launcher/src/main/java/space/vectrix/ignite/launch/LaunchImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ public void configure(final @NotNull EmberClassLoader classLoader, final @NotNul
}
}

classLoader.addTransformationFilter(this.transformationFilter());
classLoader.addTransformationFilter(this.packageFilter());
classLoader.addManifestLocator(this.manifestLocator());
transformer.addResourceExclusion(this.resourceFilter());
}

@Override
Expand Down Expand Up @@ -133,7 +134,7 @@ public void prepare(final @NotNull EmberTransformer transformer) {
};
}

private @NotNull Predicate<String> transformationFilter() {
private @NotNull Predicate<String> packageFilter() {
return name -> {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_PACKAGES) {
if(name.startsWith(test)) {
Expand All @@ -145,6 +146,18 @@ public void prepare(final @NotNull EmberTransformer transformer) {
};
}

private @NotNull Predicate<String> resourceFilter() {
return path -> {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_RESOURCES) {
if(path.startsWith(test)) {
return false;
}
}

return true;
};
}

private @NotNull Function<URLConnection, Optional<Manifest>> manifestLocator() {
final ModsImpl engine = IgniteBootstrap.instance().engine();

Expand Down Expand Up @@ -193,14 +206,14 @@ private boolean transformable(final @NotNull URI uri) throws URISyntaxException,
}

if(target.isDirectory()) {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_PATHS) {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_RESOURCES) {
if(new File(target, test).exists()) {
return false;
}
}
} else if(target.isFile()) {
try(final JarFile jarFile = new JarFile(new File(uri))) {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_PATHS) {
for(final String test : IgniteExclusions.TRANSFORMATION_EXCLUDED_RESOURCES) {
if(jarFile.getEntry(test) != null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,16 @@ public static void launch(final String@NotNull [] arguments) {

private static Ember INSTANCE;

private final ServiceLoader<LaunchService> serviceLoader = ServiceLoader.load(LaunchService.class, Ember.class.getClassLoader());
private final LaunchService service;

private EmberTransformer transformer;
private EmberClassLoader loader;

private Ember() {
Ember.INSTANCE = this;
this.service = IgniteCollections.firstOrNull(this.serviceLoader.iterator());

final ServiceLoader<LaunchService> serviceLoader = ServiceLoader.load(LaunchService.class, Ember.class.getClassLoader());
this.service = IgniteCollections.firstOrNull(serviceLoader.iterator());
}

/* package */ @NotNull EmberTransformer transformer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,6 @@ private static final class DynamicClassLoader extends URLClassLoader {
public void addURL(final @NotNull URL url) {
super.addURL(url);
}

@Override
public @Nullable Package getPackage(final @NotNull String name) {
return super.getPackage(name);
}

@Override
public Package@NotNull [] getPackages() {
return super.getPackages();
}
}

private static final List<String> EXCLUDE_PACKAGES = Arrays.asList(
Expand Down Expand Up @@ -167,16 +157,14 @@ public void addTransformationFilter(final @NotNull Predicate<String> transformat
target = this.findClass(canonicalName, TransformPhase.INITIALIZE);
if(target == null) {
Logger.trace("Unable to locate class: {}", canonicalName);
final String internalName = canonicalName.replace('.', '/').concat(".class");
final URL url = this.parent.getResource(internalName);

if(url != null) {
Logger.trace("Attempting to load parent class: {}", canonicalName);
Logger.trace("Attempting to load parent class: {}", canonicalName);
try {
target = this.parent.loadClass(canonicalName);
Logger.trace("Loaded parent class: {}", canonicalName);
} else {
Logger.trace("Unable to locate parent resource: {}", canonicalName);
throw new ClassNotFoundException("Unable to locate parent resource: " + canonicalName);
} catch(final ClassNotFoundException exception) {
Logger.trace("Unable to locate parent class: {}", canonicalName);
throw exception;
}
} else {
Logger.trace("Loaded transformed class: {}", canonicalName);
Expand All @@ -191,70 +179,70 @@ public void addTransformationFilter(final @NotNull Predicate<String> transformat

@Override
protected @NotNull Class<?> findClass(final @NotNull String name) throws ClassNotFoundException {
Logger.trace("Finding class: {}", name);
final Class<?> target = this.findClass(name, TransformPhase.INITIALIZE);
final String canonicalName = name.replace('/', '.');

Logger.trace("Finding class: {}", canonicalName);
final Class<?> target = this.findClass(canonicalName, TransformPhase.INITIALIZE);
if(target == null) {
Logger.trace("Unable to find class: {}", name);
throw new ClassNotFoundException(name);
Logger.trace("Unable to find class: {}", canonicalName);
throw new ClassNotFoundException(canonicalName);
}

Logger.trace("Found class: {}", name);
Logger.trace("Found class: {}", canonicalName);
return target;
}

@SuppressWarnings("SameParameterValue")
/* package */ @Nullable Class<?> findClass(final @NotNull String name, final @NotNull TransformPhase phase) {
final String canonicalName = name.replace('/', '.');
if(canonicalName.startsWith("java.")) {
Logger.trace("Skipping platform class: {}", canonicalName);
if(name.startsWith("java.")) {
Logger.trace("Skipping platform class: {}", name);
return null;
}

// Grab the class bytes.
final Map.Entry<byte[], Manifest> transformed = this.transformData(canonicalName, phase);
final Map.Entry<byte[], Manifest> transformed = this.transformData(name, phase);
if(transformed == null) return null;

// Check if the class has already been loaded by the transform.
final Class<?> existingClass = this.findLoadedClass(canonicalName);
final Class<?> existingClass = this.findLoadedClass(name);
if(existingClass != null) {
Logger.trace("Skipping already defined transformed class: {}", canonicalName);
Logger.trace("Skipping already defined transformed class: {}", name);
return existingClass;
}

// Find the package for this class.
final int classIndex = canonicalName.lastIndexOf('.');
final int classIndex = name.lastIndexOf('.');
if(classIndex > 0) {
final String packageName = canonicalName.substring(0, classIndex);
final String packageName = name.substring(0, classIndex);
this.findPackage(packageName, transformed.getValue());
}

final byte[] bytes = transformed.getKey();
return this.defineClass(canonicalName, bytes, 0, bytes.length);
return this.defineClass(name, bytes, 0, bytes.length);
}

/* package */ Map.@Nullable Entry<byte@NotNull [], @NotNull Manifest> transformData(final @NotNull String name, final @NotNull TransformPhase phase) {
final String canonicalName = name.replace('/', '.');

final Map.Entry<byte[], Manifest> transformed = this.classData(canonicalName, phase);
if(transformed == null) return null;
/* package */ Map.@Nullable Entry<byte@NotNull [], @Nullable Manifest> transformData(final @NotNull String name, final @NotNull TransformPhase phase) {
final Map.Entry<byte[], Manifest> data = this.classData(name, phase);
if(data == null) return null;

// Prevent transforming classes that are excluded from transformation.
if(!this.transformationFilter.test(canonicalName)) {
Logger.trace("Skipping transformer excluded class: {}", canonicalName);
if(!this.transformationFilter.test(name)) {
Logger.trace("Skipping transformer excluded class: {}", name);
return null;
}

// Run the transformation.
final byte[] bytes = this.transformer.transform(canonicalName, transformed.getKey(), phase);
return new AbstractMap.SimpleEntry<>(bytes, transformed.getValue());
final byte[] bytes = this.transformer.transform(name, data.getKey(), phase);
return new AbstractMap.SimpleEntry<>(bytes, data.getValue());
}

/* package */ Map.@Nullable Entry<byte@NotNull [], @NotNull Manifest> classData(final @NotNull String name, final @NotNull TransformPhase phase) {
final String internalName = name.replace('.', '/').concat(".class");
/* package */ Map.@Nullable Entry<byte@NotNull [], @Nullable Manifest> classData(final @NotNull String name, final @NotNull TransformPhase phase) {
final String resourceName = name.replace('.', '/').concat(".class");

URL url = this.findResource(internalName);
URL url = this.findResource(resourceName);
if(url == null) {
if(phase == TransformPhase.INITIALIZE) return null;
url = this.parent.getResource(internalName);
url = this.parent.getResource(resourceName);
if(url == null) return null;
}

Expand All @@ -274,7 +262,7 @@ public void addTransformationFilter(final @NotNull Predicate<String> transformat
final Manifest manifest = connection.manifest();
return new AbstractMap.SimpleEntry<>(bytes, manifest);
} catch(final Exception exception) {
Logger.trace(exception, "Failed to resolve class data: {}", internalName);
Logger.trace(exception, "Failed to resolve class data: {}", resourceName);
return null;
}
}
Expand Down Expand Up @@ -348,11 +336,13 @@ public void addTransformationFilter(final @NotNull Predicate<String> transformat

@Override
protected @Nullable URL findResource(final @NotNull String name) {
requireNonNull(name, "name");
return this.dynamic.findResource(name);
}

@Override
protected @NotNull Enumeration<URL> findResources(final @NotNull String name) throws IOException {
requireNonNull(name, "name");
return this.dynamic.findResources(name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@
* @since 1.0.0
*/
public final class EmberTransformer {
private final ServiceLoader<TransformerService> serviceLoader = ServiceLoader.load(TransformerService.class, Ember.class.getClassLoader());
private final Map<Class<? extends TransformerService>, TransformerService> transformers = new IdentityHashMap<>();

private Predicate<String> exclusionFilter = path -> false;
private Predicate<String> resourceExclusionFilter = path -> true;

/* package */ EmberTransformer() {
for(final TransformerService service : this.serviceLoader) {
final ServiceLoader<TransformerService> serviceLoader = ServiceLoader.load(TransformerService.class, Ember.class.getClassLoader());
for(final TransformerService service : serviceLoader) {
this.transformers.put(service.getClass(), service);
}
}
Expand All @@ -69,8 +69,8 @@ public final class EmberTransformer {
* @param predicate the filter
* @since 1.0.0
*/
public void exclude(final @NotNull Predicate<String> predicate) {
this.exclusionFilter = this.exclusionFilter.or(predicate);
public void addResourceExclusion(final @NotNull Predicate<String> predicate) {
this.resourceExclusionFilter = predicate;
}

/**
Expand Down Expand Up @@ -99,7 +99,8 @@ public void exclude(final @NotNull Predicate<String> predicate) {
final String internalName = className.replace('.', '/');

// Check if the path is excluded from transformation.
if(this.exclusionFilter.test(internalName)) {
if(!this.resourceExclusionFilter.test(internalName)) {
Logger.debug("Skipping resource excluded class: {}", internalName);
return input;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
import java.util.function.Function;
import java.util.jar.Manifest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/* package */ final class ResourceConnection implements AutoCloseable {
private final URLConnection connection;
private final InputStream stream;
private final Function<URLConnection, Manifest> manifestFunction;

/* package */ ResourceConnection(final @NotNull URL url, final @NotNull Function<URLConnection, Manifest> manifestLocator) throws IOException {
/* package */ ResourceConnection(final @NotNull URL url, final @NotNull Function<@NotNull URLConnection, @Nullable Manifest> manifestLocator) throws IOException {
this.connection = url.openConnection();
this.stream = this.connection.getInputStream();
this.manifestFunction = manifestLocator;
Expand All @@ -51,7 +52,7 @@
return this.stream;
}

/* package */ @NotNull Manifest manifest() {
/* package */ @Nullable Manifest manifest() {
return this.manifestFunction.apply(this.connection);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public final class IgniteExclusions {
*
* @since 1.0.0
*/
public static final String[] TRANSFORMATION_EXCLUDED_PATHS = {
public static final String[] TRANSFORMATION_EXCLUDED_RESOURCES = {
"org/spongepowered/asm/"
};

Expand All @@ -48,9 +48,14 @@ public final class IgniteExclusions {
public static final String[] TRANSFORMATION_EXCLUDED_PACKAGES = {
// Launcher
"com.astrafell.ignite.",
"org.tinylog.",

// Mixin
"org.spongepowered.asm."
"org.spongepowered.asm.",
"com.llamalad7.mixinextras.",

// Access Widener
"net.fabricmc.accesswidener.",
};

private IgniteExclusions() {
Expand Down

0 comments on commit 75100ee

Please sign in to comment.