package me.tomassetti.turin.compiler; import com.google.common.collect.ImmutableList; import me.tomassetti.turin.classloading.TurinClassLoader; import me.tomassetti.turin.classloading.ClassFileDefinition; import me.tomassetti.turin.compiler.errorhandling.ErrorCollector; import me.tomassetti.turin.parser.Parser; import me.tomassetti.turin.resolvers.*; import me.tomassetti.turin.resolvers.compiled.DirClassesTypeResolver; import me.tomassetti.turin.resolvers.compiled.JarTypeResolver; import me.tomassetti.turin.resolvers.jdk.JdkTypeResolver; import me.tomassetti.turin.parser.ast.Position; import me.tomassetti.turin.parser.ast.TurinFile; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; public abstract class AbstractCompilerTest { public static class MyErrorCollector implements ErrorCollector { @Override public void recordSemanticError(Position position, String description) { if (position == null) { throw new IllegalArgumentException("null position"); } throw new RuntimeException(position.toString() + " : " + description); } } // Used for debugging protected static void saveClassFile(ClassFileDefinition classFileDefinition, String dir) { File output = null; try { output = new File(dir + "/" + classFileDefinition.getName().replaceAll("\\.", "/") + ".class"); output.getParentFile().mkdirs(); FileOutputStream fos = new FileOutputStream(output); fos.write(classFileDefinition.getBytecode()); } catch (IOException e) { System.err.println("Problem writing file "+output+": "+ e.getMessage()); System.exit(3); } } protected SymbolResolver getResolverFor(TurinFile turinFile) { try { TypeResolver turinStdLib = new JarTypeResolver(new File("../turin-standard-library/target/turin-standard-library-0.0.3-SNAPSHOT.jar")); return new ComposedSymbolResolver(ImmutableList.of( new InFileSymbolResolver(new ComposedTypeResolver(ImmutableList.of( JdkTypeResolver.getInstance(), turinStdLib))), new SrcSymbolResolver(ImmutableList.of(turinFile)))); } catch (IOException e) { throw new RuntimeException(e); } } protected SymbolResolver getResolverFor(TurinFile turinFile, List<String> jarFiles) { return getResolverFor(turinFile, jarFiles, Collections.emptyList()); } protected SymbolResolver getResolverFor(TurinFile turinFile, List<String> jarFiles, List<String> classesDirs) { return getResolverFor(ImmutableList.of(turinFile), jarFiles, classesDirs); } protected SymbolResolver getResolverFor(List<TurinFile> turinFiles, List<String> jarFiles, List<String> classesDirs) { jarFiles = new ArrayList<>(jarFiles); jarFiles.add("../turin-standard-library/target/turin-standard-library-0.0.3-SNAPSHOT.jar"); TypeResolver typeResolver = new ComposedTypeResolver(ImmutableList.<TypeResolver>builder() .add(JdkTypeResolver.getInstance()) .addAll(jarFiles.stream().map((jf) -> { try { return new JarTypeResolver(new File(jf)); } catch (IOException e) { throw new RuntimeException(e); } }).collect(Collectors.toList())) .addAll(classesDirs.stream().map((d) -> { try { return new DirClassesTypeResolver(new File(d)); } catch (IOException e) { throw new RuntimeException(e); } }).collect(Collectors.toList())) .build()); return new ComposedSymbolResolver(ImmutableList.of(new InFileSymbolResolver(typeResolver), new SrcSymbolResolver(turinFiles))); } public Method compileFunction(String exampleName, Class[] paramTypes) throws NoSuchMethodException, IOException { return compileFunction(exampleName, paramTypes, Collections.emptyList()); } protected ErrorCollector getErrorCollector() { return new MyErrorCollector(); } public Class compileType(String exampleName, List<String> classPathElements) throws NoSuchMethodException, IOException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/" + exampleName + ".to")); // generate bytecode Compiler.Options options = new Compiler.Options(); options.setClassPathElements(classPathElements); Compiler instance = new Compiler(getResolverFor(turinFile, classPathElements), options); List<ClassFileDefinition> classFileDefinitions = instance.compile(turinFile, getErrorCollector()); saveClassFile(classFileDefinitions.get(0), "tmp"); assertEquals(1, classFileDefinitions.size()); TurinClassLoader turinClassLoader = new TurinClassLoader(); Class clazz = turinClassLoader.addClass(classFileDefinitions.get(0).getName(), classFileDefinitions.get(0).getBytecode()); return clazz; } public Method compileFunction(String exampleName, Class[] paramTypes, List<String> classPathElements) throws NoSuchMethodException, IOException { Class functionClass = compileType(exampleName, classPathElements); assertEquals(0, functionClass.getConstructors().length); Method invoke = functionClass.getMethod("invoke", paramTypes); return invoke; } public void attemptToCompile(String exampleName, List<String> classPathElements) throws NoSuchMethodException, IOException { InputStream is = this.getClass().getResourceAsStream("/" + exampleName + ".to"); if (is == null) { throw new RuntimeException(); } TurinFile turinFile = new Parser().parse(is); // generate bytecode Compiler.Options options = new Compiler.Options(); options.setClassPathElements(classPathElements); Compiler instance = new Compiler(getResolverFor(turinFile, classPathElements), options); List<ClassFileDefinition> classFileDefinitions = instance.compile(turinFile, getErrorCollector()); } protected Optional<Throwable> getException(Method functionMethod) throws IllegalAccessException { try { functionMethod.invoke(null); return Optional.empty(); } catch (InvocationTargetException e) { return Optional.of(e.getTargetException()); } } }