Unlock classpath in unsafe init

This commit is contained in:
Johannes Frohnmeyer 2021-12-30 13:37:05 +01:00
parent 968e3e22e2
commit ffd9905d40
Signed by: Johannes
GPG Key ID: E76429612C2929F4
6 changed files with 179 additions and 2 deletions

View File

@ -1,13 +1,17 @@
package io.gitlab.jfronny.libjf.unsafe;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.unsafe.inject.FabricLauncherClassUnlocker;
import io.gitlab.jfronny.libjf.unsafe.inject.KnotClassLoaderInterfaceAccessor;
import net.fabricmc.loader.api.LanguageAdapter;
import net.fabricmc.loader.impl.FabricLoaderImpl;
public class JfLanguageAdapter implements LanguageAdapter {
@Override
public native <T> T create(net.fabricmc.loader.api.ModContainer mod, String value, Class<T> type);
static {
FabricLoaderImpl.INSTANCE.getGameProvider().unlockClassPath(new FabricLauncherClassUnlocker(new KnotClassLoaderInterfaceAccessor(Thread.currentThread().getContextClassLoader())));
DynamicEntry.execute(LibJf.MOD_ID + ":preEarly", UltraEarlyInit.class, s -> s.instance().init());
DynamicEntry.execute(LibJf.MOD_ID + ":early", UltraEarlyInit.class, s -> s.instance().init());
LibJf.LOGGER.info("LibJF unsafe init completed");

View File

@ -0,0 +1,108 @@
package io.gitlab.jfronny.libjf.unsafe.inject;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.impl.launch.FabricLauncher;
import net.fabricmc.loader.impl.launch.MappingConfiguration;
import net.fabricmc.loader.impl.util.UrlUtil;
import net.fabricmc.loader.impl.util.log.Log;
import net.fabricmc.loader.impl.util.log.LogCategory;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.util.List;
import java.util.jar.Manifest;
public record FabricLauncherClassUnlocker(KnotClassLoaderInterfaceAccessor classLoader) implements FabricLauncher {
static {
System.err.println("[libjf-unsafe-v0] Preparing to unlock classpath via reflection");
}
@Override
public MappingConfiguration getMappingConfiguration() {
return invalidCall();
}
@Override
public void addToClassPath(Path path, String... allowedPrefixes) {
Log.debug(LogCategory.KNOT, "Adding " + path + " to classpath.");
try {
URL url = UrlUtil.asUrl(path);
classLoader.getDelegate().setAllowedPrefixes(url, allowedPrefixes);
classLoader.addURL(url);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public void setAllowedPrefixes(Path path, String... prefixes) {
try {
classLoader.getDelegate().setAllowedPrefixes(UrlUtil.asUrl(path), prefixes);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public EnvType getEnvironmentType() {
return invalidCall();
}
@Override
public boolean isClassLoaded(String name) {
return invalidCall();
}
@Override
public Class<?> loadIntoTarget(String name) throws ClassNotFoundException {
return invalidCall();
}
@Override
public InputStream getResourceAsStream(String name) {
return invalidCall();
}
@Override
public ClassLoader getTargetClassLoader() {
return invalidCall();
}
@Override
public byte[] getClassByteArray(String name, boolean runTransformers) throws IOException {
return invalidCall();
}
@Override
public Manifest getManifest(Path originPath) {
return invalidCall();
}
@Override
public boolean isDevelopment() {
return invalidCall();
}
@Override
public String getEntrypoint() {
return invalidCall();
}
@Override
public String getTargetNamespace() {
return invalidCall();
}
@Override
public List<Path> getClassPath() {
return invalidCall();
}
private <T> T invalidCall() {
throw new IllegalStateException("unlockClassPath attempted to call a method not implemented here");
}
}

View File

@ -0,0 +1,26 @@
package io.gitlab.jfronny.libjf.unsafe.inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
public record KnotClassDelegateAccessor(Object delegate) {
private static final Method setAllowedPrefixesMethod;
static {
try {
Class<?> klazz = Class.forName("net.fabricmc.loader.impl.launch.knot.KnotClassDelegate");
setAllowedPrefixesMethod = klazz.getDeclaredMethod("setAllowedPrefixes", URL.class, String[].class);
setAllowedPrefixesMethod.setAccessible(true);
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new IllegalStateException("Could not build accessor for class delegate. This version of fabric is likely not yet supported", e);
}
}
public void setAllowedPrefixes(URL url, String... prefixes) {
try {
setAllowedPrefixesMethod.invoke(delegate, url, prefixes);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Could not invoke setAllowedPrefixes on class delegate. This version of fabric is likely not yet supported", e);
}
}
}

View File

@ -0,0 +1,37 @@
package io.gitlab.jfronny.libjf.unsafe.inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
public record KnotClassLoaderInterfaceAccessor(ClassLoader loader) {
private static final Method addURLMethod;
private static final Method getDelegateMethod;
static {
try {
Class<?> klazz = Class.forName("net.fabricmc.loader.impl.launch.knot.KnotClassLoaderInterface");
addURLMethod = klazz.getDeclaredMethod("addURL", URL.class);
addURLMethod.setAccessible(true);
getDelegateMethod = klazz.getDeclaredMethod("getDelegate");
getDelegateMethod.setAccessible(true);
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new IllegalStateException("Could not build accessor for class loader. This version of fabric is likely not yet supported", e);
}
}
public void addURL(URL url) {
try {
addURLMethod.invoke(loader, url);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Could not invoke addURL on class loader. This version of fabric is likely not yet supported", e);
}
}
public KnotClassDelegateAccessor getDelegate() {
try {
return new KnotClassDelegateAccessor(getDelegateMethod.invoke(loader));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Could not invoke getDelegate on class loader. This version of fabric is likely not yet supported", e);
}
}
}

View File

@ -17,7 +17,7 @@
},
"mixins": ["libjf-unsafe-v0.mixins.json"],
"depends": {
"fabricloader": ">=0.12.0",
"fabricloader": ">=0.12.12",
"minecraft": "*",
"libjf-base": ">=${version}"
},

View File

@ -22,7 +22,9 @@
"fabricloader": ">=0.12.0",
"minecraft": "*",
"libjf-base": ">=${version}",
"libjf-config-v0": ">=${version}"
"libjf-config-v0": ">=${version}",
"fabric-lifecycle-events-v1": "*",
"fabric-command-api-v1": "*"
},
"custom": {
"modmenu": {