Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ release_type = beta
maven_group = com.cleanroommc
archives_base_name = groovyscript

groovy_version = 4.0.8
groovy_version = 4.0.13
mod_ref_path=com.cleanroommc.groovyscript.GroovyScript
debug_load_all_mods = true

Expand Down
106 changes: 0 additions & 106 deletions src/main/java/com/cleanroommc/groovyscript/core/mixin/Java8Mixin.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.cleanroommc.groovyscript.core.mixin.groovy;

import com.cleanroommc.groovyscript.sandbox.transformer.AsmDecompileHelper;
import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.ast.decompiled.AsmDecompiler;
import org.codehaus.groovy.ast.decompiled.ClassStub;
import org.codehaus.groovy.util.URLStreams;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;

@Mixin(value = AsmDecompiler.class, remap = false)
public class AsmDecompilerMixin {

@Shadow
@Final
private static Map<URI, SoftReference<ClassStub>> stubCache;

@Inject(method = "parseClass", at = @At("HEAD"), cancellable = true)
private static void parseClass(URL url, CallbackInfoReturnable<ClassStub> cir) {
URI uri;
try {
uri = url.toURI();
} catch (URISyntaxException e) {
throw new GroovyRuntimeException(e);
}

SoftReference<ClassStub> ref = stubCache.get(uri);
ClassStub stub = (ref != null ? ref.get() : null);
if (stub == null) {
try (InputStream stream = new BufferedInputStream(URLStreams.openUncachedStream(url))) {
ClassReader classReader = new ClassReader(stream);
ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
byte[] bytes = writer.toByteArray();
if (!AsmDecompileHelper.remove(classNode.visibleAnnotations, AsmDecompileHelper.SIDE)) {
bytes = AsmDecompileHelper.transform(classNode.name, bytes);
}

// now decompile the class normally
groovyjarjarasm.asm.ClassReader classReader2 = new groovyjarjarasm.asm.ClassReader(bytes);
groovyjarjarasm.asm.ClassVisitor decompiler = AsmDecompileHelper.makeGroovyDecompiler();
classReader2.accept(decompiler, ClassReader.SKIP_FRAMES);
stub = AsmDecompileHelper.getDecompiledClass(decompiler);
stubCache.put(uri, new SoftReference<>(stub));
} catch (IOException |
ClassNotFoundException |
NoSuchFieldException |
NoSuchMethodException |
IllegalAccessException |
InvocationTargetException |
InstantiationException e) {
throw new RuntimeException(e);
}
}
cir.setReturnValue(stub);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.cleanroommc.groovyscript.core.mixin;
package com.cleanroommc.groovyscript.core.mixin.groovy;

import com.cleanroommc.groovyscript.sandbox.transformer.GroovyCodeFactory;
import org.codehaus.groovy.ast.ClassNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.cleanroommc.groovyscript.core.mixin;
package com.cleanroommc.groovyscript.core.mixin.groovy;

import com.cleanroommc.groovyscript.api.IDynamicGroovyProperty;
import groovy.lang.MetaClassImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void load(boolean run, boolean loadClasses) throws Exception {
protected void loadScripts(GroovyScriptEngine engine, Binding binding, Set<File> executedClasses, boolean run) {
for (File scriptFile : getScriptFiles()) {
if (!executedClasses.contains(scriptFile)) {
Class<?> clazz = loadScriptClass(engine, scriptFile, true);
Class<?> clazz = loadScriptClass(engine, scriptFile);
if (clazz == null) {
GroovyLog.get().errorMC("Error loading script for {}", scriptFile.getPath());
GroovyLog.get().errorMC("Did you forget to register your class file in your run config?");
Expand All @@ -128,7 +128,7 @@ protected void loadScripts(GroovyScriptEngine engine, Binding binding, Set<File>

protected void loadClassScripts(GroovyScriptEngine engine, Binding binding, Set<File> executedClasses, boolean run) {
for (File classFile : getClassFiles()) {
Class<?> clazz = loadScriptClass(engine, classFile, false);
Class<?> clazz = loadScriptClass(engine, classFile);
if (clazz == null) {
// loading script fails if the file is a script that depends on a class file that isn't loaded yet
// we cant determine if the file is a script or a class
Expand Down Expand Up @@ -219,7 +219,7 @@ public static String getRelativePath(String source) {
}
}

private Class<?> loadScriptClass(GroovyScriptEngine engine, File file, boolean printError) {
private Class<?> loadScriptClass(GroovyScriptEngine engine, File file) {
Class<?> scriptClass = null;
try {
try {
Expand All @@ -237,9 +237,8 @@ private Class<?> loadScriptClass(GroovyScriptEngine engine, File file, boolean p

// if the file is still not found something went wrong
} catch (Exception e) {
if (printError) {
GroovyLog.get().exception(e);
}
GroovyLog.get().fatalMC("An error occurred while trying to load script class {}", file.toString());
GroovyLog.get().exception(e);
}
return scriptClass;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.cleanroommc.groovyscript.sandbox.transformer;

import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.codehaus.groovy.ast.decompiled.ClassStub;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

public class AsmDecompileHelper {

public static final String SIDE = FMLLaunchHandler.side().name();
private static final boolean DEBUG = false;
private static final List<String> transformerExceptions = new ArrayList<>();
private static Class<?> decompilerClass;
private static Constructor<?> decompilerConstructor;
private static Field resultField;

static {
transformerExceptions.add("javax.");
transformerExceptions.add("argo.");
transformerExceptions.add("org.objectweb.asm.");
transformerExceptions.add("com.google.common.");
transformerExceptions.add("org.bouncycastle.");
transformerExceptions.add("net.minecraft.launchwrapper.injector.");
}

public static groovyjarjarasm.asm.ClassVisitor makeGroovyDecompiler() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
if (decompilerClass == null) {
decompilerClass = Class.forName("org.codehaus.groovy.ast.decompiled.AsmDecompiler$DecompilingVisitor");
decompilerConstructor = decompilerClass.getDeclaredConstructors()[0];
decompilerConstructor.setAccessible(true);
}
return (groovyjarjarasm.asm.ClassVisitor) decompilerConstructor.newInstance();
}

public static ClassStub getDecompiledClass(groovyjarjarasm.asm.ClassVisitor classVisitor) throws NoSuchFieldException, IllegalAccessException {
if (resultField == null) {
resultField = decompilerClass.getDeclaredField("result");
resultField.setAccessible(true);
}
return (ClassStub) resultField.get(classVisitor);
}

public static byte[] transform(String className, byte[] bytes) {
for (String s : transformerExceptions) {
if (className.startsWith(s)) {
return bytes;
}
}
String untransformed = FMLDeobfuscatingRemapper.INSTANCE.unmap(className.replace('.', '/')).replace('/', '.');
String transformed = FMLDeobfuscatingRemapper.INSTANCE.map(className.replace('.', '/')).replace('/', '.');
for (IClassTransformer transformer : Launch.classLoader.getTransformers()) {
bytes = transformer.transform(untransformed, transformed, bytes);
}
return bytes;
}

public static boolean remove(List<AnnotationNode> anns, String side) {
if (anns == null) {
return false;
}
for (AnnotationNode ann : anns) {
if (ann.desc.equals(Type.getDescriptor(SideOnly.class))) {
if (ann.values != null) {
for (int x = 0; x < ann.values.size() - 1; x += 2) {
Object key = ann.values.get(x);
Object value = ann.values.get(x + 1);
if (key instanceof String && key.equals("value")) {
if (value instanceof String[]) {
if (!((String[]) value)[1].equals(side)) {
return true;
}
}
}
}
}
}
}
return false;
}

public static class DecompileVisitor extends ClassVisitor {

private ClassStub result;

public DecompileVisitor() {
super(Opcodes.ASM5);
}
}
}
6 changes: 3 additions & 3 deletions src/main/resources/mixin.groovyscript.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
"minVersion": "0.8",
"compatibilityLevel": "JAVA_8",
"mixins": [
"CompUnitClassGenMixin",
"EventBusMixin",
"FluidStackMixin",
"ForgeRegistryMixin",
"FurnaceRecipeMixin",
"InventoryCraftingAccess",
"ItemMixin",
"ItemStackMixin",
"Java8Mixin",
"LoaderControllerMixin",
"MetaClassImplMixin",
"OreDictionaryAccessor",
"SlotCraftingAccess",
"groovy.AsmDecompilerMixin",
"groovy.CompUnitClassGenMixin",
"groovy.MetaClassImplMixin",
"loot.LootPoolAccessor",
"loot.LootTableAccessor"
],
Expand Down