Skip to content

Commit

Permalink
Stuff (#178)
Browse files Browse the repository at this point in the history
* auto inject package declaration

* fix uri path issues

* transform mods import to var declaration

* fix

* canonicalize script path
  • Loading branch information
brachy84 authored Jun 16, 2024
1 parent 6b5a659 commit 27b4452
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 22 deletions.
6 changes: 6 additions & 0 deletions src/main/java/com/cleanroommc/groovyscript/GroovyScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ public static void initializeRunConfig(File minecraftHome) {
} else {
scriptPath = new File(minecraftHome, "groovy");
}
try {
scriptPath = scriptPath.getCanonicalFile();
} catch (IOException e) {
GroovyLog.get().error("Failed to canonicalize groovy script path '" + scriptPath + "'!");
GroovyLog.get().exception(e);
}
runConfigFile = new File(scriptPath, "runConfig.json");
resourcesFile = new File(scriptPath, "assets");
reloadRunConfig(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.cleanroommc.groovyscript.core.mixin.groovy;

import org.codehaus.groovy.ast.ImportNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import java.util.List;

@Mixin(value = ModuleNode.class, remap = false)
public interface ModuleNodeAccessor {

@Accessor("imports")
List<ImportNode> getModifiableImports();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void onCompile(Class<?> clazz, String basePath) {
GroovyLog.get().errorMC("The class doesnt seem to be compiled yet. (" + name + ")");
return;
}
if (!GroovyScriptSandbox.WRITE_CACHE) return;
if (!GroovyScriptSandbox.ENABLE_CACHE) return;
try {
File file = getDataFile(basePath);
file.getParentFile().mkdirs();
Expand All @@ -48,7 +48,7 @@ public void onCompile(Class<?> clazz, String basePath) {
}

public boolean readData(String basePath) {
if (this.data != null) return true;
if (this.data != null && GroovyScriptSandbox.ENABLE_CACHE) return true;
File file = getDataFile(basePath);
if (!file.exists()) return false;
try {
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/com/cleanroommc/groovyscript/sandbox/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.Files;

public class FileUtil {

public static String relativize(String rootPath, String longerThanRootPath) {
try {
longerThanRootPath = URLDecoder.decode(longerThanRootPath, "UTF-8");
} catch (UnsupportedEncodingException ignored) {
}

if (File.separatorChar != '/') {
longerThanRootPath = longerThanRootPath.replace('/', File.separatorChar);
}
int index = longerThanRootPath.indexOf(rootPath);
if (index < 0) {
if (longerThanRootPath.contains("%20")) {
longerThanRootPath = longerThanRootPath.replace("%20", " ");
index = longerThanRootPath.indexOf(rootPath);
}
if (index < 0) {
throw new IllegalArgumentException("The path '" + longerThanRootPath + "' does not contain the root path '" + rootPath + "'");
}
throw new IllegalArgumentException("The path '" + longerThanRootPath + "' does not contain the root path '" + rootPath + "'");
}
return longerThanRootPath.substring(index + rootPath.length() + 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.cleanroommc.groovyscript.helper.JsonHelper;
import com.cleanroommc.groovyscript.registry.ReloadableRegistryManager;
import com.cleanroommc.groovyscript.sandbox.transformer.GroovyScriptCompiler;
import com.cleanroommc.groovyscript.sandbox.transformer.GroovyScriptEarlyCompiler;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand Down Expand Up @@ -51,7 +52,7 @@ public class GroovyScriptSandbox extends GroovySandbox {
private int cacheVersion = 1;
private final Map<String, CompiledScript> index = new Object2ObjectOpenHashMap<>();

public static final boolean WRITE_CACHE = true;
public static final boolean ENABLE_CACHE = true;

private LoadStage currentLoadStage;

Expand Down Expand Up @@ -121,7 +122,7 @@ private void readIndex() {
}

private void writeIndex() {
if (!WRITE_CACHE) return;
if (!ENABLE_CACHE) return;
JsonObject json = new JsonObject();
json.addProperty("!DANGER!", "DO NOT EDIT THIS FILE!!!");
json.addProperty("version", this.cacheVersion);
Expand Down Expand Up @@ -263,14 +264,14 @@ protected Class<?> loadScriptClass(GroovyScriptEngine engine, File file) {
long lastModified = file.lastModified();
CompiledScript comp = this.index.get(relativeFile.toString());

if (comp != null && lastModified <= comp.lastEdited && comp.clazz == null && comp.readData(this.cacheRoot.getPath())) {
if (ENABLE_CACHE && comp != null && lastModified <= comp.lastEdited && comp.clazz == null && comp.readData(this.cacheRoot.getPath())) {
// class is not loaded, but the cached class bytes are still valid
if (!comp.checkPreprocessors(this.scriptRoot)) {
return GroovyLog.class; // failed preprocessor check
}
comp.ensureLoaded(engine.getGroovyClassLoader(), this.cacheRoot.getPath());

} else if (comp == null || comp.clazz == null || lastModified > comp.lastEdited) {
} else if (!ENABLE_CACHE || (comp == null || comp.clazz == null || lastModified > comp.lastEdited)) {
// class is not loaded and class bytes don't exist yet or script has been edited
if (comp == null) {
comp = new CompiledScript(relativeFile.toString(), 0);
Expand All @@ -292,7 +293,7 @@ protected Class<?> loadScriptClass(GroovyScriptEngine engine, File file) {
if (comp.clazz == null) {
// should not happen
GroovyLog.get().errorMC("Class for {} was loaded, but didn't receive class created callback! Index: {}", relativeFile, this.index);
comp.clazz = clazz;
if (ENABLE_CACHE) comp.clazz = clazz;
}
} else {
// class is loaded and script wasn't edited
Expand All @@ -312,7 +313,8 @@ protected void postInitBindings(Binding binding) {

@Override
protected void initEngine(GroovyScriptEngine engine, CompilerConfiguration config) {
config.addCompilationCustomizers(GroovyScriptCompiler.transformer());
config.addCompilationCustomizers(new GroovyScriptCompiler());
config.addCompilationCustomizers(new GroovyScriptEarlyCompiler());
config.addCompilationCustomizers(this.importCustomizer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@ public class GroovyScriptCompiler extends CompilationCustomizer {
private static final String SIDE_ONLY_CLASS = "net.minecraftforge.fml.relauncher.SideOnly";
private static final String SIDE_CLASS = "net.minecraftforge.fml.relauncher.Side";

public static GroovyScriptCompiler transformer() {
return new GroovyScriptCompiler(CompilePhase.CANONICALIZATION);
}

private GroovyScriptCompiler(CompilePhase phase) {
super(phase);
public GroovyScriptCompiler() {
super(CompilePhase.CANONICALIZATION);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.cleanroommc.groovyscript.sandbox.transformer;

import com.cleanroommc.groovyscript.GroovyScript;
import com.cleanroommc.groovyscript.api.GroovyLog;
import com.cleanroommc.groovyscript.compat.mods.GroovyContainer;
import com.cleanroommc.groovyscript.compat.mods.ModSupport;
import com.cleanroommc.groovyscript.core.mixin.groovy.ModuleNodeAccessor;
import com.cleanroommc.groovyscript.sandbox.FileUtil;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;

import java.io.File;

public class GroovyScriptEarlyCompiler extends CompilationCustomizer {

public GroovyScriptEarlyCompiler() {
super(CompilePhase.CONVERSION);
}

@Override
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
String root = GroovyScript.getScriptPath();
String script = source.getName();
ModuleNode module = classNode.getModule();
String rel = FileUtil.relativize(root, script);
int i = rel.lastIndexOf(File.separatorChar);
if (i >= 0) {
// inject correct package declaration into script
String packageName = rel.substring(0, i).replace(File.separatorChar, '.') + '.';
if (module.getPackage() != null && !module.getPackage().getName().equals(packageName)) {
GroovyLog.get().error("Expected package {} but got {} in script {}", packageName, module.getPackage().getName(), rel);
}
module.setPackageName(packageName);
}
BlockStatement scriptStatement = (BlockStatement) module.getClasses().get(0).getMethods("run").get(0).getCode();
// transform 'import mods.[mod].[registry]' statements into 'def [registry] = mods.[mod].[registry]' expressions
((ModuleNodeAccessor) module).getModifiableImports().removeIf(imp -> {
ClassNode type = imp.getType();
if (type.getName().startsWith("mods.")) {
String[] parts = type.getName().split("\\.");
if (!ModSupport.INSTANCE.hasCompatFor(parts[1]) || parts.length > 3) return false;
GroovyContainer<?> mpc = ModSupport.INSTANCE.getContainer(parts[1]);
if (!mpc.isLoaded()) return true; // mod not loaded -> remove import
Expression prop = new PropertyExpression(new VariableExpression("mods", ClassHelper.makeCached(ModSupport.class)), parts[1]);
if (parts.length > 2) {
prop = new PropertyExpression(prop, parts[2]);
}
Expression expr = new DeclarationExpression(new VariableExpression(imp.getAlias()),
Token.newSymbol(Types.ASSIGN, imp.getLineNumber(), 5 + parts[1].length()),
prop);
scriptStatement.getStatements().add(0, new ExpressionStatement(expr));
return true;
}
return false;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.cleanroommc.groovyscript.GroovyScript;
import com.cleanroommc.groovyscript.sandbox.LoadStage;
import com.cleanroommc.groovyscript.sandbox.transformer.GroovyScriptCompiler;
import com.cleanroommc.groovyscript.sandbox.transformer.GroovyScriptEarlyCompiler;
import groovy.lang.GroovyClassLoader;
import net.minecraft.launchwrapper.Launch;
import net.prominic.groovyls.compiler.control.GroovyLSCompilationUnit;
Expand Down Expand Up @@ -40,7 +41,8 @@ protected CompilerConfiguration getConfiguration() {

config.setSourceEncoding("UTF-8");

config.addCompilationCustomizers(GroovyScriptCompiler.transformer());
config.addCompilationCustomizers(new GroovyScriptCompiler());
config.addCompilationCustomizers(new GroovyScriptEarlyCompiler());
config.addCompilationCustomizers(languageServerContext.getSandbox().getImportCustomizer());

return config;
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/mixin.groovyscript.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"groovy.GroovyClassLoaderMixin",
"groovy.Java8Mixin",
"groovy.MetaClassImplMixin",
"groovy.ModuleNodeAccessor",
"loot.LoadTableEventMixin",
"loot.LootPoolAccessor",
"loot.LootTableAccessor",
Expand Down

0 comments on commit 27b4452

Please sign in to comment.