/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.frontend.typechecker.locationtypes.infer; import java.io.File; import java.io.IOException; import java.util.*; import java.util.Map.Entry; import abs.common.FileUtils; import abs.common.NotImplementedYetException; import abs.common.WrongProgramArgumentException; import abs.frontend.ast.ASTNode; import abs.frontend.ast.ClassDecl; import abs.frontend.ast.CompilationUnit; import abs.frontend.ast.Decl; import abs.frontend.ast.FieldDecl; import abs.frontend.ast.InterfaceDecl; import abs.frontend.ast.InterfaceTypeUse; import abs.frontend.ast.Model; import abs.frontend.ast.VarDecl; import abs.frontend.parser.Main; import abs.frontend.typechecker.locationtypes.LocationType; public class InferMain extends Main { public enum Config { INTERFACES, CLASSES, LOCAL_VAR_DECLS, FIELDS, FUNCTIONS } EnumSet<Config> config = EnumSet.of(Config.INTERFACES, Config.CLASSES); public static void main(final String... args) { InferMain m = new InferMain(); try { m.compile(args); } catch (WrongProgramArgumentException pe) { System.err.println(pe.getMessage()); m.printUsageAndExit(); } catch (NotImplementedYetException e) { System.err.println(e.getMessage()); System.exit(0); } catch (Exception e) { System.err.println("An error occurred during compilation:\n" + e.getMessage()); e.printStackTrace(); System.exit(1); } } private File destDir = new File("."); @Override public List<String> parseArgs(String[] args) { List<String> restArgs = super.parseArgs(args); List<String> remainingArgs = new ArrayList<String>(); for (int i = 0; i < restArgs.size(); i++) { String arg = restArgs.get(i); if (arg.equals("-d")) { i++; if (i == restArgs.size()) { System.err.println("Please provide a destination directory"); System.exit(1); } else { destDir = new File(args[i]); } } if (arg.startsWith("-locinferwritebackscope=")) { String[] s = arg.split("="); if (s.length < 2) { System.err.println("Please provide a scope"); System.exit(1); } else { try { readScopeArg(s[1]); } catch (WrongProgramArgumentException e) { System.err.println(e.getMessage()); System.exit(1); } } } else { remainingArgs.add(arg); } } return remainingArgs; } private void readScopeArg(String scope) throws WrongProgramArgumentException { String[] scopes = scope.split(","); config = EnumSet.noneOf(Config.class); for (String s : scopes) { if (s.equals("all")) { config = EnumSet.allOf(Config.class); } else { try { Config c = Config.valueOf(s.toUpperCase()); config.add(c); } catch (IllegalArgumentException e) { throw new WrongProgramArgumentException("Unkown scope "+scope); } } } } protected void printUsage() { super.printUsage(); System.out.println("Location Type Inferrer:"); System.out.println(" -d <dir> generate files to <dir>"); System.out.println(" -locinferwritebackscope=<scope>,<scope>,... "); System.out.println(" only write back location type inference results"); System.out.println(" to given scopes. Where <scope> can be one of "); System.out.println(" interfaces, classes, fields, functions, and all \n"); } private void compile(String[] args) throws Exception { locationTypeInferenceEnabled = true; final Model model = parse(args); if (model.hasParserErrors() || model.hasErrors() || model.hasTypeErrors()) return; destDir.mkdirs(); if (!destDir.exists()) { System.err.println("Destination directory " + destDir.getAbsolutePath() + " does not exist!"); System.exit(1); } if (!destDir.canWrite()) { System.err.println("Destination directory " + destDir.getAbsolutePath() + " cannot be written to!"); System.exit(1); } LocationTypeInferrerExtension ltie = (LocationTypeInferrerExtension)model.getTypeExt().getFirstRegisteredTypeExtension(LocationTypeInferrerExtension.class); writeInferenceResultsBack(ltie.getResults()); } public void setConfig(Config... configs) { this.config = EnumSet.copyOf(Arrays.asList(configs)); } public void writeInferenceResultsBack(Map<LocationTypeVariable, LocationType> results) throws IOException { Map<CompilationUnit, List<LocationTypeVariable>> m = clusterByCompilationUnit(results); for (Entry<CompilationUnit, List<LocationTypeVariable>> e : m.entrySet()) { CompilationUnit cu = e.getKey(); List<LocationTypeVariable> l = getSortedList(e); File file = new File(cu.getFileName()); StringBuilder sb = FileUtils.fileToStringBuilder(file); int offset = 0; for (LocationTypeVariable ltv : l) { if (shouldBeConsidered(ltv)) { int diff = ltv.getTypeNode().getAbsolutePosition(); int pos = offset + diff; String s = results.get(ltv).toAnnoString(); sb.insert(pos, s); offset += s.length(); } } FileUtils.writeStringBuilderToFile(sb, file); } } private List<LocationTypeVariable> getSortedList(Entry<CompilationUnit, List<LocationTypeVariable>> e) { List<LocationTypeVariable> l = e.getValue(); Collections.sort(l, new Comparator<LocationTypeVariable>() { @Override public int compare(LocationTypeVariable o1, LocationTypeVariable o2) { int pos1 = o1.getTypeNode().getAbsolutePosition(); int pos2 = o2.getTypeNode().getAbsolutePosition(); if (pos1 == -1 || pos2 == -1) { throw new RuntimeException("Absolute position not defined"); } if (pos1 == pos2) { throw new RuntimeException("Two elements can not have the same position"); } return Integer.valueOf(pos1).compareTo(pos2); } }); return l; } private Map<CompilationUnit, List<LocationTypeVariable>> clusterByCompilationUnit( Map<LocationTypeVariable, LocationType> results) { Map<CompilationUnit, List<LocationTypeVariable>> m = new HashMap<CompilationUnit, List<LocationTypeVariable>>(); for (LocationTypeVariable ltv : results.keySet()) { ASTNode<?> node = ltv.getNode(); if (node == null) continue; CompilationUnit cu = node.getCompilationUnit(); if (node.getModuleDecl().getName().startsWith("ABS.")) continue; List<LocationTypeVariable> list = m.get(cu); if (list == null) { list = new ArrayList<LocationTypeVariable>(); m.put(cu, list); } list.add(ltv); } return m; } private boolean shouldBeConsidered(LocationTypeVariable ltv) { ASTNode<?> node = ltv.getNode(); Decl contextDecl = node.getContextDecl(); if (contextDecl != null) { // Don't print interface annotations in "implements/extends" clauses: if (node instanceof InterfaceTypeUse && (contextDecl instanceof ClassDecl || contextDecl instanceof InterfaceDecl)) return false; if (contextDecl.isClass() && !config.contains(Config.CLASSES)) return false; if (contextDecl.isInterface() && !config.contains(Config.INTERFACES)) return false; if (contextDecl.isFunction() && !config.contains(Config.FUNCTIONS)) return false; } if (node instanceof VarDecl && !config.contains(Config.LOCAL_VAR_DECLS)) return false; if (node instanceof FieldDecl && !config.contains(Config.FIELDS)) return false; if (ltv.getAnnotatedType() != null) { return false; } return true; } }