Skip to content

Commit 1505a5f

Browse files
committed
Add support to run some Java11 code on Java8 JVMs
1 parent 56e9ce4 commit 1505a5f

28 files changed

+1304
-4
lines changed

betacraft/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ dependencies {
55
// compileOnly("com.github.Vatuu:discord-rpc:1.6.2")
66
// compileOnly("aaaaaaaa:bbbbbbb:1.0.0") something.repo/aaaaaaaa/bbbbbbb/1.0.0/bbbbbbb-1.0.0.pom
77

8-
// Let's use betacraft project fir client dev runs.
8+
// Let's use betacraft project for client dev runs.
99
implementation(project(":client"))
1010

1111
//noinspection GradlePackageUpdate

common/src/main/java/com/fox2code/foxloader/launcher/FoxClassLoader.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,13 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
6464
Class<?> c = findLoadedClass(name);
6565
if (c == null) {
6666
URL resource = findResource(name.replace('.', '/') + ".class");
67-
if (resource != null)
67+
if (resource != null) {
6868
c = findClass(name, resource);
69-
else
69+
} else try {
7070
c = super.loadClass(name, false);
71+
} catch (SecurityException securityException) {
72+
throw new ClassNotFoundException(name, securityException);
73+
}
7174
}
7275
return c;
7376
}

common/src/main/java/com/fox2code/foxloader/loader/FoxWrappedExtensions.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import com.fox2code.foxloader.launcher.ClassTransformer;
44
import com.fox2code.foxloader.launcher.FoxClassLoader;
5+
import com.fox2code.foxloader.launcher.utils.Platform;
56
import com.fox2code.foxloader.loader.rebuild.ClassDataProvider;
7+
import com.fox2code.foxloader.loader.transformer.JvmCompatTransformer;
68
import org.objectweb.asm.*;
9+
import org.objectweb.asm.commons.ClassRemapper;
710

811
import java.util.logging.Logger;
912

@@ -20,7 +23,10 @@ public final class FoxWrappedExtensions extends FoxClassLoader.WrappedExtensions
2023
public byte[] computeFrames(byte[] classData) {
2124
ClassReader classReader = new ClassReader(classData);
2225
ClassWriter classWriter = classDataProvider.newClassWriter();
23-
classReader.accept(new ClassVisitor(ClassTransformer.ASM_BUILD, classWriter) {
26+
JvmCompatTransformer jvmCompatTransformer = PreLoader.getJvmCompatTransformer();
27+
classReader.accept(new ClassVisitor(ClassTransformer.ASM_BUILD,
28+
jvmCompatTransformer == null ? classWriter : // Fix Java11 code
29+
new ClassRemapper(classWriter, jvmCompatTransformer)) {
2430
@Override
2531
public MethodVisitor visitMethod(int access, final String name, final String descriptor, String signature, String[] exceptions) {
2632
return new MethodVisitor(ClassTransformer.ASM_BUILD, super.visitMethod(access, name, descriptor, signature, exceptions)) {

common/src/main/java/com/fox2code/foxloader/loader/PreLoader.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import com.fox2code.foxloader.launcher.BuildConfig;
44
import com.fox2code.foxloader.launcher.FoxClassLoader;
55
import com.fox2code.foxloader.launcher.FoxLauncher;
6+
import com.fox2code.foxloader.launcher.utils.Platform;
67
import com.fox2code.foxloader.loader.rebuild.ClassDataProvider;
78
import com.fox2code.foxloader.loader.transformer.*;
89
import org.objectweb.asm.ClassReader;
910
import org.objectweb.asm.ClassWriter;
11+
import org.objectweb.asm.commons.ClassRemapper;
1012
import org.objectweb.asm.tree.ClassNode;
1113

1214
import java.io.*;
@@ -33,6 +35,7 @@ public class PreLoader {
3335
private static final byte[] metaInf = ("Manifest-Version: 1.0\n" +
3436
"FoxLoader-Transformer-Version: " + BuildConfig.FOXLOADER_TRANSFORMER_VERSION +
3537
"Multi-Release: true\n").getBytes(StandardCharsets.UTF_8);
38+
private static JvmCompatTransformer jvmCompatTransformer = null;
3639

3740
static {
3841
preLoadMetaJarHash.addString(BuildConfig.REINDEV_VERSION);
@@ -77,6 +80,9 @@ public static void patchForMixin(ClassNode classNode, String className) {
7780
e.printStackTrace();
7881
}
7982
}
83+
if (jvmCompatTransformer != null) {
84+
jvmCompatTransformer.transform(classNode, className);
85+
}
8086
}
8187

8288
static void addCoreMod(File coreMod) {
@@ -100,6 +106,18 @@ static void initializePrePatch(boolean client) {
100106

101107
static void loadPrePatches(boolean client) {
102108
preTransformers.clear();
109+
if (FoxLauncher.getFoxClassLoader() != null) {
110+
final int jvmVersion = Platform.getJvmVersion();
111+
if (jvmVersion < 11) {
112+
ModLoader.foxLoader.logger.info( // Tell the user we are doing that :3
113+
"Registering JvmCompatTransformer to run Java11 code on Java" + jvmVersion);
114+
preTransformers.add(jvmCompatTransformer = new JvmCompatTransformer(jvmVersion));
115+
} else {
116+
ModLoader.foxLoader.logger.info( // Tell the user their jvm version
117+
"You are currently running on Java" + jvmVersion);
118+
jvmCompatTransformer = null;
119+
}
120+
}
103121
registerPrePatch(new VarNameTransformer());
104122
registerPrePatch(new RegistryTransformer());
105123
if (client) {
@@ -263,4 +281,8 @@ public void freeze() {
263281
this.cache = this.makeHash();
264282
}
265283
}
284+
285+
static JvmCompatTransformer getJvmCompatTransformer() {
286+
return jvmCompatTransformer;
287+
}
266288
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.fox2code.foxloader.loader.transformer;
2+
3+
import org.objectweb.asm.commons.Remapper;
4+
import org.objectweb.asm.tree.ClassNode;
5+
6+
import java.util.Arrays;
7+
import java.util.HashSet;
8+
9+
/**
10+
* Allow to run up to Java11 code on a Java8 runtime
11+
*/
12+
public final class JvmCompatTransformer extends Remapper implements PreClassTransformer {
13+
private static final HashSet<String> java9Cls = new HashSet<>(Arrays.asList(
14+
"java/lang/invoke/StringConcatFactory", "java/util/concurrent/Flow"
15+
));
16+
private static final int maxClassVersionSupport = V11;
17+
public final int classVersionSupport;
18+
19+
public JvmCompatTransformer(int jvmVersion) {
20+
int classVersionSupportTmp;
21+
switch (jvmVersion) {
22+
default:
23+
throw new IllegalStateException("JvmCompatTransformer should not be used on java" + jvmVersion);
24+
case 8:
25+
classVersionSupportTmp = V1_8;
26+
break;
27+
case 9:
28+
classVersionSupportTmp = V9;
29+
break;
30+
case 10:
31+
classVersionSupportTmp = V10;
32+
}
33+
this.classVersionSupport = classVersionSupportTmp;
34+
}
35+
36+
@Override
37+
public void transform(ClassNode classNode, String className) {
38+
if (classNode.version <= this.classVersionSupport ||
39+
classNode.version > maxClassVersionSupport) {
40+
return;
41+
}
42+
classNode.version = this.classVersionSupport;
43+
if (this.classVersionSupport < V9) {
44+
classNode.module = null;
45+
}
46+
}
47+
48+
@Override
49+
public String map(String internalName) {
50+
if (this.classVersionSupport > V9 && "notjava/util/concurrent/Flow".equals(internalName))
51+
return "java/util/concurrent/Flow"; // <- Used in some java11 fallbacks
52+
return (this.classVersionSupport < V9 && java9Cls.contains(internalName)) ||
53+
internalName.startsWith("java/net/http/") ? "not" + internalName : internalName;
54+
}
55+
}

common/src/main/resources/assets/foxloader/lang/ru_RU.lang

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ mods.updateMod=Обновить мод
88
mods.updateReIndev=Обновить ReIndev
99
mods.configureMod=Настроить мод
1010
mods.pack=Модпаки
11+
command.usage.id-meta=Должен использоваться только 1 аргумент, используйте либо id, либо id:meta
12+
command.usage.id-meta-twice=Следует использовать только 2 аргумента, для каждого из них, используйте либо id, либо id:meta
13+
command.error.creative-only=Вы должны быть в творческом режиме, чтобы выполнить эту команду
14+
command.error.no-area-selected=Перед использованием этой команды необходимо выделить область!
15+
command.error.invalid-block-id=Неверный id блока: %s
16+
command.error.invalid-meta-data=Неверные метаданные: %s
17+
command.error.changing-too-many-blocks=Вы собирались изменить %s блоков и это кажется слишком...
18+
command.error.internal-error=Во время выполнения команды произошла внутренняя ошибка.
19+
command.changing-blocks=Изменение %s блоков...
20+
command.done=Готово!
1121

1222

1323
# Installer strings (Note: Usable by mods too)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package javax.xml.bind.annotation.adapters;
2+
3+
import javax.xml.bind.DatatypeConverter;
4+
5+
/**
6+
* Partial Implementation For Java9 support if
7+
* {@code javax.xml.bind.annotation.adapters.HexBinaryAdapter} is missing
8+
*/
9+
@Deprecated
10+
public class HexBinaryAdapter extends XmlAdapter<byte[], String> {
11+
@Override
12+
@Deprecated
13+
public String unmarshal(byte[] v) throws Exception {
14+
return DatatypeConverter.printHexBinary(v);
15+
}
16+
17+
@Override
18+
@Deprecated
19+
public byte[] marshal(String v) throws Exception {
20+
return DatatypeConverter.parseHexBinary(v);
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package javax.xml.bind.annotation.adapters;
2+
3+
/**
4+
* Partial Implementation For Java9 support if
5+
* {@code javax.xml.bind.annotation.adapters.XmlAdapter} is missing
6+
*/
7+
@Deprecated
8+
@SuppressWarnings({"DeprecatedIsStillUsed"})
9+
public abstract class XmlAdapter<To,From> {
10+
@Deprecated
11+
protected XmlAdapter() {}
12+
13+
@Deprecated
14+
public abstract To marshal(From v) throws Exception;
15+
16+
@Deprecated
17+
public abstract From unmarshal(To v) throws Exception;
18+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package notjava.lang.invoke;
2+
3+
import java.lang.invoke.CallSite;
4+
import java.lang.invoke.ConstantCallSite;
5+
import java.lang.invoke.MethodHandles;
6+
import java.lang.invoke.MethodType;
7+
8+
public class StringConcatFactory {
9+
private static final char TAG_ARG = '\u0001';
10+
private static final char TAG_CONST = '\u0002';
11+
12+
public static CallSite makeConcat(MethodHandles.Lookup lookup,
13+
String name,
14+
MethodType concatType) {
15+
throw new UnsupportedOperationException("Not yet implemented");
16+
}
17+
18+
public static CallSite makeConcatWithConstants(
19+
MethodHandles.Lookup lookup, String name, MethodType concatType,
20+
String recipe, Object... constants) throws NoSuchMethodException, IllegalAccessException {
21+
String hardcodedMethodName;
22+
switch (recipe) {
23+
case "https://\u0001/":
24+
hardcodedMethodName = "httpsUrlFormat";
25+
break;
26+
case "wss://\u0001/":
27+
hardcodedMethodName = "wssUrlFormat";
28+
break;
29+
case "Request failed: \u0001":
30+
hardcodedMethodName = "requestFailedFormat";
31+
break;
32+
case "Location header not returned: \u0001":
33+
hardcodedMethodName = "locationHeaderNotReturnedFormat";
34+
break;
35+
default:
36+
throw new UnsupportedOperationException("Not yet implemented");
37+
}
38+
return new ConstantCallSite(lookup.findStatic(HardCodedImpls.class, hardcodedMethodName, concatType));
39+
}
40+
41+
public static class HardCodedImpls {
42+
public static String httpsUrlFormat(String text) {
43+
return String.format("https://%s/", text);
44+
}
45+
46+
public static String wssUrlFormat(String text) {
47+
return String.format("wss://%s/", text);
48+
}
49+
50+
public static String requestFailedFormat(String text) {
51+
return String.format("Request failed: %s", text);
52+
}
53+
54+
public static String locationHeaderNotReturnedFormat(String text) {
55+
return String.format("Location header not returned: %s", text);
56+
}
57+
}
58+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package notjava.net.http;
2+
3+
import java.io.IOException;
4+
import java.net.Proxy;
5+
import java.net.ProxySelector;
6+
import java.net.SocketAddress;
7+
import java.net.URI;
8+
import notjava.net.http.impl.HttpClientBuilderImpl;
9+
import java.time.Duration;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.Optional;
13+
import java.util.concurrent.CompletableFuture;
14+
15+
/**
16+
* Partial Implementation For pre Java11 JVMs
17+
*/
18+
public abstract class HttpClient {
19+
protected HttpClient() {}
20+
21+
public static HttpClient newHttpClient() {
22+
return newBuilder().build();
23+
}
24+
25+
public static Builder newBuilder() {
26+
return new HttpClientBuilderImpl();
27+
}
28+
29+
public interface Builder {
30+
ProxySelector NO_PROXY = new ProxySelector() {
31+
@Override
32+
public List<Proxy> select(URI uri) {
33+
return Collections.singletonList(Proxy.NO_PROXY);
34+
}
35+
36+
@Override
37+
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
38+
if (uri == null || sa == null || ioe == null) {
39+
throw new IllegalArgumentException("Arguments can't be null.");
40+
}
41+
}
42+
};
43+
44+
Builder connectTimeout(Duration duration);
45+
46+
Builder proxy(ProxySelector proxySelector);
47+
48+
HttpClient build();
49+
}
50+
51+
public abstract Optional<Duration> connectTimeout();
52+
53+
public abstract Optional<ProxySelector> proxy();
54+
55+
public abstract <T> HttpResponse<T> send(
56+
HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler)
57+
throws IOException, InterruptedException;
58+
59+
public abstract <T> CompletableFuture<HttpResponse<T>> sendAsync(
60+
HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler);
61+
62+
public WebSocket.Builder newWebSocketBuilder() {
63+
throw new UnsupportedOperationException();
64+
}
65+
}

0 commit comments

Comments
 (0)