/** * 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.codegeneration; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.lang.reflect.Method; import abs.backend.java.JavaBackend; import abs.backend.java.JavaBackendConstants; import abs.backend.java.lib.runtime.ABSBuiltInFunctions; import abs.backend.java.lib.runtime.ABSFut; import abs.backend.java.lib.runtime.ABSRuntime; import abs.backend.java.lib.runtime.AbstractAsyncCall; import abs.backend.java.lib.runtime.AbstractAsyncCallRT; import abs.backend.java.lib.runtime.Task; import abs.backend.java.lib.types.ABSBool; import abs.backend.java.lib.types.ABSProcess; import abs.backend.java.lib.types.ABSValue; import abs.backend.java.scheduling.UserSchedulingStrategy; import abs.common.CompilerUtils; import abs.common.Constants; import abs.common.NotImplementedYetException; import abs.frontend.ast.ASTNode; import abs.frontend.ast.Annotation; import abs.frontend.ast.AsyncCall; import abs.frontend.ast.AwaitAsyncCall; import abs.frontend.ast.AwaitStmt; import abs.frontend.ast.ClaimGuard; import abs.frontend.ast.ClassDecl; import abs.frontend.ast.Decl; import abs.frontend.ast.ExpGuard; import abs.frontend.ast.FnApp; import abs.frontend.ast.FunctionDecl; import abs.frontend.ast.HasTypeParameters; import abs.frontend.ast.LetExp; import abs.frontend.ast.List; import abs.frontend.ast.MethodImpl; import abs.frontend.ast.MethodSig; import abs.frontend.ast.NewExp; import abs.frontend.ast.ParamDecl; import abs.frontend.ast.PureExp; import abs.frontend.ast.ReturnStmt; import abs.frontend.ast.ThisExp; import abs.frontend.ast.TypeParameterDecl; import abs.frontend.ast.TypedVarOrFieldDecl; import abs.frontend.ast.VarDecl; import abs.frontend.ast.VarOrFieldDecl; import abs.frontend.ast.VarUse; import abs.frontend.typechecker.Type; public class JavaGeneratorHelper { private static final String FLI_METHOD_PREFIX = "fli_"; public static void generateHelpLine(ASTNode<?> node, PrintStream stream) { stream.println("// " + node.getPositionString()); } public static void generateArgs(PrintStream stream, List<PureExp> args, java.util.List<Type> types) { generateArgs(stream, null, args, types); } public static void generateArgs(PrintStream stream, String firstArg, List<PureExp> args, java.util.List<Type> types) { stream.print("("); boolean first = true; if (firstArg != null) { stream.print(firstArg); first = false; } for (int i = 0; i < args.getNumChild(); i++) { PureExp e = args.getChild(i); if (!first) stream.print(", "); e.generateJava(stream); if (types.get(i).isIntType() && e.getType().isRatType()) stream.print(".truncate()"); first = false; } stream.print(")"); } public static void generateParamArgs(PrintStream stream, List<ParamDecl> params) { generateParamArgs(stream,null,params); } public static void generateParamArgs(PrintStream stream, String firstArg, List<ParamDecl> params) { stream.print("("); boolean first = true; if (firstArg != null) { stream.print(firstArg); first = false; } for (ParamDecl d : params) { if (!first) stream.print(", "); stream.print(JavaBackend.getVariableName(d.getName())); first = false; } stream.print(")"); } public static void generateParams(PrintStream stream, List<ParamDecl> params) { generateParams(stream, null, params); } public static void generateParams(PrintStream stream, String firstArg, List<ParamDecl> params) { stream.print("("); boolean first = true; if (firstArg != null) { stream.print(firstArg); first = false; } for (ParamDecl d : params) { if (!first) stream.print(", "); // stream.print("final "); d.generateJava(stream); first = false; } stream.print(")"); } public static void generateTypeParameters(PrintStream stream, Decl dtd, boolean plusExtends) { List<TypeParameterDecl> typeParams = null; if (dtd instanceof HasTypeParameters) { typeParams = ((HasTypeParameters)dtd).getTypeParameters(); } else return; if (typeParams.getNumChild() > 0) { stream.print("<"); boolean isFirst = true; for (TypeParameterDecl d : typeParams) { if (isFirst) isFirst = false; else stream.print(","); stream.print(d.getName()); if (plusExtends) stream.print(" extends " + ABSValue.class.getName()); } stream.print(">"); } } public static void generateBuiltInFnApp(PrintStream stream, FnApp app) { FunctionDecl d = (FunctionDecl) app.getDecl(); String name = d.getName(); if (!builtInFunctionExists(name)) { throw new NotImplementedYetException(app, "The built in function '" + name + "' is not implemented in the Java backend."); } // if builtin function returns a non-builtin type, cast the returned value to that type boolean returnsGeneratedDataType = ! JavaBackend.isBuiltinDataType(app.getType()); if (returnsGeneratedDataType) stream.print("((" + JavaBackend.getQualifiedString(app.getType()) + ")"); stream.print(ABSBuiltInFunctions.class.getName() + "." + name); String firstArgs = null; if (Constants.isFunctionalBreakPointFunctionName(d.getModuleDecl().getName() + "." + name)) firstArgs = generateFunctionalBreakPointArgs(app); generateArgs(stream, firstArgs, app.getParams(), d.getTypes()); if (returnsGeneratedDataType) stream.print(")"); } private static String generateFunctionalBreakPointArgs(FnApp app) { return new StringBuilder("\"").append(app.getCompilationUnit().getFileName().replace("\\", "\\\\")) .append("\", ").append(app.getStartLine()).toString(); } private static boolean builtInFunctionExists(String name) { for (Method m : ABSBuiltInFunctions.class.getMethods()) { if (m.getName().equals(name)) { return true; } } return false; } public static String getDebugString(ASTNode<?> node) { return getDebugString(node, node.getStartLine()); } public static String getDebugString(ASTNode<?> node, int line) { String fileName = node.getCompilationUnit().getFileName().replace("\\", "\\\\"); return "if (__ABS_getRuntime().debuggingEnabled()) __ABS_getRuntime().nextStep(\"" + fileName + "\"," + line + ");"; } public static void generateMethodSig(PrintStream stream, MethodSig sig, boolean async) { generateMethodSig(stream, sig, async, "", ""); } public static void generateMethodSig(PrintStream stream, MethodSig sig, boolean async, String modifier, String prefix) { JavaGeneratorHelper.generateHelpLine(sig,stream); stream.print("public " + modifier + " "); if (async) { prefix = "async_"; stream.print(ABSFut.class.getName() + "<"); } sig.getReturnType().generateJava(stream); if (async) stream.print(">"); stream.print(" " + prefix+JavaBackend.getMethodName(sig.getName())); JavaGeneratorHelper.generateParams(stream, sig.getParams()); } public static void generateAsyncMethod(PrintStream stream, MethodImpl method) { final MethodSig sig = method.getMethodSig(); generateMethodSig(stream, sig, true, "final", ""); stream.println(" {"); stream.print("return (" + ABSFut.class.getName() + ")"); generateAsyncCall(stream, "this", null, method.getContextDecl().getType(), null, sig.getParams(), sig, new List<Annotation>()); stream.println(";"); stream.println("}"); } public static void generateAsyncCall(PrintStream stream, AsyncCall call) { final PureExp callee = call.getCallee(); final List<PureExp> params = call.getParams(); final MethodSig sig = call.getMethodSig(); final List<Annotation> annotations = call.getAnnotations(); generateAsyncCall(stream, null, callee, callee.getType(), params, null, sig, annotations); } private static void generateAsyncCall(PrintStream stream, final String calleeString, final PureExp callee, final Type calleeType, final List<PureExp> args, final List<ParamDecl> params, final MethodSig sig, final List<Annotation> annotations) { final java.util.List<Type> paramTypes = sig.getTypes(); stream.print(ABSRuntime.class.getName() + ".getCurrentRuntime().asyncCall("); String targetType = JavaBackend.getQualifiedString(calleeType); stream.println("new " + AbstractAsyncCallRT.class.getName() + "<" + targetType + ">("); stream.println("this,"); if (callee instanceof ThisExp) { if (calleeString != null) stream.print(calleeString); else callee.generateJava(stream); } else { stream.print(ABSRuntime.class.getName() + ".checkForNull("); if (calleeString != null) stream.print(calleeString); else callee.generateJava(stream); stream.print(")"); } stream.println(","); PureExp rtAttr; rtAttr = CompilerUtils.getAnnotationValueFromSimpleName(annotations, "Deadline"); if (rtAttr == null) stream.print("new ABS.StdLib.Duration_InfDuration()"); else rtAttr.generateJava(stream); stream.println(","); rtAttr = CompilerUtils.getAnnotationValueFromSimpleName(annotations, "Cost"); if (rtAttr == null) stream.print("new ABS.StdLib.Duration_InfDuration()"); else rtAttr.generateJava(stream); stream.println(","); rtAttr = CompilerUtils.getAnnotationValueFromSimpleName(annotations, "Critical"); if (rtAttr == null) stream.print(ABSBool.class.getName() + ".FALSE"); else rtAttr.generateJava(stream); stream.println(") {"); int i = 0; for (Type t : paramTypes) { stream.println(JavaBackend.getQualifiedString(t) + " arg" + i + ";"); i++; } generateTaskGetArgsMethod(stream, paramTypes.size()); generateTaskInitMethod(stream, paramTypes); stream.println("public java.lang.String methodName() {"); stream.println("return \"" + sig.getName() + "\";"); stream.println("}"); stream.println("public Object execute() {"); stream.print("return target." + JavaBackend.getMethodName(sig.getName()) + "("); for (i = 0; i < paramTypes.size(); i++) { if (i > 0) stream.print(","); stream.println("arg" + i); if (paramTypes.get(i).isIntType()) stream.print(".truncate()"); } stream.println(");"); stream.println("}"); stream.print("}.init"); if (args != null) JavaGeneratorHelper.generateArgs(stream,args, paramTypes); else JavaGeneratorHelper.generateParamArgs(stream, params); stream.println(")"); } public static void generateAwaitAsyncCall(PrintStream stream, AwaitAsyncCall call) { final PureExp callee = call.getCallee(); final List<PureExp> params = call.getParams(); final MethodSig sig = call.getMethodSig(); final List<Annotation> annotations = call.getAnnotations(); // FIXME: implement await, assignment afterwards // OutputStream exprOStream = new ByteArrayOutputStream(); // PrintStream exprStream = new PrintStream(exprOStream); // ClaimGuard guard = new ClaimGuard(); // // Necessary temporary variables are written to "stream" and the // // await-expression is written to exprStream // // // FIXME: implement await, assignment afterwards // guard.generateJavaGuard(stream, exprStream); // stream.print(JavaBackendConstants.ABSRUNTIME + ".await("); // stream.print(exprOStream.toString()); // stream.println(");"); generateAsyncCall(stream, null, callee, callee.getType(), params, null, sig, annotations); } private static void generateTaskInitMethod(PrintStream stream, final java.util.List<Type> paramTypes) { int i; stream.print("public " + abs.backend.java.lib.runtime.AsyncCall.class.getName() + "<?> init("); i = 0; for (Type t : paramTypes) { if (i > 0) stream.print(","); stream.print(JavaBackend.getQualifiedString(t) + " _arg" + i); i++; } stream.println(") {"); for (i = 0; i < paramTypes.size(); i++) { stream.println("arg" + i + " = _arg" + i + ";"); } stream.println("return this;"); stream.println("}"); } private static void generateTaskGetArgsMethod(PrintStream stream, final int n) { stream.println("public java.util.List<" + ABSValue.class.getName() + "> getArgs() {"); stream.println("return java.util.Arrays.asList(new " + ABSValue.class.getName() + "[] {"); generateArgStringList(stream, n); stream.println("});"); stream.println("}"); } private static void generateArgStringList(PrintStream stream, final int n) { for (int i = 0; i < n; i++) { if (i > 0) stream.print(","); stream.print("arg" + i); } } public static void generateClassDecl(PrintStream stream, final ClassDecl decl) { new ClassDeclGenerator(stream, decl).generate(); } public static void generateMethodImpl(PrintStream stream, final MethodImpl m) { // Async variant JavaGeneratorHelper.generateAsyncMethod(stream, m); // Sync variant generateMethodSig(stream, m.getMethodSig(), false, "final", ""); generateMethodBody(stream, m, false); if (m.isForeign()) { generateFLIMethod(stream,m); } } public static void generateMethodBody(PrintStream stream, final MethodImpl m, boolean isFliMethod) { boolean addReturn = false; if (m.getMethodSig().getReturnType().getType().isUnitType()) { if (m.getBlock().getNumStmt() == 0 || (! (m.getBlock().getStmt(m.getBlock().getNumStmt()-1) instanceof ReturnStmt))) { addReturn = true; } } stream.println(" {"); stream.println("__ABS_checkSameCOG(); "); if (!isFliMethod && m.isForeign()) { stream.print("return this."); stream.print(FLI_METHOD_PREFIX+JavaBackend.getMethodName(m.getMethodSig().getName())); JavaGeneratorHelper.generateParamArgs(stream, m.getMethodSig().getParams()); stream.println(";"); } else { stream.println("if (__ABS_getRuntime().debuggingEnabled()) {"); stream.println(Task.class.getName() + "<?> __ABS_currentTask = __ABS_getRuntime().getCurrentTask();"); stream.println("__ABS_currentTask.newStackFrame(this, \"" + m.getMethodSig().getName() + "\");"); for (ParamDecl d : m.getMethodSig().getParams()) { stream.print("__ABS_currentTask.setLocalVariable("); stream.println("\"" + d.getName() + "\"," + d.getName() + ");"); } stream.println("}"); m.getBlock().generateJava(stream, addReturn); } stream.println("}"); } private static void generateFLIMethod(PrintStream stream, MethodImpl m) { generateMethodSig(stream, m.getMethodSig(), false, "", FLI_METHOD_PREFIX); generateMethodBody(stream,m,true); } /** * removes the gen folder and all its contents * @param code */ public static void cleanGenFolder(JavaCode code) { File genDir = code.getSrcDir(); cleanGenFolderRecursively(genDir); } private static void cleanGenFolderRecursively(File dir) { if (dir == null) throw new IllegalArgumentException(); if (!dir.exists()) return; for (File f : dir.listFiles()) { if (f.isDirectory()) { cleanGenFolderRecursively(f); } else { if (f.getAbsolutePath().endsWith(".java") || f.getAbsolutePath().endsWith(".class")) { f.delete(); } } } dir.delete(); } public static void printEscapedString(PrintStream stream, String content) { for (int i=0; i<content.length(); i++) { char c = content.charAt(i); switch (c) { case '\t': stream.append('\\').append('t'); break; case '\b': stream.append('\\').append('b'); break; case '\n': stream.append('\\').append('n'); break; case '\r': stream.append('\\').append('r'); break; case '\f': stream.append('\\').append('f'); break; case '\'': stream.append('\\').append('\''); break; case '\"': stream.append('\\').append('\"'); break; case '\\': stream.append('\\').append('\\'); break; default: stream.append(c); } } } public static void generateExprGuard(ExpGuard expGuard, PrintStream beforeAwaitStream, PrintStream stream) { PureExp expr = expGuard.getPureExp(); replaceLocalVariables((PureExp)expr.copy(), beforeAwaitStream); stream.print("new " + JavaBackendConstants.EXPGUARD + "() { public " + ABSBool.class.getName() + " evaluateExp() { return "); expGuard.getPureExp().generateJava(stream); stream.print("; }}"); } /** * replace all uses of local variables and parameters by a use of a newly introduced * temporary final local variable */ private static void replaceLocalVariables(ASTNode<?> astNode, PrintStream beforeAwaitStream) { if (isLocalVarUse(astNode)) { VarUse v = (VarUse) astNode; replaceVarUse(beforeAwaitStream, v, (TypedVarOrFieldDecl) v.getDecl()); } else { // process children: for (int i=0; i < astNode.getNumChild(); i++) { ASTNode<?> child = astNode.getChild(i); replaceLocalVariables(child, beforeAwaitStream); } } } /*** * checks if astNode is a use of a local variable or parameter */ private static boolean isLocalVarUse(ASTNode<?> astNode) { if (astNode instanceof VarUse) { VarUse v = (VarUse) astNode; VarOrFieldDecl decl = v.getDecl(); if (decl instanceof VarDecl || decl instanceof ParamDecl) { return !(decl.getParent() instanceof LetExp); } } return false; } private static long tempCounter = 0; /** * replaces a varUse v of the local variable vDecl by a new temporary variable, which will be * written to beforeAwaitStream */ private static void replaceVarUse(PrintStream beforeAwaitStream, VarUse v, TypedVarOrFieldDecl vDecl) { String name = JavaBackend.getVariableName(vDecl.getName()); String tempName = "temp$" + tempCounter + "$" + name; tempCounter = Math.max(tempCounter+1, 0); // copy value of variable to temporary, final variable beforeAwaitStream.print("final "); vDecl.getAccess().generateJava(beforeAwaitStream); beforeAwaitStream.print(" " + tempName + " = " + name + ";"); // replace variable name with name of temporary variable v.setName(tempName); } public static void generateAwaitStmt(AwaitStmt awaitStmt, PrintStream stream) { OutputStream exprOStream = new ByteArrayOutputStream(); PrintStream exprStream = new PrintStream(exprOStream); // Necessary temporary variables are written to "stream" and the // await-expression is written to exprStream awaitStmt.getGuard().generateJavaGuard(stream, exprStream); stream.print(JavaBackendConstants.ABSRUNTIME + ".await("); stream.print(exprOStream.toString()); stream.println(");"); } public static String generateUserSchedulingStrategy(NewExp exp, PureExp scheduler) { String className = "UserSchedulingStrategy_" + JavaBackend.getRandomName(); PrintStream stream = null; try { JavaCode.Package pkg = exp.getModuleDecl().getJavaPackage(); File file = pkg.createJavaFile(className); stream = new JavaCodeStream(file); stream.println("package " + pkg.packageName + ";"); stream.print("public final class " + className); stream.println(" extends " + UserSchedulingStrategy.class.getName() + " {"); stream.println("public synchronized " + ABSProcess.class.getName() + " userschedule(Object q) {"); stream.println("ABS.StdLib.List<" + ABSProcess.class.getName() + "> queue = (ABS.StdLib.List<" + ABSProcess.class.getName() + ">) q;"); // call the given scheduling function // here goes whatever is specified in the Scheduler annotation // i.e. a function call or some other code that returns a Process stream.println("// user-specified scheduler expression"); stream.print("return "); scheduler.generateJava(stream); stream.println(";"); stream.println("}"); stream.println("}"); // connect generated TaskSchedulingStrategy to the cog's TaskScheduler return pkg.packageName + "." + className; } catch (JavaCodeGenerationException e) { // TODO properly handle exception e.printStackTrace(); } catch (IOException e) { // TODO properly handle exception e.printStackTrace(); } finally { if (stream != null) stream.close(); } return null; } }