/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.backend.java; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.util.*; import java.util.List; import abs.backend.java.codegeneration.JavaCode; import abs.backend.java.codegeneration.JavaCodeGenerationException; import abs.backend.java.lib.runtime.ABSFut; import abs.backend.java.lib.runtime.ABSObject; import abs.backend.java.lib.types.*; import abs.common.NotImplementedYetException; import abs.frontend.ast.*; import abs.frontend.parser.Main; import abs.frontend.typechecker.*; public class JavaBackend extends Main { public final static String CHARSET = "UTF-8"; public static void main(final String... args) { try { new JavaBackend().compile(args); } 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()); if (Arrays.asList(args).contains("-debug")) { e.printStackTrace(); } System.exit(1); } } private File destDir = new File("gen/"); private boolean sourceOnly = false; private boolean untypedJavaGen = false; private boolean includeDebug = false; @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]); } } else if (arg.equals("-sourceonly")) { this.sourceOnly = true; } else if (arg.equals("-dynamic")) { this.untypedJavaGen = true; } else if (arg.equals("-no-debuginfo")) { this.includeDebug = false; } else if (arg.equals("-debuginfo")) { this.includeDebug = true; } else if (arg.equals("-debug")) { /* Print stacktrace on exception, used in main(), must be removed from remaining args. */ } else if(arg.equals("-java")) { // nothing to do } else { remainingArgs.add(arg); } } return remainingArgs; } protected void printUsage() { super.printUsage(); System.out.println("Java Backend:\n" + " -d <dir> generate files to <dir>\n" + " -debug print stacktrace on exception\n" + " -sourceonly do not generate class files\n" + " -no-debuginfo generate code without listener / debugger support (default)\n" + " -debuginfo generate code with listener / debugger support\n" + " -dynamic generate dynamically updateable code\n"); } private void compile(String[] args) throws Exception { final Model model = parse(args); if (model.hasParserErrors() || model.hasErrors() || model.hasTypeErrors()) printParserErrorAndExit(); 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); } compile(model, destDir); } private void compile(Model m, File destDir) throws IOException, JavaCodeGenerationException { JavaCode javaCode = new JavaCode(destDir); if (this.untypedJavaGen) { if (verbose) System.out.println("Generating dynamic Java code..."); m.generateJavaCodeDynamic(javaCode, this.includeDebug); } else { if (verbose) System.out.println("Generating Java code..."); m.generateJavaCode(javaCode, this.includeDebug); } if (!sourceOnly) { if (verbose) System.out.println("Compiling generated Java code..."); javaCode.compile(); } } private static final Map<String, String> dataTypeMap = initDataTypeMap(); private static Map<String, String> initDataTypeMap() { final Map<String, String> res = new HashMap<String, String>(); res.put("Int", ABSInteger.class.getName()); res.put("Rat", ABSRational.class.getName()); res.put("Bool", ABSBool.class.getName()); res.put("String", ABSString.class.getName()); res.put("Fut", ABSFut.class.getName()); res.put("Unit", ABSUnit.class.getName()); res.put("Process", ABSProcess.class.getName()); return res; } public static boolean isBuiltinDataType(Type absType) { if (absType.isDataType()) return dataTypeMap.containsKey(((DataTypeType)absType).getDecl().getName()); else return false; } public static String getJavaType(ConstructorArg u) { return getJavaType(u.getTypeUse()); } public static String getJavaType(TypeUse absType) { return getQualifiedString(absType.getType()); } public static String getQualifiedString(String s) { return s; } public static String getQualifiedString(Name name) { return getQualifiedString(name.getString()); } public static String getQualifiedString(Type absType) { String res = null; if (absType.isDataType()) { DataTypeType dt = (DataTypeType) absType; res = dataTypeMap.get(dt.getDecl().getName()); if (res != null) return res; StringBuilder sb = new StringBuilder(); if (dt.hasTypeArgs() && !containsUnboundedType(dt.getTypeArgs())) { sb.append("<"); boolean first = true; for (Type t : dt.getTypeArgs()) { if (first) first = false; else sb.append(','); sb.append(getQualifiedString(t)); } sb.append(">"); } return getQualifiedString(dt.getDecl()) + sb.toString(); /* * if (dt.hasTypeArgs() && !containsUnboundedType(dt.getTypeArgs())) * { * * sb.append("<"); boolean first = true; for (Type t : * dt.getTypeArgs()) { if (first) first = false; else * sb.append(','); sb.append(getQualifiedString(t)); } * sb.append(">"); } */ } else if (absType.isInterfaceType()) { InterfaceType it = (InterfaceType) absType; return getQualifiedString(it.getDecl()); } else if (absType.isTypeParameter()) { TypeParameter tp = (TypeParameter) absType; return tp.getDecl().getName(); } else if (absType.isBoundedType()) { BoundedType bt = (BoundedType) absType; if (bt.hasBoundType()) return getQualifiedString(bt.getBoundType()); return "?"; } else if (absType.isAnyType()) { return "java.lang.Object"; } else if (absType.isUnionType()) { return getQualifiedString(((UnionType) absType).getOriginatingClass()); } throw new RuntimeException("Type " + absType.getClass().getName() + " not yet supported by Java backend"); } private static boolean containsUnboundedType(List<Type> typeArgs) { for (Type t : typeArgs) { if (t.isBoundedType()) { BoundedType bt = (BoundedType)t; if (!bt.hasBoundType()) { return true; } } } return false; } public static String getQualifiedString(Decl decl) { return decl.getModuleDecl().getName() + "." + getJavaName(decl); } private static final String[] JAVA_RESERVED_WORDS_ARRAY = { "abstract", "do", "import", "public", "throws", "boolean", "double", "instanceof", "return", "transient", "break", "else", "int", "short", "try", "byte", "extends", "interface", "static", "void", "case", "final", "long", "strictfp", "volatile", "catch", "finally", "native", "super", "while", "char", "float", "new", "switch", "class", "for", "package", "synchronized", "continue", "if", "private", "this", "default", "implements", "protected", "throw", "const", "goto", "null", "true", "false", "abs" }; private static final Set<String> JAVA_RESERVED_WORDS = new HashSet<String>(); static { for (String s : JAVA_RESERVED_WORDS_ARRAY) { JAVA_RESERVED_WORDS.add(s); } // add methods from ABSObject to reserved words: for (Method m : ABSObject.class.getMethods()) { JAVA_RESERVED_WORDS.add(m.getName()); } // the run method is special, because it can be overridden JAVA_RESERVED_WORDS.remove("run"); } public static String getConstructorName(DataTypeDecl dataType, String name) { return truncate(dataType.getName() + "_" + name); } public static String getConstructorName(DataConstructor decl) { return getConstructorName(((DataTypeType) decl.getType()).getDecl(), decl.getName()); } public static String getInterfaceName(String name) { return truncate(name + "_i"); } public static String getClassName(String name) { return truncate(name + "_c"); } public static String getProductName(String name) { return truncate(name + "_prod"); } public static String getReconfigurationName(String from, String to) { return truncate(from + "__" + to + "_recf"); } public static String getDeltaName(String name) { return truncate(name + "_delta"); } public static String getDeltaPackageName(String name) { return truncate(JavaBackendConstants.LIB_DELTAS_PACKAGE + "." + name); } public static String getUpdateName(String name) { return truncate(name + "_upd"); } public static String getModifierPackageName(String name) { return truncate(JavaBackendConstants.LIB_DELTAS_PACKAGE + "." + name); } public static String getModifierName() { return truncate("Mod_" + getRandomName()); } public static String getFunctionName(String name) { return truncate(escapeReservedWords(name) + "_f"); } public static String getMethodName(String name) { return escapeReservedWords(name); } public static String getVariableName(String name) { return escapeReservedWords(name); } private static String escapeReservedWords(String name) { if (JAVA_RESERVED_WORDS.contains(name)) { return name + "__"; } else { return name; } } public static String getJavaName(Decl decl) { String result; if (decl instanceof FunctionDecl) { result = getFunctionName(decl.getName()); } else if (decl instanceof DataConstructor) { result = getConstructorName((DataConstructor) decl); } else if (decl instanceof ClassDecl) { result = getClassName(decl.getName()); } else if (decl instanceof InterfaceDecl) { result = getInterfaceName(decl.getName()); } else { result = truncate(decl.getName()); } return result; } public static String getJavaName(ModuleModifier mod) { String result; if (mod instanceof ClassModifier) { result = getClassName(mod.getName()); } else { result = truncate(mod.getName()); } return result; } // Shorten name to 255 chars as files with these names are created private static String truncate(String s) { int maxlength = 200; if (s.length() < maxlength) { return s; } else { String prefix = s.substring(0, maxlength); int suffix = s.hashCode(); // We do not consider collisions as highly unlikely return prefix + suffix; } } /** * get the java name main blocks */ public static String getJavaNameForMainBlock() { return "Main"; } /** * get the fully qualified java name for the main block of a given module */ public static String getFullJavaNameForMainBlock(ModuleDecl module) { return module.getName() + "." + getJavaNameForMainBlock(); } /** * Just return a randomly generated string */ public static String getRandomName() { return Integer.toHexString(UUID.randomUUID().hashCode()); } }