Skip to content

Commit 1a73101

Browse files
committed
gen types & injector changes
-added 3 options for gentypes -injecting errors should be more informative
1 parent bb8f721 commit 1a73101

File tree

5 files changed

+160
-74
lines changed

5 files changed

+160
-74
lines changed

src/main/java/de/blazemcworld/jsscripts/Injector.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import org.objectweb.asm.ClassWriter;
66
import org.objectweb.asm.tree.ClassNode;
77
import org.objectweb.asm.tree.MethodNode;
8+
import org.objectweb.asm.util.CheckClassAdapter;
89
import org.spongepowered.asm.transformers.MixinClassWriter;
910

11+
import java.io.PrintWriter;
12+
import java.io.StringWriter;
1013
import java.lang.instrument.ClassFileTransformer;
1114
import java.lang.instrument.Instrumentation;
1215
import java.security.ProtectionDomain;
@@ -80,7 +83,17 @@ public static void transformBytes(String className, Function<byte[], byte[]> tra
8083
@Override
8184
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
8285
if (!Objects.equals(slashName, className)) return null;
83-
return transformer.apply(classfileBuffer);
86+
byte[] out = transformer.apply(classfileBuffer);
87+
88+
if (out != null) {
89+
StringWriter sw = new StringWriter();
90+
CheckClassAdapter.verify(new ClassReader(out), JsScripts.class.getClassLoader(), false, new PrintWriter(sw));
91+
if (sw.toString().length() > 0) {
92+
JsScripts.LOGGER.warn(sw);
93+
}
94+
}
95+
96+
return out;
8497
}
8598
};
8699

src/main/java/de/blazemcworld/jsscripts/JsScripts.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import net.minecraft.text.Text;
77
import org.apache.logging.log4j.LogManager;
88
import org.apache.logging.log4j.Logger;
9-
import org.graalvm.polyglot.Context;
109

1110
public class JsScripts implements ModInitializer {
1211

@@ -21,9 +20,6 @@ public static void displayChat(Text t) {
2120

2221
@Override
2322
public void onInitialize() {
24-
try (Context ctx = Context.create("js")) {
25-
ctx.eval("js", "console.log(\"GraalJS initialized!\")");
26-
}
2723
ByteBuddyAgent.install();
2824
new JsScriptsCmd().register();
2925
ScriptManager.init();

src/main/java/de/blazemcworld/jsscripts/JsScriptsCmd.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ public void register() {
2828
)
2929
.then(literal("gen_types")
3030
.executes((e) -> {
31-
JsScripts.displayChat(Text.literal("Invalid usage! Try one of:").formatted(Formatting.AQUA));
32-
JsScripts.displayChat(Text.literal("/jsscripts gen_types *").formatted(Formatting.AQUA));
33-
JsScripts.displayChat(Text.literal("/jsscripts gen_types some.package.*").formatted(Formatting.AQUA));
34-
JsScripts.displayChat(Text.literal("/jsscripts gen_types some.class.Name").formatted(Formatting.AQUA));
31+
JsScripts.displayChat(Text.literal("Invalid usage! Usage:").formatted(Formatting.AQUA));
32+
JsScripts.displayChat(Text.literal("/jsscripts gen_types <args>").formatted(Formatting.AQUA));
33+
JsScripts.displayChat(Text.literal("Specific classes: the.class.Name"));
34+
JsScripts.displayChat(Text.literal("All in a package: some.package.*"));
35+
JsScripts.displayChat(Text.literal("Enable asm for methods: -asm"));
36+
JsScripts.displayChat(Text.literal("Remap asm output: -remap"));
37+
JsScripts.displayChat(Text.literal("Show private methods: -private"));
38+
JsScripts.displayChat(Text.literal("Example: /jsscripts gen_types java.lang.System -private -asm"));
3539
return 1;
3640
})
3741
.then(argument("classes", StringArgumentType.greedyString())

src/main/java/de/blazemcworld/jsscripts/Script.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public Script(File file) throws Exception {
2525
ctx = Context.newBuilder()
2626
.allowAllAccess(true)
2727
.logHandler(System.out)
28+
.option("engine.WarnInterpreterOnly", "false")
2829
.build();
2930
Value bindings = ctx.getBindings("js");
3031
bindings.putMember("script", this);

src/main/java/de/blazemcworld/jsscripts/TypingGen.java

Lines changed: 137 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,26 @@
1010
import org.objectweb.asm.tree.ClassNode;
1111
import org.objectweb.asm.tree.FieldNode;
1212
import org.objectweb.asm.tree.MethodNode;
13+
import org.objectweb.asm.util.Textifier;
14+
import org.objectweb.asm.util.TraceMethodVisitor;
1315

1416
import java.io.File;
1517
import java.io.InputStream;
18+
import java.io.PrintWriter;
19+
import java.io.StringWriter;
1620
import java.net.URI;
1721
import java.net.URL;
1822
import java.nio.file.*;
1923
import java.util.*;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
2026
import java.util.stream.Collectors;
2127
import java.util.stream.Stream;
2228

2329
public class TypingGen {
2430

31+
private static final Pattern remapAsmPattern = Pattern.compile("net/minecraft/class_\\d+(\\.(method|field)_\\d+)?");
32+
2533
public static void genTypesIn(String targets) {
2634
try {
2735
Path out = JsScripts.MC.runDirectory.toPath()
@@ -34,90 +42,104 @@ public static void genTypesIn(String targets) {
3442
Set<String> all = new HashSet<>();
3543
Set<String> forced = new HashSet<>();
3644

45+
Set<String> optionFlags = new HashSet<>();
46+
47+
boolean needsScan = false;
48+
3749
for (String target : targets.split(" ")) {
38-
if (!target.endsWith("*")) {
39-
forced.add(Mappings.remapClass("named", Mappings.current(), target));
50+
if (target.endsWith("*")) {
51+
needsScan = true;
52+
continue;
53+
}
54+
if (target.startsWith("-")) {
55+
optionFlags.add(target.substring(1).toLowerCase());
56+
continue;
4057
}
58+
forced.add(Mappings.remapClass("named", Mappings.current(), target));
4159
}
4260

43-
Set<String> checkedPackages = new HashSet<>();
61+
if (needsScan) {
62+
Set<String> checkedPackages = new HashSet<>();
4463

45-
List<FileSystem> fsList = new ArrayList<>();
64+
List<FileSystem> fsList = new ArrayList<>();
4665

47-
for (Class<?> c : Injector.listLoadedClasses()) {
48-
String p = c.getPackageName();
49-
while (true) {
50-
if (checkedPackages.contains(p)) break;
51-
checkedPackages.add(p);
66+
for (Class<?> c : Injector.listLoadedClasses()) {
67+
String p = c.getPackageName();
68+
while (true) {
69+
if (checkedPackages.contains(p)) break;
70+
checkedPackages.add(p);
5271

53-
Enumeration<URL> enu = cl.getResources(p.replace('.', '/'));
72+
Enumeration<URL> enu = cl.getResources(p.replace('.', '/'));
5473

55-
while (enu.hasMoreElements()) {
56-
URI u = enu.nextElement().toURI();
74+
while (enu.hasMoreElements()) {
75+
URI u = enu.nextElement().toURI();
5776

58-
try {
59-
FileSystems.getFileSystem(u);
60-
} catch (FileSystemNotFoundException err) {
61-
HashMap<String, Boolean> options = new HashMap<>();
62-
options.put("create", true);
63-
fsList.add(FileSystems.newFileSystem(u, options));
64-
}
77+
try {
78+
FileSystems.getFileSystem(u);
79+
} catch (FileSystemNotFoundException err) {
80+
HashMap<String, Boolean> options = new HashMap<>();
81+
options.put("create", true);
82+
fsList.add(FileSystems.newFileSystem(u, options));
83+
}
6584

66-
try (Stream<Path> javaClasses = Files.walk(Path.of(u))) {
67-
for (Path cp : javaClasses.collect(Collectors.toSet())) {
68-
String str = cp.toString();
69-
if (str.endsWith(".class")) {
70-
all.add(str.substring(1).replaceAll("/", ".").substring(0, str.length() - 7));
85+
try (Stream<Path> javaClasses = Files.walk(Path.of(u))) {
86+
for (Path cp : javaClasses.collect(Collectors.toSet())) {
87+
String str = cp.toString();
88+
if (str.endsWith(".class")) {
89+
all.add(str.substring(1).replaceAll("/", ".").substring(0, str.length() - 7));
90+
}
7191
}
7292
}
7393
}
74-
}
7594

76-
if (!p.contains(".")) {
77-
break;
95+
if (!p.contains(".")) {
96+
break;
97+
}
98+
p = p.substring(0, p.indexOf("."));
7899
}
79-
p = p.substring(0, p.indexOf("."));
80100
}
81-
}
82101

83-
for (FileSystem fs : fsList) {
84-
fs.close();
85-
}
102+
for (FileSystem fs : fsList) {
103+
fs.close();
104+
}
86105

87-
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
106+
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
88107

89-
Stream<Path> javaClasses = Files.walk(fs.getPath("/"));
90-
for (Path path : javaClasses.collect(Collectors.toSet())) {
91-
List<String> parts = new ArrayList<>(Arrays.asList(path.toString().split("/")));
108+
Stream<Path> javaClasses = Files.walk(fs.getPath("/"));
109+
for (Path path : javaClasses.collect(Collectors.toSet())) {
110+
List<String> parts = new ArrayList<>(Arrays.asList(path.toString().split("/")));
92111

93-
if (parts.size() > 3) {
94-
parts = parts.subList(3, parts.size());
95-
if (parts.get(parts.size() - 1).endsWith(".class")) {
96-
String str = String.join(".", parts);
97-
all.add(str.substring(0, str.length() - 6));
112+
if (parts.size() > 3) {
113+
parts = parts.subList(3, parts.size());
114+
if (parts.get(parts.size() - 1).endsWith(".class")) {
115+
String str = String.join(".", parts);
116+
all.add(str.substring(0, str.length() - 6));
117+
}
98118
}
99119
}
100-
}
101-
javaClasses.close();
120+
javaClasses.close();
102121

103-
JsScripts.displayChat(Text.literal("Found " + (all.size() + forced.size()) + " classes!").formatted(Formatting.AQUA));
122+
JsScripts.displayChat(Text.literal("Found " + (all.size() + forced.size()) + " classes!").formatted(Formatting.AQUA));
104123

105-
all = all.stream().filter(name -> {
106-
if (name.startsWith("jdk")) return false;
107-
if (!name.contains(".")) return false;
124+
all = all.stream().filter(name -> {
125+
if (name.startsWith("jdk")) return false;
126+
if (!name.contains(".")) return false;
108127

109-
for (String target : targets.split(" ")) {
110-
if (target.endsWith("*") && name.startsWith(target.substring(0, target.length() - 1))) {
111-
return true;
128+
for (String target : targets.split(" ")) {
129+
if (target.endsWith("*") && name.startsWith(target.substring(0, target.length() - 1))) {
130+
return true;
131+
}
112132
}
113-
}
114-
115-
return false;
116-
}).collect(Collectors.toSet());
117133

118-
all.addAll(forced);
134+
return false;
135+
}).collect(Collectors.toSet());
119136

120-
JsScripts.displayChat(Text.literal("Of which " + all.size() + " classes match the filter.").formatted(Formatting.AQUA));
137+
all.addAll(forced);
138+
JsScripts.displayChat(Text.literal("Of which " + all.size() + " classes match the filter.").formatted(Formatting.AQUA));
139+
} else {
140+
all.addAll(forced);
141+
JsScripts.displayChat(Text.literal("Got " + all.size() + " classes.").formatted(Formatting.AQUA));
142+
}
121143

122144
JsScripts.displayChat(Text.literal("Starting generation...").formatted(Formatting.AQUA));
123145

@@ -133,7 +155,7 @@ public static void genTypesIn(String targets) {
133155
JsScripts.displayChat(Text.literal("Generating... " + progress + " of " + all.size() + " completed, est: " + est).formatted(Formatting.AQUA));
134156
}
135157
try {
136-
genTypesFor(currentName, out, cl);
158+
genTypesFor(currentName, out, cl, optionFlags);
137159
} catch (Exception e) {
138160
e.printStackTrace();
139161
errors++;
@@ -152,7 +174,7 @@ public static void genTypesIn(String targets) {
152174
}
153175
}
154176

155-
private static void genTypesFor(String currentName, Path outDir, ClassLoader cl) throws Exception {
177+
private static void genTypesFor(String currentName, Path outDir, ClassLoader cl, Set<String> optionFlags) throws Exception {
156178
currentName = Mappings.remapClass("named", Mappings.current(), currentName);
157179
InputStream stream = cl.getResourceAsStream(currentName.replace('.', '/') + ".class");
158180
if (stream == null) {
@@ -189,19 +211,20 @@ private static void genTypesFor(String currentName, Path outDir, ClassLoader cl)
189211
}
190212

191213
for (FieldNode field : classNode.fields) {
192-
if ((field.access & Opcodes.ACC_PUBLIC) == 0) {
214+
if ((field.access & Opcodes.ACC_PUBLIC) == 0 && !optionFlags.contains("private")) {
193215
continue;
194216
}
195217

196-
fields.add("%s%s: %s;".formatted(
218+
fields.add("%s%s%s: %s;".formatted(
219+
((field.access & Opcodes.ACC_PUBLIC) == 0) ? "private " : "",
197220
((field.access & Opcodes.ACC_STATIC) != 0) ? "static " : "",
198221
Mappings.remapField("named", namedName, Mappings.current(), "named", field.name),
199222
parseSingleDescriptor(field.desc, imports)
200223
));
201224
}
202225

203226
for (MethodNode method : classNode.methods) {
204-
if ((method.access & Opcodes.ACC_PUBLIC) == 0 || method.name.equals("<clinit>")) {
227+
if (((method.access & Opcodes.ACC_PUBLIC) == 0 || method.name.equals("<clinit>")) && !optionFlags.contains("private")) {
205228
continue;
206229
}
207230
List<String> params = new ArrayList<>();
@@ -213,18 +236,36 @@ private static void genTypesFor(String currentName, Path outDir, ClassLoader cl)
213236
}
214237

215238
if (method.name.equals("<init>")) {
216-
methods.add("constructor(%s);".formatted(
217-
String.join(", ", params)
239+
methods.add("constructor(%s); %s".formatted(
240+
String.join(", ", params),
241+
optionFlags.contains("asm") ? "/*" + getAsm(method, optionFlags.contains("remap")) + "*/" : ""
242+
));
243+
} else if (method.name.equals("<clinit>")) {
244+
methods.add("/*<clinit>%s*/".formatted(
245+
optionFlags.contains("asm") ? getAsm(method, optionFlags.contains("remap")) : ""
218246
));
219247
} else {
220248
String displayName = Mappings.remapMethod("named", namedName, Mappings.current(), "named", method.name);
221249

222-
methods.add("%s%s(%s): %s;%s".formatted(
250+
StringBuilder comment = new StringBuilder();
251+
252+
if (!displayName.equals(method.name)) {
253+
comment.append(method.name);
254+
}
255+
if (optionFlags.contains("asm")) {
256+
if (!comment.isEmpty()) {
257+
comment.append(" ");
258+
}
259+
comment.append(getAsm(method, optionFlags.contains("remap")));
260+
}
261+
262+
methods.add("%s%s%s(%s): %s;%s".formatted(
263+
((method.access & Opcodes.ACC_PUBLIC) == 0) ? "private " : "",
223264
((method.access & Opcodes.ACC_STATIC) != 0) ? "static " : "",
224265
displayName,
225266
String.join(", ", params),
226267
info.getRight(),
227-
displayName.equals(method.name) ? "" : " //" + method.name
268+
comment.length() == 0 ? "" : "/*" + comment + "*/"
228269
));
229270
}
230271
}
@@ -255,6 +296,37 @@ export default class %s%s {
255296
));
256297
}
257298

299+
private static String getAsm(MethodNode method, boolean remap) {
300+
Textifier t = new Textifier();
301+
TraceMethodVisitor tmv = new TraceMethodVisitor(t);
302+
method.accept(tmv);
303+
StringWriter sw = new StringWriter();
304+
t.print(new PrintWriter(sw));
305+
StringBuilder out = new StringBuilder();
306+
307+
if (remap) {
308+
Matcher m = remapAsmPattern.matcher(sw.toString());
309+
310+
while (m.find()) {
311+
if (m.group().contains(".")) {
312+
String[] parts = m.group().split("\\.");
313+
parts[1] = Mappings.remapField(Mappings.current(), parts[0], Mappings.current(), "named", parts[1]);
314+
parts[1] = Mappings.remapMethod(Mappings.current(), parts[0], Mappings.current(), "named", parts[1]);
315+
parts[0] = Mappings.remapClass(Mappings.current(), "named", parts[0]).replace('.', '/');
316+
317+
m.appendReplacement(out, parts[0] + "." + parts[1]);
318+
} else {
319+
m.appendReplacement(out, Mappings.remapClass(Mappings.current(), "named", m.group()).replace('.','/'));
320+
}
321+
}
322+
m.appendTail(out);
323+
} else {
324+
out.append(sw);
325+
}
326+
327+
return " {\n" + out + " \n }";
328+
}
329+
258330
private static String parseSingleDescriptor(String descriptor, HashMap<String, String> imports) throws Exception {
259331
Type t = Type.getType(descriptor);
260332

0 commit comments

Comments
 (0)