mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-08-15 02:57:27 +03:00
[FEATURE][EXPERIMENTAL] Implement modular class transformation, fix class transform filter
This commit is contained in:
parent
6ead3c336e
commit
b0715e0922
2 changed files with 122 additions and 5 deletions
|
@ -9,6 +9,7 @@ public class LaunchOptions {
|
||||||
|
|
||||||
|
|
||||||
public static final class ModuleConf {
|
public static final class ModuleConf {
|
||||||
|
public boolean enableModularClassTransform = true;
|
||||||
public List<String> modules = new ArrayList<>();
|
public List<String> modules = new ArrayList<>();
|
||||||
public List<String> modulePath = new ArrayList<>();
|
public List<String> modulePath = new ArrayList<>();
|
||||||
public Map<String, String> exports = new HashMap<>();
|
public Map<String, String> exports = new HashMap<>();
|
||||||
|
|
|
@ -5,21 +5,26 @@
|
||||||
import pro.gravit.utils.helper.JVMHelper;
|
import pro.gravit.utils.helper.JVMHelper;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.module.Configuration;
|
import java.lang.module.*;
|
||||||
import java.lang.module.ModuleFinder;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class ModuleLaunch implements Launch {
|
public class ModuleLaunch implements Launch {
|
||||||
private ModuleClassLoader moduleClassLoader;
|
private ModuleClassLoader moduleClassLoader;
|
||||||
|
@ -59,6 +64,10 @@ public ClassLoaderControl init(List<Path> files, String nativePath, LaunchOption
|
||||||
if(options.moduleConf != null) {
|
if(options.moduleConf != null) {
|
||||||
// Create Module Layer
|
// Create Module Layer
|
||||||
moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).map(Path::toAbsolutePath).toArray(Path[]::new));
|
moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).map(Path::toAbsolutePath).toArray(Path[]::new));
|
||||||
|
if(options.moduleConf.enableModularClassTransform) {
|
||||||
|
AtomicReference<ModuleClassLoader> clRef = new AtomicReference<>(moduleClassLoader);
|
||||||
|
moduleFinder = new CustomModuleFinder(moduleFinder, clRef);
|
||||||
|
}
|
||||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||||
if(options.moduleConf.modules.contains("ALL-MODULE-PATH")) {
|
if(options.moduleConf.modules.contains("ALL-MODULE-PATH")) {
|
||||||
var set = moduleFinder.findAll();
|
var set = moduleFinder.findAll();
|
||||||
|
@ -182,6 +191,105 @@ private static String getPackageFromClass(String clazz) {
|
||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CustomModuleFinder implements ModuleFinder {
|
||||||
|
private final ModuleFinder delegate;
|
||||||
|
private AtomicReference<ModuleClassLoader> cl;
|
||||||
|
|
||||||
|
public CustomModuleFinder(ModuleFinder delegate, AtomicReference<ModuleClassLoader> cl) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.cl = cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ModuleReference> find(String name) {
|
||||||
|
return delegate.find(name).map(this::makeModuleReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ModuleReference> findAll() {
|
||||||
|
return delegate.findAll().stream()
|
||||||
|
.map(this::makeModuleReference)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CustomModuleReference makeModuleReference(ModuleReference x) {
|
||||||
|
return new CustomModuleReference(x.descriptor(), x.location().orElse(null), x, cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomModuleReference extends ModuleReference {
|
||||||
|
private final ModuleReference delegate;
|
||||||
|
private final AtomicReference<ModuleClassLoader> cl;
|
||||||
|
|
||||||
|
public CustomModuleReference(ModuleDescriptor descriptor, URI location, ModuleReference delegate, AtomicReference<ModuleClassLoader> cl) {
|
||||||
|
super(descriptor, location);
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.cl = cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModuleReader open() throws IOException {
|
||||||
|
return new CustomModuleReader(delegate.open(), cl, descriptor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomModuleReader implements ModuleReader {
|
||||||
|
private final ModuleReader delegate;
|
||||||
|
private final AtomicReference<ModuleClassLoader> cl;
|
||||||
|
private final ModuleDescriptor descriptor;
|
||||||
|
|
||||||
|
public CustomModuleReader(ModuleReader delegate, AtomicReference<ModuleClassLoader> cl, ModuleDescriptor descriptor) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.cl = cl;
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<URI> find(String name) throws IOException {
|
||||||
|
return delegate.find(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<InputStream> open(String name) throws IOException {
|
||||||
|
ModuleClassLoader classLoader = cl.get();
|
||||||
|
if(classLoader == null || !name.endsWith(".class")) {
|
||||||
|
return delegate.open(name);
|
||||||
|
}
|
||||||
|
var inputOptional = delegate.open(name);
|
||||||
|
if(inputOptional.isEmpty()) {
|
||||||
|
return inputOptional;
|
||||||
|
}
|
||||||
|
try(ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||||
|
inputOptional.get().transferTo(output);
|
||||||
|
var realClassName = name.replace("/", ".").substring(0, name.length()-".class".length()-1);
|
||||||
|
byte[] bytes = classLoader.transformClass(descriptor.name(), realClassName, output.toByteArray());
|
||||||
|
return Optional.of(new ByteArrayInputStream(bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||||
|
// TODO class transformation unimplemented
|
||||||
|
return delegate.read(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release(ByteBuffer bb) {
|
||||||
|
// TODO class transformation unimplemented
|
||||||
|
delegate.release(bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<String> list() throws IOException {
|
||||||
|
return delegate.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
delegate.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ModuleClassLoader extends URLClassLoader {
|
private class ModuleClassLoader extends URLClassLoader {
|
||||||
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
||||||
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
||||||
|
@ -251,9 +359,7 @@ protected Class<?> findClass(String moduleName, String name) {
|
||||||
String rawClassName = name.replace(".", "/").concat(".class");
|
String rawClassName = name.replace(".", "/").concat(".class");
|
||||||
try(InputStream input = getResourceAsStream(rawClassName)) {
|
try(InputStream input = getResourceAsStream(rawClassName)) {
|
||||||
byte[] bytes = IOHelper.read(input);
|
byte[] bytes = IOHelper.read(input);
|
||||||
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
bytes = transformClass(moduleName, name, bytes);
|
||||||
bytes = t.transform(moduleName, name, null, bytes);
|
|
||||||
}
|
|
||||||
clazz = defineClass(name, bytes, 0, bytes.length);
|
clazz = defineClass(name, bytes, 0, bytes.length);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -286,6 +392,16 @@ protected Class<?> findClass(String moduleName, String name) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] transformClass(String moduleName, String name, byte[] bytes) {
|
||||||
|
for(ClassLoaderControl.ClassTransformer t : transformers) {
|
||||||
|
if(!t.filter(moduleName, name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bytes = t.transform(moduleName, name, null, bytes);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String findLibrary(String name) {
|
public String findLibrary(String name) {
|
||||||
if(nativePath == null) {
|
if(nativePath == null) {
|
||||||
|
|
Loading…
Reference in a new issue