Skip to content

Commit

Permalink
Broken WIP (le grand refacteur)
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Jan 6, 2025
1 parent 4072d7e commit cec35a1
Show file tree
Hide file tree
Showing 59 changed files with 1,649 additions and 1,763 deletions.
2 changes: 1 addition & 1 deletion loader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ sourceSets {
jmh
testJars

// Additional test JARs, to be loaded via SecureJar.from(...)
// Additional test JARs, to be loaded via Jar.of(...)
testjar1
testjar2
// Test classpath code, to make sure that ModuleClassLoader is properly isolated from the classpath
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cpw.mods.cl.benchmarks;

import cpw.mods.cl.JarModuleFinder;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.jarhandling.JarContents;
import cpw.mods.jarhandling.impl.Jar;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
Expand All @@ -24,9 +27,9 @@ public void setup() throws Exception {
}

@Benchmark
public void benchJarModuleFinderOf(Blackhole blackhole) {
var secureJar1 = SecureJar.from(path1, path2);
var secureJar2 = SecureJar.from(path3);
public void benchJarModuleFinderOf(Blackhole blackhole) throws IOException {
var secureJar1 = Jar.of(JarContents.ofPaths(List.of(path1, path2)));
var secureJar2 = Jar.of(path3);
var jarModuleFinder = JarModuleFinder.of(secureJar1, secureJar2);

blackhole.consume(jarModuleFinder);
Expand Down
3 changes: 1 addition & 2 deletions loader/src/main/java/cpw/mods/cl/ModuleClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,9 @@ protected byte[] getClassBytes(final ModuleReader reader, final ModuleReference
private Class<?> readerToClass(final ModuleReader reader, final ModuleReference ref, final String name) {
var bytes = maybeTransformClassBytes(getClassBytes(reader, ref, name), name, null);
if (bytes.length == 0) return null;
var cname = name.replace('.', '/') + ".class";
var modroot = this.resolvedRoots.get(ref.descriptor().name());
ProtectionDomainHelper.tryDefinePackage(this, name, modroot.jar().getManifest(), t -> modroot.jar().getManifest().getAttributes(t), this::definePackage); // Packages are dirctories, and can't be signed, so use raw attributes instead of signed.
var cs = ProtectionDomainHelper.createCodeSource(toURL(ref.location()), modroot.jar().verifyAndGetSigners(cname, bytes));
var cs = ProtectionDomainHelper.createCodeSource(toURL(ref.location()), null);
var cls = defineClass(name, bytes, 0, bytes.length, ProtectionDomainHelper.createProtectionDomain(cs, this));
ProtectionDomainHelper.trySetPackageModule(cls.getPackage(), cls.getModule());
return cls;
Expand Down
3 changes: 2 additions & 1 deletion loader/src/main/java/cpw/mods/cl/ProtectionDomainHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.jetbrains.annotations.Nullable;

public class ProtectionDomainHelper {
private static final Map<URL, CodeSource> csCache = new HashMap<>();

public static CodeSource createCodeSource(final URL url, final CodeSigner[] signers) {
public static CodeSource createCodeSource(URL url, @Nullable CodeSigner[] signers) {
synchronized (csCache) {
return csCache.computeIfAbsent(url, u -> new CodeSource(url, signers));
}
Expand Down
145 changes: 145 additions & 0 deletions loader/src/main/java/cpw/mods/jarhandling/CompositeModContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package cpw.mods.jarhandling;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;

public final class CompositeModContainer implements JarContents {
private final JarContents[] delegates;
@Nullable
private final PathFilter[] filters;

public CompositeModContainer(List<JarContents> delegates) {
this(delegates, null);
}

public CompositeModContainer(List<JarContents> delegates, @Nullable List<PathFilter> filters) {
this.delegates = delegates.toArray(JarContents[]::new);
this.filters = filters != null ? filters.toArray(PathFilter[]::new) : null;
if (delegates.isEmpty()) {
throw new IllegalArgumentException("Cannot construct an empty mod container");
}
if (filters != null && delegates.size() != filters.size()) {
throw new IllegalArgumentException("The number of delegates and filters must match.");
}
}

@Override
public Path getPrimaryPath() {
return delegates[0].getPrimaryPath();
}

@Override
public String toString() {
return "[" + Arrays.stream(delegates).map(Object::toString).collect(Collectors.joining(", ")) + "]";
}

@Override
public Optional<URI> findFile(String relativePath) {
for (var delegate : delegates) {
var result = delegate.findFile(relativePath);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}

@Nullable
@Override
public Manifest getJarManifest() {
for (var delegate : delegates) {
var manifest = delegate.getJarManifest();
if (manifest != null) {
return manifest;
}
}
return null;
}

@Override
public InputStream openFile(String relativePath) throws IOException {
for (var delegate : delegates) {
var stream = delegate.openFile(relativePath);
if (stream != null) {
return stream;
}
}
return null;
}

@Override
public byte[] readFile(String relativePath) throws IOException {
for (var delegate : delegates) {
var content = delegate.readFile(relativePath);
if (content != null) {
return content;
}
}
return null;
}

@Override
public boolean hasContentRoot(Path path) {
for (var delegate : delegates) {
if (delegate.hasContentRoot(path)) {
return true;
}
}
return false;
}

@Override
public Stream<URL> getClasspathRoots() {
return Arrays.stream(delegates).flatMap(JarContents::getClasspathRoots);
}

@Override
public void visitContent(ModContentVisitor visitor) {
// This is based on the logic that openResource will return the file from the *first* delegate
// Every relative path we return will not be returned again
var distinctVisitor = new ModContentVisitor() {
final Set<String> pathsVisited = new HashSet<>();

@Override
public void visit(String relativePath, IOSupplier<InputStream> contentSupplier, IOSupplier<ModContentAttributes> attributesSupplier) {
if (pathsVisited.add(relativePath)) {
visitor.visit(relativePath, contentSupplier, attributesSupplier);
}
}
};

for (var delegate : delegates) {
delegate.visitContent(distinctVisitor);
}
}

@Override
public void close() throws IOException {
IOException error = null;
for (var delegate : delegates) {
try {
delegate.close();
} catch (IOException e) {
if (error == null) {
error = new IOException("Failed to close one ore more delegates of " + this);
}
error.addSuppressed(e);
}
}
if (error != null) {
throw error;
}
}
}
54 changes: 54 additions & 0 deletions loader/src/main/java/cpw/mods/jarhandling/EmptyModContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cpw.mods.jarhandling;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;

public final class EmptyModContainer implements JarContents {
private final Path path;

public EmptyModContainer(Path path) {
this.path = path;
}

@Override
public Optional<URI> findFile(String relativePath) {
return Optional.empty();
}

@Override
public Path getPrimaryPath() {
return path;
}

@Override
public @Nullable InputStream openFile(String relativePath) throws IOException {
return null;
}

@Override
public byte @Nullable [] readFile(String relativePath) throws IOException {
return null;
}

@Override
public boolean hasContentRoot(Path path) {
return false;
}

@Override
public Stream<URL> getClasspathRoots() {
return Stream.empty();
}

@Override
public void visitContent(ModContentVisitor visitor) {}

@Override
public void close() throws IOException {}
}
94 changes: 94 additions & 0 deletions loader/src/main/java/cpw/mods/jarhandling/FolderModContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cpw.mods.jarhandling;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.util.Optional;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public record FolderModContainer(Path path) implements JarContents {
private static final Logger LOG = LoggerFactory.getLogger(FolderModContainer.class);

@Override
public Path getPrimaryPath() {
return path;
}

@Override
public InputStream openFile(String relativePath) throws IOException {
try {
return Files.newInputStream(path.resolve(relativePath));
} catch (NoSuchFileException e) {
return null;
}
}

@Override
public byte[] readFile(String relativePath) throws IOException {
try {
return Files.readAllBytes(path.resolve(relativePath));
} catch (NoSuchFileException e) {
return null;
}
}

@Override
public boolean hasContentRoot(Path path) {
return this.path.equals(path);
}

@Override
public Stream<URL> getClasspathRoots() {
try {
return Stream.of(path.toUri().toURL());
} catch (MalformedURLException e) {
LOG.error("Failed to convert path to URL: {}", path, e);
return Stream.of();
}
}

@Override
public void visitContent(ModContentVisitor visitor) {
var startingPoint = path;
try (var stream = Files.walk(startingPoint)) {
stream.forEach(path -> {
var file = path.toFile();
if (file.isFile()) {
var relativePath = PathNormalization.normalize(startingPoint.relativize(path).toString());
visitor.visit(relativePath, () -> Files.newInputStream(path), () -> {
var attributes = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
return new ModContentAttributes(attributes.lastModifiedTime(), attributes.size());
});
}
});
} catch (IOException e) {
throw new UncheckedIOException("Failed to walk contents of " + path, e);
}
}

@Override
public String toString() {
return path.toString();
}

@Override
public Optional<URI> findFile(String relativePath) {
if (relativePath.startsWith("/")) {
throw new IllegalArgumentException("Must be a relative path: " + relativePath);
}
var pathToFile = path.resolve(relativePath);
return pathToFile.toFile().isFile() ? Optional.of(pathToFile.toUri()) : Optional.empty();
}

@Override
public void close() {}
}
8 changes: 8 additions & 0 deletions loader/src/main/java/cpw/mods/jarhandling/IOSupplier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cpw.mods.jarhandling;

import java.io.IOException;

@FunctionalInterface
public interface IOSupplier<T> {
T get() throws IOException;
}
Loading

0 comments on commit cec35a1

Please sign in to comment.