Skip to content

Commit dbff4e0

Browse files
committed
Add workaround to BuildConfig values getting inlined in mods
1 parent e5fbb0a commit dbff4e0

File tree

3 files changed

+107
-4
lines changed

3 files changed

+107
-4
lines changed

loader/build.gradle

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ tasks.compileJava.dependsOn(":patching:jar")
100100
File classDir = new File(project.rootProject.rootDir, "loader" + File.separator +
101101
"build" + File.separator + "classes" + File.separator + "java" + File.separator + "main")
102102
File resourcesDir = new File(project.rootProject.rootDir, "loader" + File.separator +
103-
"build" + File.separator + "resources" + File.separator + File.separator + "main")
103+
"build" + File.separator + "resources" + File.separator + "main")
104104

105105
tasks.register("transformFoxLoader", JavaExec) {
106106
classpath = project(":patching").sourceSets.main.runtimeClasspath
@@ -112,6 +112,18 @@ tasks.register("transformFoxLoader", JavaExec) {
112112

113113
tasks.classes.dependsOn(tasks.transformFoxLoader)
114114

115+
File outputJar = new File(project.rootProject.rootDir, "loader" + File.separator + "build" +
116+
File.separator + "libs" + File.separator + "loader-" + project['foxloader.version'] + ".jar")
117+
118+
tasks.register("transformFoxLoaderJar", JavaExec) {
119+
classpath = project(":patching").sourceSets.main.runtimeClasspath
120+
mainClass = "com.fox2code.foxloader.patching.FoxLoaderPatches"
121+
args = [outputJar.getAbsolutePath()]
122+
dependsOn(tasks.jar)
123+
}
124+
125+
tasks.assemble.dependsOn(tasks.transformFoxLoaderJar)
126+
115127
final Properties properties = new Properties()
116128
final File localPropertiesFile =
117129
project.rootProject.file("local.properties")

patching/generate.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ static void generateBuildConfig0(File buildConfigSrc, Project project) {
7373
printStream.println("// Auto generated class, do not modify.")
7474
printStream.println("package com.fox2code.foxloader.launcher;")
7575
printStream.println()
76+
printStream.println("/**")
77+
printStream.println(" * Class used to get info about FoxLoader, unlike regular final constants,")
78+
printStream.println(" * the one in this class won't get inlined due to a FoxLoader specific ASM hack.")
79+
printStream.println(" */")
7680
printStream.println("public final class BuildConfig {")
7781
printStream.println(" private BuildConfig() {}")
7882
printStream.println(" public static final String FOXLOADER_VERSION = \"" + FOXLOADER_VERSION + "\";")

patching/src/main/java/com/fox2code/foxloader/patching/FoxLoaderPatches.java

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.fox2code.foxloader.dependencies.DependencyHelper;
2727
import com.fox2code.foxloader.launcher.BuildConfig;
28+
import com.fox2code.foxloader.utils.io.IOUtils;
2829
import org.objectweb.asm.ClassReader;
2930
import org.objectweb.asm.ClassWriter;
3031
import org.objectweb.asm.Opcodes;
@@ -33,14 +34,21 @@
3334
import java.io.*;
3435
import java.nio.file.Files;
3536
import java.util.Locale;
37+
import java.util.zip.ZipEntry;
38+
import java.util.zip.ZipInputStream;
39+
import java.util.zip.ZipOutputStream;
3640

3741
// Allows us to do stuff java doesn't allow us to do.
3842
public final class FoxLoaderPatches implements Opcodes {
3943
private static final boolean DEBUG = false;
4044

4145
public static void main(String[] args) throws IOException {
42-
patchFoxLoaderClassesDir(new File(args[0]));
43-
patchFoxLoaderResourcesDir(new File(args[1]));
46+
if (args.length == 2) {
47+
patchFoxLoaderClassesDir(new File(args[0]));
48+
patchFoxLoaderResourcesDir(new File(args[1]));
49+
} else {
50+
patchFoxLoaderFinalJar(new File(args[0]));
51+
}
4452
}
4553

4654
public static void patchFoxLoaderClassesDir(File classesDir) throws IOException {
@@ -51,6 +59,85 @@ public static void patchFoxLoaderResourcesDir(File resourcesDir) throws IOExcept
5159
generateDependenciesResources(resourcesDir);
5260
}
5361

62+
// Patching this late allow FoxLoader to inline BuildConfig values while having mods not inline them.
63+
public static void patchFoxLoaderFinalJar(File foxloaderJar) throws IOException {
64+
File backup = new File(foxloaderJar.getParentFile(), foxloaderJar.getName() + ".bak");
65+
if (backup.exists() && !backup.delete()) {
66+
throw new IOException("Failed to delete backup jar");
67+
}
68+
if (!foxloaderJar.renameTo(backup)) {
69+
throw new IOException("Failed to rename loader jar");
70+
}
71+
try (ZipInputStream zipInputStream = new ZipInputStream(
72+
new BufferedInputStream(Files.newInputStream(backup.toPath())));
73+
ZipOutputStream zipOutputStream = new ZipOutputStream(
74+
new BufferedOutputStream(Files.newOutputStream(foxloaderJar.toPath())))) {
75+
zipOutputStream.setLevel(9);
76+
ZipEntry input;
77+
while ((input = zipInputStream.getNextEntry()) != null) {
78+
if (input.isDirectory()) continue;
79+
String pathName = input.getName();
80+
ZipEntry output = new ZipEntry(pathName);
81+
zipOutputStream.putNextEntry(output);
82+
if ("com/fox2code/foxloader/launcher/BuildConfig.class".equals(pathName)) {
83+
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
84+
IOUtils.copy(zipInputStream, byteArrayOutputStream);
85+
zipOutputStream.write(patchBuildConfig(byteArrayOutputStream.toByteArray()));
86+
} else {
87+
IOUtils.copy(zipInputStream, zipOutputStream);
88+
}
89+
zipOutputStream.closeEntry();
90+
zipInputStream.closeEntry();
91+
}
92+
zipOutputStream.finish();
93+
}
94+
if (backup.exists() && !backup.delete()) {
95+
throw new IOException("Failed to delete backup jar");
96+
}
97+
}
98+
99+
private static byte[] patchBuildConfig(byte[] buildConfig) {
100+
ClassNode classNode = new ClassNode();
101+
new ClassReader(buildConfig).accept(classNode, 0);
102+
if (TransformerUtils.findMethod(classNode, "<clinit>") != null) {
103+
throw new RuntimeException("<clinit>()V detected in BuildConfig!");
104+
}
105+
MethodNode clInit = new MethodNode(ACC_STATIC, "<clinit>", "()V", null, null);
106+
for (FieldNode fieldNode : classNode.fields) {
107+
if (fieldNode.access != (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) {
108+
throw new RuntimeException("Invalid field access: " + fieldNode.access);
109+
}
110+
AbstractInsnNode constInsn;
111+
switch (fieldNode.desc) {
112+
case "Ljava/lang/String;":
113+
constInsn = new LdcInsnNode(fieldNode.value);
114+
break;
115+
case "I":
116+
case "Z":
117+
if (fieldNode.value instanceof Integer) {
118+
constInsn = TransformerUtils.getNumberInsn((Integer) fieldNode.value);
119+
} else if (fieldNode.value instanceof Boolean) {
120+
constInsn = TransformerUtils.getBooleanInsn((Boolean) fieldNode.value);
121+
} else {
122+
throw new RuntimeException("WTF???");
123+
}
124+
break;
125+
default:
126+
throw new RuntimeException("Unsupported desc: \"" + fieldNode.desc + "\"");
127+
}
128+
fieldNode.value = null;
129+
clInit.instructions.add(constInsn);
130+
clInit.instructions.add(new FieldInsnNode(PUTSTATIC,
131+
"com/fox2code/foxloader/launcher/BuildConfig",
132+
fieldNode.name, fieldNode.desc));
133+
}
134+
clInit.instructions.add(new InsnNode(RETURN));
135+
classNode.methods.add(clInit);
136+
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
137+
classNode.accept(classWriter);
138+
return classWriter.toByteArray();
139+
}
140+
54141
private static void patchFoxClassLoader(File classesDir) throws IOException {
55142
File foxClassLoader = new File(classesDir, "com/fox2code/foxloader/launcher/FoxClassLoader.class");
56143
File source = foxClassLoader;
@@ -139,7 +226,7 @@ private static void generateDependenciesResources(File resourcesDir) throws IOEx
139226
private static void generateDependenciesResource(
140227
File file, DependencyHelper.Dependency[] dependencies) throws IOException {
141228
try (PrintStream printStream = new PrintStream(file, "UTF-8")) {
142-
printStream.println("# Autogenerated by FoxLoader " + BuildConfig.FOXLOADER_VERSION);
229+
printStream.println("# Autogenerated by " + BuildConfig.FOXLOADER_DISPLAY);
143230
for (DependencyHelper.Dependency dependency : dependencies) {
144231
printStream.println(dependency.javaSupport + " " + dependency.name);
145232
}

0 commit comments

Comments
 (0)