/* * Copyright 2010 Pablo Arrighi, Alex Concha, Miguel Lezama for version 1. * Copyright 2013 Pablo Arrighi, Miguel Lezama, Kevin Mazet for version 2. * * This file is part of GOOL. * * GOOL is free software: you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, version 3. * * GOOL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License version 3 for more details. * * You should have received a copy of the GNU General Public License along with GOOL, * in the file COPYING.txt. If not, see <http://www.gnu.org/licenses/>. */ /** * This is a Visitor. It visits the Java abstract syntax tree generated by Sun's java parser, to produce a GOOL abstract syntax tree. * */ package gool.recognizer.java; import gool.ast.core.ArrayAccess; import gool.ast.core.ArrayNew; import gool.ast.core.Assign; import gool.ast.core.BinaryOperation; import gool.ast.core.Block; import gool.ast.core.CastExpression; import gool.ast.core.Catch; import gool.ast.core.ClassDef; import gool.ast.core.ClassNew; import gool.ast.core.Comment; import gool.ast.core.CompoundAssign; import gool.ast.core.Constant; import gool.ast.core.Constructor; import gool.ast.core.CustomDependency; import gool.ast.core.Dec; import gool.ast.core.Dependency; import gool.ast.core.EnhancedForLoop; import gool.ast.core.EqualsCall; import gool.ast.core.Expression; import gool.ast.core.ExpressionUnknown; import gool.ast.core.Field; import gool.ast.core.For; import gool.ast.core.If; import gool.ast.core.InitCall; import gool.ast.core.Language; import gool.ast.core.MainMeth; import gool.ast.core.MemberSelect; import gool.ast.core.Meth; import gool.ast.core.MethCall; import gool.ast.core.Modifier; import gool.ast.core.Node; import gool.ast.core.Operator; import gool.ast.core.Package; import gool.ast.core.Parameterizable; import gool.ast.core.ParentCall; import gool.ast.core.RecognizedDependency; import gool.ast.core.Return; import gool.ast.core.Statement; import gool.ast.core.ThisCall; import gool.ast.core.Throw; import gool.ast.core.ToStringCall; import gool.ast.core.Try; import gool.ast.core.TypeDependency; import gool.ast.core.UnaryOperation; import gool.ast.core.UnrecognizedDependency; import gool.ast.core.VarAccess; import gool.ast.core.VarDeclaration; import gool.ast.core.While; import gool.ast.list.ListAddCall; import gool.ast.list.ListContainsCall; import gool.ast.list.ListGetCall; import gool.ast.list.ListGetIteratorCall; import gool.ast.list.ListIsEmptyCall; import gool.ast.list.ListRemoveAtCall; import gool.ast.list.ListRemoveCall; import gool.ast.list.ListSizeCall; import gool.ast.map.MapContainsKeyCall; import gool.ast.map.MapEntryGetKeyCall; import gool.ast.map.MapEntryGetValueCall; import gool.ast.map.MapGetCall; import gool.ast.map.MapGetIteratorCall; import gool.ast.map.MapIsEmptyCall; import gool.ast.map.MapPutCall; import gool.ast.map.MapRemoveCall; import gool.ast.map.MapSizeCall; import gool.ast.system.SystemOutDependency; import gool.ast.system.SystemOutPrintCall; import gool.ast.type.IType; import gool.ast.type.TypeArray; import gool.ast.type.TypeBool; import gool.ast.type.TypeByte; import gool.ast.type.TypeChar; import gool.ast.type.TypeClass; import gool.ast.type.TypeDecimal; import gool.ast.type.TypeEntry; import gool.ast.type.TypeException; import gool.ast.type.TypeInt; import gool.ast.type.TypeList; import gool.ast.type.TypeMap; import gool.ast.type.TypeGoolLibraryClass; import gool.ast.type.TypeMethod; import gool.ast.type.TypeNone; import gool.ast.type.TypeNull; import gool.ast.type.TypeObject; import gool.ast.type.TypePackage; import gool.ast.type.TypeString; import gool.ast.type.TypeUnknown; import gool.ast.type.TypeVar; import gool.ast.type.TypeVoid; import gool.generator.common.Platform; import gool.generator.java.JavaPlatform; import gool.generator.objc.ObjcPlatform; import gool.recognizer.common.GoolLibraryClassAstBuilder; import gool.recognizer.common.RecognizerMatcher; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.lang.model.type.ArrayType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import logger.Log; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.ArrayTypeTree; import com.sun.source.tree.AssertTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.BreakTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.CatchTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.CompoundAssignmentTree; import com.sun.source.tree.ConditionalExpressionTree; import com.sun.source.tree.ContinueTree; import com.sun.source.tree.DoWhileLoopTree; import com.sun.source.tree.EmptyStatementTree; import com.sun.source.tree.EnhancedForLoopTree; import com.sun.source.tree.ErroneousTree; import com.sun.source.tree.ExpressionStatementTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.ForLoopTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.IfTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.InstanceOfTree; import com.sun.source.tree.LabeledStatementTree; import com.sun.source.tree.LiteralTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.PrimitiveTypeTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.SwitchTree; import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.TypeParameterTree; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.tree.WildcardTree; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.TreeInfo; /** * The JavaRecognizer does the work of converting Sun's abstract Java to * abstract GOOL. The documentation of abstract Java is at * http://docs.oracle.com * /javase/7/docs/api/javax/lang/model/package-summary.html The class Context is * necessary for that and is declared at the bottom. */ public class JavaRecognizer extends TreePathScanner<Object, Context> { /** * The Sun's abstract Java AST that we will now convert to abstract GOOL. */ private CompilationUnitTree ast; /** * The type information that was obtained from Sun's java parser analysis of * the AST. */ private Trees typetrees; /** * The default platform used to specify the Target Language, which will be * annotated in the newly created classes. */ private Platform defaultPlatform; /** * The list of abstract GOOL classes and packages that will be generated. */ private Map<IType, ClassDef> goolClasses = new HashMap<IType, ClassDef>(); private Map<String, Package> packagesCache = new HashMap<String, Package>(); /** * The list of keywords that may cause problems when generating target code * from concrete or abstract GOOL. */ private static final Set<String> FORBIDDEN_KEYWORDS = new HashSet<String>(); static { // C# FORBIDDEN_KEYWORDS.add("out"); FORBIDDEN_KEYWORDS.add("params"); FORBIDDEN_KEYWORDS.add("value"); FORBIDDEN_KEYWORDS.add("out"); FORBIDDEN_KEYWORDS.add("ref"); FORBIDDEN_KEYWORDS.add("object"); FORBIDDEN_KEYWORDS.add("string"); // PYTHON FORBIDDEN_KEYWORDS.add("print"); } /** * The map between Java operators and GOOL operators. Left are the Java * abstract operators. Right are the GOOL abstract operators. */ static final private Map<Kind, Operator> operatorMap = new HashMap<Kind, Operator>(); static { operatorMap.put(Kind.PLUS, Operator.PLUS); operatorMap.put(Kind.MINUS, Operator.MINUS); operatorMap.put(Kind.UNARY_MINUS, Operator.MINUS); operatorMap.put(Kind.MULTIPLY, Operator.MULT); operatorMap.put(Kind.DIVIDE, Operator.DIV); operatorMap.put(Kind.CONDITIONAL_AND, Operator.AND); operatorMap.put(Kind.CONDITIONAL_OR, Operator.OR); operatorMap.put(Kind.EQUAL_TO, Operator.EQUAL); operatorMap.put(Kind.NOT_EQUAL_TO, Operator.NOT_EQUAL); operatorMap.put(Kind.LESS_THAN, Operator.LT); operatorMap.put(Kind.LESS_THAN_EQUAL, Operator.LEQ); operatorMap.put(Kind.GREATER_THAN, Operator.GT); operatorMap.put(Kind.GREATER_THAN_EQUAL, Operator.GEQ); operatorMap.put(Kind.LOGICAL_COMPLEMENT, Operator.NOT); operatorMap.put(Kind.PREFIX_DECREMENT, Operator.PREFIX_DECREMENT); operatorMap.put(Kind.POSTFIX_DECREMENT, Operator.POSTFIX_DECREMENT); operatorMap.put(Kind.PREFIX_INCREMENT, Operator.PREFIX_INCREMENT); operatorMap.put(Kind.POSTFIX_INCREMENT, Operator.POSTFIX_INCREMENT); operatorMap.put(Kind.AND_ASSIGNMENT, Operator.AND); operatorMap.put(Kind.DIVIDE_ASSIGNMENT, Operator.DIV); operatorMap.put(Kind.MINUS_ASSIGNMENT, Operator.MINUS); operatorMap.put(Kind.MULTIPLY_ASSIGNMENT, Operator.MULT); operatorMap.put(Kind.OR_ASSIGNMENT, Operator.OR); operatorMap.put(Kind.PLUS_ASSIGNMENT, Operator.PLUS); } static { // register standard exceptions // TODO: only exception from java.lang are registered for now TypeException.add(new TypeException("Exception", "java.lang", TypeException.Kind.GLOBAL), new TypeException( "RuntimeException", "java.lang", TypeException.Kind.GLOBAL), new TypeException("ArithmeticException", "java.lang", TypeException.Kind.ARITHMETIC), new TypeException( "ArrayStoreException", "java.lang", TypeException.Kind.COLLECTION), new TypeException( "ClassCastException", "java.lang", TypeException.Kind.CAST), new TypeException( "EnumConstantNotPresentException", "java.lang", TypeException.Kind.ENUM), new TypeException( "IllegalArgumentException", "java.lang", TypeException.Kind.ARGUMENT), new TypeException( "IllegalThreadStateException", "java.lang", TypeException.Kind.ARGUMENT), new TypeException( "NumberFormatException", "java.lang", TypeException.Kind.ARGUMENT), new TypeException( "IllegalMonitorStateException", "java.lang", TypeException.Kind.THREAD), new TypeException( "IllegalStateException", "java.lang", TypeException.Kind.STATE), new TypeException( "IndexOutOfBoundsException", "java.lang", TypeException.Kind.ARRAY), new TypeException( "ArrayIndexOutOfBoundsException", "java.lang", TypeException.Kind.ARRAY), new TypeException( "StringIndexOutOfBoundsException", "java.lang", TypeException.Kind.ARRAY), new TypeException( "NegativeArraySizeException", "java.lang", TypeException.Kind.ARRAYSIZE), new TypeException( "NullPointerException", "java.lang", TypeException.Kind.NULLREFERENCE), new TypeException( "SecurityException", "java.lang", TypeException.Kind.SECURITY), new TypeException( "TypeNotPresentException", "java.lang", TypeException.Kind.TYPE), new TypeException( "UnsupportedOperationException", "java.lang", TypeException.Kind.UNSUPORTED), new TypeException( "ClassNotFoundException", "java.lang", TypeException.Kind.CLASSNOTFOUND), new TypeException( "CloneNotSupportedException", "java.lang", TypeException.Kind.DEFAULT), new TypeException( "IllegalAccessException", "java.lang", TypeException.Kind.ACCESS), new TypeException( "InstantiationException", "java.lang", TypeException.Kind.NEWINSTANCE), new TypeException( "InterruptedException", "java.lang", TypeException.Kind.INTERUPT), new TypeException( "NoSuchFieldException", "java.lang", TypeException.Kind.NOSUCHFIELD), new TypeException( "NoSuchMethodException", "java.lang", TypeException.Kind.NOSUCHMETH)); } /** * Even though this is now our turn to take Sun's abstract Java and make it * into abstract GOOL, It turns out that Sun's Java parser has already got a * notion of TreePathScanner for traveling through its ASTs, which we here * extend. Scan() is what launches the whole process. */ public void scan() { super.scan(ast, null); } /** * Setters and getters */ public static void addForbiddenKeyword(String keyword) { FORBIDDEN_KEYWORDS.add(keyword); } public static void addForbiddenKeyword(File keywordsFile) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(keywordsFile)); String keyword; while ((keyword = reader.readLine()) != null) { addForbiddenKeyword(keyword); } reader.close(); } public final void setCurrentCompilationUnit( CompilationUnitTree currentCompilationUnit) { this.ast = currentCompilationUnit; } public final void setDefaultPlatform(Platform defaultPlatform) { this.defaultPlatform = defaultPlatform; } public final void setTrees(Trees typetrees) { this.typetrees = typetrees; } public final Collection<ClassDef> getGoolClasses() { return goolClasses.values(); } private void addDependencyToContext(Context context, Dependency newDep) { if (newDep != null && context != null && context.getClassDef() != null) { // if the new dependency has already been added to the context, // there is nothing to do for (Dependency d : context.getClassDef().getDependencies()) { String dString, newDepString; if (d instanceof RecognizedDependency) dString = ((RecognizedDependency) d).getName(); else dString = d.toString(); if (newDep instanceof RecognizedDependency) newDepString = ((RecognizedDependency) newDep).getName(); else newDepString = newDep.toString(); if (dString.equals(newDepString)) return; } // else, if it's indeed a new dependency, we add it to the context context.getClassDef().addDependency(newDep); } } /** * THIS PART IS ABOUT TYPE CONVERSION */ /** * Converts Java abstract operator kinds to GOOL abstract operators. */ private Operator getOperator(Kind kind) { Operator result = operatorMap.get(kind); if (result == null) { result = Operator.UNKNOWN; } return result; } /** * Get a Java type from a sub-branch of Java type tree. */ private TypeMirror getTypeMirror(Tree n) { TreePath path = TreePath.getPath(ast, n); TypeMirror typeMirror = typetrees.getTypeMirror(path); return typeMirror; } /** * Get a GOOL Type from a sub-branch of Java type tree, and a context. */ private IType goolType(Tree n, Context context) { // A null abstract Java type usually means that we are dealing with a // constructor. if (n == null) { return TypeNone.INSTANCE; } Log.i(getTypeMirror(n) == null ? "null" : getTypeMirror(n).toString()); return goolType(getTypeMirror(n), context); } /** * For primitive types. Converts Java type kinds to GOOL types. */ private IType goolType(TypeKind typeKind, String textualType) { switch (typeKind) { case BOOLEAN: return TypeBool.INSTANCE; case DOUBLE: case FLOAT: return TypeDecimal.INSTANCE; case SHORT: case INT: case LONG: return TypeInt.INSTANCE; case VOID: return TypeVoid.INSTANCE; case BYTE: return TypeByte.INSTANCE; case CHAR: return TypeChar.INSTANCE; default: return new TypeUnknown(textualType); } } /** * Get a GOOL Type from a Java type, and a context. */ private IType goolType(TypeMirror typeMirror, Context context) { if (typeMirror == null) { // A null abstract Java type usually means that we are dealing with // a constructor. return TypeNone.INSTANCE; } else if (typeMirror.getKind().isPrimitive()) { // Dealing with primitive types. // The overloaded goolType method will map Java abstract "kinds" to // GOOL primitive types // The textualtype is for passing on unrecognized primitive types return goolType(typeMirror.getKind(), typeMirror.toString()); } // System.out.println("Type " + typeMirror); // Here, types are intercepted by the RecognizerMatcher to be translated // into // GOOL library classes String goolClass = RecognizerMatcher.matchClass(typeMirror.toString()); if (goolClass != null) { // A GOOL library class matched with the current type has been // found. // We add it as a RecognizedDependency to the current context, // this dependency will get generated into imports in the target // language addDependencyToContext(context, new RecognizedDependency(goolClass)); return new TypeGoolLibraryClass(goolClass); } // Dealing with non-primitive types. // First, retrieve the full name of the Java type. Type type = (Type) typeMirror; Symbol classSymbol = (Symbol) type.asElement(); Log.d("XXX just before claiming a typeName from classsymbol XXX"); Log.d("XXX type, classSymbol, kind XXX"); Log.d(type.toString()); Log.d(classSymbol == null ? "null" : classSymbol.toString()); Log.d(typeMirror.getKind().toString()); Log.d("XXX"); String typeName; IType goolType; switch (typeMirror.getKind()) { case PACKAGE: // -- Dealing with Packages // Create a GOOL type of a type that matches the full name of the // Java type. typeName = classSymbol.getSimpleName().toString(); goolType = new TypePackage(typeName); // Whether in abstract Java of in GOOL, non-primitive types may have // arguments. // We convert them recursively, and add them up to the GOOL type. for (Type t : type.getTypeArguments()) { goolType.addArgument(goolType(t, context)); } return goolType; case DECLARED: // -- Dealing with classes // Create a GOOL type of a type that matches the full name of the // Java type. // Usually, this is a TypeClass, i.e. we assume that the type is // some generic declared class. // However some classes receive a particular treatment like Lists, // Maps etc. typeName = classSymbol.getSimpleName().toString(); goolType = string2IType(typeName, context); // Whether in abstract Java or in GOOL, enums are codes as classes // with some flag. // We deal with this case. boolean isEnum = ((classSymbol.flags() & Flags.ENUM) != 0); if (isEnum && goolType instanceof TypeClass) { ((TypeClass) goolType).setIsEnum(isEnum); } // Whether in abstract Java of in GOOL, non-primitive types may have // arguments. // We convert them recursively, and add them up to the GOOL type. for (Type t : type.getTypeArguments()) { goolType.addArgument(goolType(t, context)); } // TODO: sort out imports // Add the encountered type as a dependency of the current class, // which is context.getClassDef(). /* * if (!type.toString().startsWith("java.lang") && * !type.toString().startsWith("java.lang")) { if * (!goolType.toString().equalsIgnoreCase("gool") && * !context.getClassDef().getType().equals(goolType)) { * context.getClassDef().addDependency( new * TypeDependency(goolType)); } } */ return goolType; case EXECUTABLE: // -- Dealing with methods // Create a GOOL type of a type that matches the Java type. typeName = "MethodType"; goolType = new TypeMethod(typeName); // Whether in abstract Java of in GOOL, non-primitive types may have // arguments. // We convert them recursively, and add them up to the GOOL type. for (Type t : type.getTypeArguments()) { goolType.addArgument(goolType(t, context)); } return goolType; case TYPEVAR: // -- Dealing type variables // Create a GOOL type of a type that matches the Java type. typeName = "TypeVarType"; goolType = new TypeVar(typeName); // Whether in abstract Java of in GOOL, non-primitive types may have // arguments. // We convert them recursively, and add them up to the GOOL type. for (Type t : type.getTypeArguments()) { goolType.addArgument(goolType(t, context)); } return goolType; case VOID: return TypeVoid.INSTANCE; case ARRAY: // Recognized a fixed length Array: int[ ] intarray = new int[6]. // Not to be confused with java.util arrays. // Convert the type of the elements of the Array. // Then create a GOOL array type containing that elements of that // converted type. // As for other kind of arrays like ArrayList etc., they fall under // the DECLARED case. ArrayType arrayType = (ArrayType) typeMirror; return new TypeArray( goolType(arrayType.getComponentType(), context)); case NULL: return TypeNull.INSTANCE; default: // We met a type that we do not know how to handle. // Instead of throwing an error, we will just pass it on as such. return new TypeUnknown(typeMirror.toString()); } } // These Otd are classes that return classes like TypeList, TypeMap,... // why go through this intermediate step, and not replace the instantiations // new Otd() by new TypeList(), new TypeMap... // This is because these instances of Otd get placed in a map, // which gets called with a string like List and replies with what should // be, every time it gets called, a fresh instance of TypeList. // Thus, returning and Otd wrapper and doing a getType() upon it does the // job. It could have been handled through a case, though. private static abstract class Otd { abstract public IType getType(); }; private static final Map<String, Otd> string2otdMap = new HashMap<String, Otd>(); static { Otd tmpOtd = new Otd() { public IType getType() { return TypeString.INSTANCE; } }; string2otdMap.put("String", tmpOtd); string2otdMap.put("java.lang.String", tmpOtd); // We found a Java boxed Double. // As far as type goes, we unbox it and just say it is a GOOL Decimal. tmpOtd = new Otd() { public IType getType() { return TypeDecimal.INSTANCE; } }; string2otdMap.put("java.lang.Double", tmpOtd); // We found a Java boxed Integer. // As far as type goes, we unbox it and just say it is a GOOL Int. tmpOtd = new Otd() { public IType getType() { return TypeInt.INSTANCE; } }; string2otdMap.put("Integer", tmpOtd); string2otdMap.put("java.lang.Integer", tmpOtd); // NEXT We recognize that the abstact Java was using some well-known // class // Which we want to treat in a particular manner // I.e. for which we have a specific representation in the abstract // GOOL. tmpOtd = new Otd() { public IType getType() { return new TypeList(); } }; string2otdMap.put("List", tmpOtd); string2otdMap.put("ArrayList", tmpOtd); string2otdMap.put("java.util.ArrayList", tmpOtd); string2otdMap.put("gool.imports.java.util.ArrayList", tmpOtd); tmpOtd = new Otd() { public IType getType() { return new TypeMap(); } }; string2otdMap.put("Map", tmpOtd); string2otdMap.put("HashMap", tmpOtd); string2otdMap.put("java.util.HashMap", tmpOtd); string2otdMap.put("gool.imports.java.util.HashMap", tmpOtd); tmpOtd = new Otd() { public IType getType() { return new TypeEntry(); } }; string2otdMap.put("Entry", tmpOtd); string2otdMap.put("gool.imports.java.util.Map.Entry", tmpOtd); /* * tmpOtd = new Otd() { public IType getType() { return new TypeFile(); * } }; string2otdMap.put("File", tmpOtd); * string2otdMap.put("java.io.File", tmpOtd); */ } private IType string2IType(String typeName, Context context) { if (string2otdMap.containsKey(typeName)) { IType type = string2otdMap.get(typeName).getType(); addDependencyToContext(context, new TypeDependency(type)); return type; } else if (typeName.equalsIgnoreCase("Object")) { return TypeObject.INSTANCE; } else if (typeName.equalsIgnoreCase("Boolean")) { return TypeBool.INSTANCE; } else if (typeName.equalsIgnoreCase("Byte")) { return TypeByte.INSTANCE; } else if (TypeException.contains(typeName)) { return TypeException.get(typeName); } else { return new TypeClass(typeName); } } /** * THIS PART IS ABOUT ANCILLARY METHODS THAT HELP VISITING */ /** * When visiting a method or a constructor, one needs to go through the list * of its arguments, Visiting each of them recursively in turn, thereby * generation an expression that gets added to the method or constructor. * * @param list * @param expr * @param context */ private void addParameters(List<? extends ExpressionTree> list, Parameterizable expr, Context context) { if (list != null) { for (ExpressionTree p : list) { Expression arg = (Expression) p.accept(this, context); if (arg != null) { expr.addParameter(arg); } } } } /** * A subroutine to generate error messages, with the name of the file that * caused the error. Notice that String.format(...) has the same sort of * syntax to the old C printf. * * @param format * : a string with holes * @param message * : data to fill in those holes * @return a string: a filled in string, plus name of file concerned. */ private String error(String format, Object... message) { return String.format("%s [File %s]", String.format(format, message), ast.getSourceFile().getName()); } /** * Lets you check for a certain annotation; e.g. - "Override": used to set a * inherited flag and that way be able to generate annotation. - * "CustomCode": used to pass on code that should not be looked at by the * GOOL system. * * @param list * @param annotation * @return */ public boolean findAnnotation(List<? extends AnnotationTree> list, String annotation) { for (AnnotationTree n : list) { if (n.getAnnotationType().toString().equals(annotation)) { return true; } } return false; } /** * THIS PART IS ABOUT VISITING UNRECOGNIZED STUFF * * The following java abstract expressions are not recognized. This does not * necessarily mean that they will not be handled correctly by the GOOL * system. It means that there is no dedicated representation for them in * abstract GOOL, other than ExpressionUnknown. Still, this * ExpressionUnknown carries: - the type of the unrecognized expression; - * the concrete Java string of characters that represents it. */ @Override public Object visitAssert(AssertTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitBreak(BreakTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitCase(CaseTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitCatch(CatchTree n, Context context) { VarDeclaration parameter = (VarDeclaration) n.getParameter().accept( this, context); Block block = (Block) n.getBlock().accept(this, context); return new Catch(parameter, block); } @Override public Object visitCompoundAssignment(CompoundAssignmentTree n, Context context) { Node variable = (Node) n.getVariable().accept(this, context); Expression expression = (Expression) n.getExpression().accept(this, context); Operator operator = getOperator(n.getKind()); IType type = goolType(n, context); String textualoperator = n.toString() .replace(n.getVariable().toString(), "") .replaceFirst("=.*", "").trim(); return new CompoundAssign(variable, expression, operator, textualoperator, type); } @Override public Object visitConditionalExpression(ConditionalExpressionTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitContinue(ContinueTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitDoWhileLoop(DoWhileLoopTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitEmptyStatement(EmptyStatementTree n, Context context) { return new ExpressionUnknown(goolType(n, context), n.toString()); } @Override public Object visitInstanceOf(InstanceOfTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitLabeledStatement(LabeledStatementTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitOther(Tree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitSwitch(SwitchTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitSynchronized(SynchronizedTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitThrow(ThrowTree node, Context p) { Expression expression = (Expression) node.getExpression().accept(this, p); return new Throw(expression); } @Override public Object visitTry(TryTree node, Context p) { List<? extends CatchTree> javaCatches = node.getCatches(); List<Catch> catches = new ArrayList<Catch>(); for (CatchTree javaCatche : javaCatches) { Catch catchstatelent = (Catch) (javaCatche.accept(this, p)); catches.add(catchstatelent); } Block block = (Block) node.getBlock().accept(this, p); Block finilyBlock = new Block(); if (node.getFinallyBlock() != null) finilyBlock = (Block) node.getFinallyBlock().accept(this, p); return new Try(catches, block, finilyBlock); } @Override public Object visitTypeParameter(TypeParameterTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } @Override public Object visitWildcard(WildcardTree node, Context p) { return new ExpressionUnknown(goolType(node, p), node.toString()); } /** * THIS PART IS ABOUT VISITING RECOGNIZED STUFF : TYPES * * Whenever we hit an abstract java type, we convert it to a goolType with * goolType(). */ @Override public Object visitArrayType(ArrayTypeTree n, Context context) { return goolType(n, context); } @Override public Object visitParameterizedType(ParameterizedTypeTree node, Context context) { return goolType(node, context); } @Override public Object visitPrimitiveType(PrimitiveTypeTree n, Context context) { return goolType(n.getPrimitiveTypeKind(), n.toString()); } /** * THIS PART IS ABOUT VISITING EASY RECURSIVE CASES * * Whenever abstract java and abstract GOOL closely match, not much work is * required: we just need to propagate the visit recursively, and make up * the abstract GOOL from what is returned. * * For example when visit some abstract java corresponding to a "t[i]" node, * we visit t, visit i, and simply make up an ArrayAccess(visited t, visited * i) abstract GOOL node. The following cases are akin. */ @Override public Object visitArrayAccess(ArrayAccessTree n, Context context) { return new ArrayAccess((Expression) n.getExpression().accept(this, context), (Expression) n.getIndex().accept(this, context)); } @Override public Object visitAssignment(AssignmentTree n, Context context) { Node variable = (Node) n.getVariable().accept(this, context); Expression expression = (Expression) n.getExpression().accept(this, context); return new Assign(variable, expression); } @Override public Object visitBlock(BlockTree n, Context context) { Block block = new Block(); for (StatementTree stmt : n.getStatements()) { Statement statement = (Statement) stmt.accept(this, new Context( context)); if (statement != null) { block.addStatement(statement); } } return block; } @Override public Object visitWhileLoop(WhileLoopTree n, Context context) { return new While((Expression) n.getCondition().accept(this, context), (Statement) n.getStatement().accept(this, context)); } @Override public Object visitForLoop(ForLoopTree node, Context p) { List<? extends StatementTree> initializers = node.getInitializer(); if (initializers.size() > 1) { return new ExpressionUnknown(goolType(node, p), node.toString()); } List<? extends StatementTree> updaters = node.getUpdate(); if (updaters.size() > 1) { return new ExpressionUnknown(goolType(node, p), node.toString()); } Statement initializer = (Statement) initializers.get(0).accept(this, p); Expression condition = (Expression) node.getCondition().accept(this, p); Statement updater = (Statement) updaters.get(0).accept(this, p); return new For(initializer, condition, updater, (Statement) node .getStatement().accept(this, p)); } @Override public Object visitEnhancedForLoop(EnhancedForLoopTree n, Context context) { VarDeclaration varDec = (VarDeclaration) n.getVariable().accept(this, context); Expression expr = (Expression) n.getExpression().accept(this, context); Statement statements = (Statement) n.getStatement().accept(this, context); return new EnhancedForLoop(varDec, expr, statements); } @Override public Object visitIf(IfTree n, Context context) { Expression condition = (Expression) n.getCondition().accept(this, context); Statement thenStmt = (Statement) n.getThenStatement().accept(this, context); Statement elseStmt = null; if (n.getElseStatement() != null) { elseStmt = (Statement) n.getElseStatement().accept(this, context); } return new If(condition, thenStmt, elseStmt); } @Override public Object visitNewClass(NewClassTree n, Context context) { IType type = goolType(n.getIdentifier(), context); ClassNew c = new ClassNew(type); addParameters(n.getArguments(), c, context); return c; } @Override public Object visitParenthesized(ParenthesizedTree n, Context context) { return n.getExpression().accept(this, context); } @Override public Object visitReturn(ReturnTree node, Context p) { return new Return((Expression) node.getExpression().accept(this, p)); } @Override public Object visitTypeCast(TypeCastTree node, Context context) { return new CastExpression(goolType(node.getType(), context), (Expression) node.getExpression().accept(this, context)); } /** * When visit some "op v" node, we visit v, translate op, work out the * entire type... and make up a UnaryOperation(translated op, visited v, * type, concrete java for op) abstract GOOL node. */ @Override public Object visitUnary(UnaryTree n, Context context) { Expression expression = (Expression) n.getExpression().accept(this, context); Operator operator = getOperator(n.getKind()); IType type = goolType(n, context); String textualoperator = n.toString() .replace(n.getExpression().toString(), "").trim(); return new UnaryOperation(operator, expression, type, textualoperator); } @Override public Object visitBinary(BinaryTree n, Context context) { Expression leftExp = (Expression) n.getLeftOperand().accept(this, context); Expression rightExp = (Expression) n.getRightOperand().accept(this, context); Operator operator = getOperator(n.getKind()); IType type = goolType(n, context); String textualoperator = n.toString() .replace(n.getLeftOperand().toString(), "") .replace(n.getRightOperand().toString(), "").trim(); return new BinaryOperation(operator, leftExp, rightExp, type, textualoperator); } /** * THIS PART IS ABOUT VISITING THE ODD CASES */ // This is when Sun's java parser failed. @Override public Object visitErroneous(ErroneousTree n, Context context) { throw new IllegalArgumentException(error( "The sun java parser failed at %s.", n.toString())); } // ExpressionStatements are just Expressions followed by semicolons. @Override public Object visitExpressionStatement(ExpressionStatementTree n, Context context) { return n.getExpression().accept(this, context); } /** * THIS PART IS ABOUT VISITING THE DELICATE CASES */ /** * Literals, Variables, Declarations */ /** * Literals are simple values such as "3". Abstract GOOL represents them * pretty much the same as abstract java. */ @Override public Object visitLiteral(LiteralTree n, Context context) { String value = n.getValue() != null ? n.getValue().toString() : "null"; return new Constant(goolType(n, context), value); } /** * Deals with variable declarations. */ @Override public Object visitVariable(VariableTree n, Context context) { if (FORBIDDEN_KEYWORDS.contains(n.getName().toString())) { throw new IllegalArgumentException(error( "The variable named '%s' uses reserved keyword.", n.getName())); } // work out the GOOL type of the variable IType type = goolType(n.getType(), context); // work out the name, and make an abstract GOOL variable declaration VarDeclaration variable = new VarDeclaration(type, n.getName() .toString()); // find whether there is an initializing expression and if so, visit it, // and add the result to the GOOL variable declaration if (n.getInitializer() != null) { Expression initializer = (Expression) n.getInitializer().accept( this, context); variable.setInitialValue(initializer); } // a variable declaration may be an attribute declaration (a field), in // which case it carries modifiers // and gets represented differently in GOOL, i.e. wrapped up with a // Field(). // TODO: actually, any variable declaration could have modifiers. Collection<Modifier> modifiers = (Collection<Modifier>) n .getModifiers().accept(this, context); if (n.getType() instanceof MemberSelectTree || !modifiers.isEmpty()) { Field f = new Field(modifiers, variable); context.addDeclaration(f, f.getName(), getTypeMirror(n)); return f; } context.addDeclaration(variable, variable.getName(), getTypeMirror(n)); return variable; } @Override public Object visitIdentifier(IdentifierTree n, Context context) { if (FORBIDDEN_KEYWORDS.contains(n.getName().toString())) { throw new IllegalArgumentException(error( "The variable named '%s' uses reserved keyword.", n.getName())); } IType type = goolType(n, context); // TODO identifiers. Create a specific node to access to class literal // (i.e. when calling static members). if (type.getName().equals(n.getName().toString())) { return new Constant(type, n.getName().toString()); } // This method returns a VarAccess, accessing a previously declared // variable, i.e. a VarDeclaration. Dec dec; if (context != null) { dec = context.getDeclaration(n.getName().toString(), getTypeMirror(n)); if (dec == null) { Log.w(String .format("No declaration found for '%s' of type '%s' in curent context", n.getName(), getTypeMirror(n))); dec = new VarDeclaration(goolType(n, context), n.getName() .toString()); } } else { dec = new VarDeclaration(goolType(n, context), n.getName() .toString()); } return new VarAccess(dec); } /** * Lists, maps */ /** * List declarations are not passed on, but represented as such in abstract * GOOL. We need to work out the type, the dimensions, and the * initialization of the list before in order to build up the ArrayNew(type, * dimensions,initialization) abstract GOOL node that represents it. */ @Override public Object visitNewArray(NewArrayTree node, Context p) { List<Expression> initialiList = new ArrayList<Expression>(); if (node.getInitializers() != null) { for (ExpressionTree expression : node.getInitializers()) { initialiList.add((Expression) expression.accept(this, p)); } } List<Expression> dimesExpressions = new ArrayList<Expression>(); if (node.getDimensions() != null) { for (ExpressionTree expression : node.getDimensions()) { dimesExpressions.add((Expression) expression.accept(this, p)); } } return new ArrayNew(goolType(node.getType(), p), dimesExpressions, initialiList); } /** * Deals with expressions like "target.identifier()". It gets called by * visitMethodInvokation(). */ @Override public Object visitMemberSelect(MemberSelectTree n, Context context) { Expression target = (Expression) n.getExpression() .accept(this, context); String identifier = n.getIdentifier().toString(); Log.d("XX Entering MemberSelect with target-identifier XX"); Log.d(identifier.toString()); Log.d("XX"); /* * TODO Currently we are assuming that the following methods are always * the same as the methods "toString" and "equals" belonging to the * Object class. */ if (identifier.equalsIgnoreCase("equals")) { return new EqualsCall(target); } if (identifier.equals("toString")) { return new ToStringCall(target); } IType type = target.getType(); Log.d("X Target type X"); Log.d(type.toString()); Log.d("X"); if (type != null) { if (type instanceof TypeList) { if (identifier.equals("add")) { return new ListAddCall(target); } if (identifier.equals("remove")) { return new ListRemoveCall(target); } if (identifier.equals("removeAt")) { return new ListRemoveAtCall(target); } if (identifier.equals("get")) { return new ListGetCall(target); } if (identifier.equals("size")) { return new ListSizeCall(target); } if (identifier.equals("isEmpty")) { return new ListIsEmptyCall(target); } if (identifier.equals("getIterator")) { return new ListGetIteratorCall(target); } if (identifier.equals("contains")) { return new ListContainsCall(target); } } if (type instanceof TypeMap) { if (identifier.equals("put")) { return new MapPutCall(target); } if (identifier.equals("remove")) { return new MapRemoveCall(target); } if (identifier.equals("get")) { return new MapGetCall(target); } if (identifier.equals("size")) { return new MapSizeCall(target); } if (identifier.equals("isEmpty")) { return new MapIsEmptyCall(target); } if (identifier.equals("getIterator")) { return new MapGetIteratorCall(target); } if (identifier.equals("containsKey")) { return new MapContainsKeyCall(target); } if (identifier.equals("entrySet")) { return target; } } if (type instanceof TypeEntry) { if (identifier.equals("getKey")) { return new MapEntryGetKeyCall(target); } if (identifier.equals("getValue")) { return new MapEntryGetValueCall(target); } } } // the type of the target // or the identifier // were not recognized // i.e. it is not a library that requires a particular treatment // it gets the standard treatment. Log.d("X Standard method call for X"); Log.d(n.toString()); Log.d("X"); if (context != null) { Dec dec = context.getClassContext().getDeclaration( n.getIdentifier().toString(), getTypeMirror(n)); if (dec == null) { Log.w(String .format("No declaration found for '%s' of type '%s' in curent context", n.getIdentifier(), getTypeMirror(n))); dec = new VarDeclaration(goolType(n, context), n .getIdentifier().toString()); } } Log.w(String.format( "No declaration found for '%s' of type '%s' in curent context", n.getIdentifier(), getTypeMirror(n))); Dec dec = new VarDeclaration(goolType(n, context), n.getIdentifier() .toString()); MemberSelect f = new MemberSelect(target, dec); return f; } /** * Classes, packages, imports... */ @Override public Object visitClass(ClassTree n, Context context) { // Get the name of the class JCClassDecl c = (JCClassDecl) n; ClassDef classDef = new ClassDef(n.getSimpleName().toString()); Log.i(String.format("Parsing class %s", n.getSimpleName())); // The new class will provide the context for the things parsed inside // of it. context = new Context(classDef, context); // Create a variable called 'this' of type 'Classtree' to prevent errors context.addDeclaration(new VarDeclaration(TypeObject.INSTANCE, "this"), "this", getTypeMirror(n)); context.addDeclaration( new VarDeclaration(TypeObject.INSTANCE, "super"), "super", null); // Enums are just classes with a particular flag, which we set up if // necessary // We said that before, with DECLARED types // Here this is for the class itself classDef.setIsEnum((c.mods.flags & Flags.ENUM) != 0); Collection<Modifier> modifiers = (Collection<Modifier>) n .getModifiers().accept(this, context); /* * For now do not allow 'static' modifier for classes, as it means very * different things in the target languages. TODO: static class support. */ modifiers.remove(Modifier.STATIC); /* * For now classes must be 'public' in order to solve accessibility in * the target languages. TODO: more visibility support. */ modifiers.add(Modifier.PUBLIC); // The forceplatform annotation is unused for now // The idea is to be able to specify the target platform // As an annotation of the concrete input language for (AnnotationTree annotationTree : n.getModifiers().getAnnotations()) { if (annotationTree.getAnnotationType().toString() .equals("ForcePlatform")) { if (annotationTree.getArguments().size() > 0) { Node an = (Node) annotationTree.getArguments().get(0) .accept(this, context); if (an instanceof Assign) { Platform platform = Platform .valueOf(((Constant) ((Assign) an).getValue()) .getValue().toString()); if (platform != null) { classDef.setPlatform(platform); } break; } } throw new IllegalStateException( error("The ForcePlatform annotation should have a platform as its parameter. For example @ForcePlatform(platform=\"JAVA\")")); } } // Let us deal with modifiers classDef.setModifiers(modifiers); JCModifiers mtree = (JCModifiers) n.getModifiers(); classDef.setIsInterface((mtree.flags & Flags.INTERFACE) != 0); // Make sure some Target language is specified for this class. if (classDef.getPlatform() == null) { classDef.setPlatform(defaultPlatform); } // Register the class as one of the outputs goolClasses.put(classDef.getType(), classDef); /* * If the class has the CustomCode annotation, we enerate a class * without processing the code inside but just commenting it Indeed, * notice that createMethod(MethodTree n, Context context, boolean * createOnlySignature, boolean commentOriginalCode) */ boolean customCode = findAnnotation(n.getModifiers().getAnnotations(), "CustomCode"); if (customCode) { for (Tree tree : n.getMembers()) { if (tree instanceof MethodTree) { MethodTree method = (MethodTree) tree; classDef.addMethod(createMethod(method, context, true, false)); } } return classDef; } // Setup Inheritance information if (n.getExtendsClause() != null) { IType parentType = goolType(n.getExtendsClause(), context); classDef.setParentClass(parentType); } for (Tree iface : n.getImplementsClause()) { classDef.addInterface(goolType(iface, context)); } // add dummy declarations to the context to solve the problem // caused by forward declarations // TODO find a way to create the actual declarations // (but shallow) before the internal parsing for (Tree tree : n.getMembers()) { Dec dec = null; Collection<Modifier> mods = null; if (tree instanceof MethodTree) { dec = new Meth(goolType(tree, context), ((MethodTree) tree) .getName().toString()); mods = (Collection<Modifier>) ((MethodTree) tree) .getModifiers().accept(this, context); } else if (tree instanceof VariableTree) { dec = new Field(((VariableTree) tree).getName().toString(), goolType(tree, context), null); mods = (Collection<Modifier>) ((VariableTree) tree) .getModifiers().accept(this, context); } if (dec != null) { dec.addModifiers(mods); context.addDeclaration(dec, dec.getName(), getTypeMirror(tree)); } } // Recursively go through each member and add it to the abstract GOOL // class for (Tree tree : n.getMembers()) { Node member = (Node) tree.accept(this, context); // if (member instanceof Dec) // context.addDeclaration((Dec)member); if (member instanceof Meth) { classDef.addMethod((Meth) member); } else if (member instanceof Field) { classDef.addField((Field) member); } else if (member instanceof VarDeclaration) { classDef.addField(new Field(new ArrayList<Modifier>(), // Arrays.asList(Modifier.PRIVATE), (VarDeclaration) member)); } else if (member != null) { Log.e(String.format("Unrecognized member for class %s: %s ", classDef.getName(), member)); } } return classDef; } /** * This is the beginning of the visit. But it gets called several times, * once per compilation unit. A CompilationUnit is a Java file But a java * file can have various classes inside. */ @Override public Object visitCompilationUnit(CompilationUnitTree n, Context context) { //System.out.println("[JavaRecognizer] BEGIN of visitCompilationUnit"); // The destination package is either null or that specified by the // visited package String ppackage = null; if (n.getPackageName() != null) { ppackage = n.getPackageName().accept(this, context).toString(); } // Dealing with the imports // Each class that is imported is registered as a dependency // TODO: We don't automatically go and compile dependencies. List<Dependency> dependencies = new ArrayList<Dependency>(); // GoolMatcher init call RecognizerMatcher.init("java"); for (ImportTree imp : n.getImports()) { String dependencyString = imp.getQualifiedIdentifier().toString(); // if (!dependencyString.contains("gool.imports.java") // && !dependencyString.contains("gool.imports.java.annotations")) { if (!RecognizerMatcher.matchImport(dependencyString)) { dependencies.add(new UnrecognizedDependency(dependencyString)); } } // Visit each member class in turn // And add it to the Dependencies too // Each class may have a package. // In order not to create two different GOOL packages // when the JAVA packages names were in fact the same // we remember the package name in the packagesCache. for (Tree unit : n.getTypeDecls()) { ClassDef classDef = (ClassDef) unit.accept(this, context); if (ppackage != null) { Package p = packagesCache.get(ppackage); if (p == null) { p = new Package(ppackage); packagesCache.put(ppackage, p); } p.addClass(classDef); classDef.setPpackage(p); } classDef.addDependencies(dependencies); } for (ClassDef classDef : getGoolClasses()) { GoolLibraryClassAstBuilder.init(defaultPlatform); int x = 0; for (Dependency dep : classDef.getDependencies()) { x++; if (dep instanceof RecognizedDependency) { GoolLibraryClassAstBuilder .buildGoolClass(((RecognizedDependency) dep) .getName()); } } } for (ClassDef goolClassAst : GoolLibraryClassAstBuilder.getBuiltAsts()) { goolClasses.put(goolClassAst.getType(), goolClassAst); /*System.out .println("[JavaRecognizer] A GOOL library AST has been successfully built and added to the current AST collection: " + goolClassAst.getPackageName() + "." + goolClassAst.getName()); */ } //System.out.println("[JavaRecognizer] END of visitCompilationUnit."); return null; } // This was handled already with the getImports of visitCompilationUnit @Override public Object visitImport(ImportTree n, Context context) { throw new IllegalStateException( "return TypeDependency(TypeClass(n.getQualifiedIdentifier().accept(this, context).toString()))"); } /** * Methods */ @Override public Object visitMethod(MethodTree n, Context context) { boolean customCode = findAnnotation(n.getModifiers().getAnnotations(), "CustomCode"); boolean isInherited = findAnnotation(n.getModifiers().getAnnotations(), "Override"); Meth method = createMethod(n, new Context(context), customCode, true); method.setInherited(isInherited); return method; } private Meth createMethod(MethodTree n, Context context, boolean createOnlySignature, boolean commentOriginalCode) { Meth method; // recover modifiers Collection<Modifier> modifiers = (Collection<Modifier>) n .getModifiers().accept(this, context); // deal with the case of constructors.... // XXXXXXXXXXXXXXXX why is there no argument to the contructor??? if (n.getReturnType() == null) { method = new Constructor(); } else { IType type = goolType(n.getReturnType(), context); /* * The hard way to determine if the current method is the entry * point. It may be better if we implement this as an annotation. * XXXXXXXXXXXXX All this to see if it is a Main? XXXXXXXXXXXXX what * is the logic of it? XXXXXXXXXXXXX Why not see if it is called * main? */ boolean isMainMethod = type.equals(TypeVoid.INSTANCE) && modifiers.contains(Modifier.PUBLIC) && modifiers.contains(Modifier.STATIC); if (n.getParameters() != null && n.getParameters().size() == 1) { IType t = goolType(n.getParameters().get(0).getType(), context); if (t instanceof TypeArray) { isMainMethod &= ((TypeArray) t).getElementType().equals( TypeString.INSTANCE); } else { isMainMethod = false; } } else { isMainMethod = false; } if (isMainMethod) { method = new MainMeth(); } else { method = new Meth(type, n.getName().toString()); } context.addDeclaration(method, method.getName(), getTypeMirror(n)); } // go through each parameter and add it. if (n.getParameters() != null) { for (VariableTree p : n.getParameters()) { VarDeclaration v = new VarDeclaration((Dec) p.accept(this, context)); // context.addDeclaration(v); method.addParameter(v); } } // Can we safely move it to L1298? method.setModifiers(modifiers); // XXXXXXXXXXXXXX What is this entire if for? if (n.getBody() != null) { if (createOnlySignature) { if (commentOriginalCode) { method.addStatement(new Comment(n.getBody().toString())); } if (!TypeVoid.INSTANCE.equals(method.getType()) && !method.isConstructor()) { String returnValue = "null"; IType returnType; if (method.getType() instanceof TypeInt) { returnValue = "-1"; returnType = TypeInt.INSTANCE; } else if (method.getType() instanceof TypeBool) { returnValue = "false"; returnType = TypeBool.INSTANCE; } else if (method.getType() instanceof TypeByte) { returnValue = "0"; returnType = TypeByte.INSTANCE; } else { returnType = TypeNull.INSTANCE; } method.addStatement(new Return(new Constant(returnType, returnValue))); } } else { for (StatementTree stmt : n.getBody().getStatements()) { Statement statement = (Statement) stmt .accept(this, context); if (statement instanceof InitCall && method instanceof Constructor) { ((Constructor) method) .addInitCall((InitCall) statement); } else if (statement != null) { method.addStatement(statement); } } } } return method; } /** * XXXXXXXXXXXXX Could you explain what the main steps are? This is the time * to deal with recognition of certain library calls such as * System.out.println Indeed, System.out.println is imbricated: * MethodInvocation * (<Target:MemberSelect<Target:MemberSelect<Target:"System", * Identifier:"out">,<Identifier:"println">>>) So it is easier to identify * at this stage */ @Override public Object visitMethodInvocation(MethodInvocationTree n, Context context) { Symbol method = (Symbol) TreeInfo.symbol((JCTree) n.getMethodSelect()); // Here, we build a method signature that may get matched with a //GOOL library method by the RecognizerMatcher String signature = (method.owner + "." + method.name + method.type) .replace(")", "):"); //System.out.println("Method " + signature); Expression target; String goolMethod = null; if (n.getMethodSelect().toString().equals("System.out.println")) { context.getClassDef().addDependency(new SystemOutDependency()); target = new SystemOutPrintCall(); } else if (n.getMethodSelect().toString().equals("super")) { target = new ParentCall(goolType( ((MethodSymbol) method).getReturnType(), context)); } else if (n.getMethodSelect().toString().equals("this")) { target = new ThisCall(goolType( ((MethodSymbol) method).getReturnType(), context)); } else { Log.d("YYYY from method to member select YYYY"); Log.d(n.getMethodSelect().toString()); Log.d("YYYYYYYYYYYYY"); // The target is the xxxx part of some method invocation xxxx(). // Here is when we possibly visitMemberSelect(). target = (Expression) n.getMethodSelect().accept(this, context); goolMethod = RecognizerMatcher.matchMethod(signature); /* * if (goolMethod != null && target instanceof MemberSelect) { * ((MemberSelect) target).setIdentifier(goolMethod * .substring(goolMethod.lastIndexOf(".") + 1)); } */ } // the following piece of code is used to fix a weird code generation // bug that occurs only with ObjC. if (context.getClassDef().getPlatform() == ObjcPlatform.getInstance() && (target instanceof InitCall) && (target instanceof Parameterizable)) { target = new MethCall(goolType( ((MethodSymbol) method).getReturnType(), context), target); addParameters(n.getArguments(), (Parameterizable) ((MethCall) target).getTarget(), context); } if (!(target instanceof Parameterizable)) { target = new MethCall(goolType( ((MethodSymbol) method).getReturnType(), context), target); } addParameters(n.getArguments(), (Parameterizable) target, context); if ((target instanceof MethCall) && (goolMethod != null)) ((MethCall) target).setGoolLibraryMethod(goolMethod); return target; } /** * Translates abstract Java modifiers into abstract GOOL modifiers */ @Override public Object visitModifiers(ModifiersTree n, Context context) { Collection<Modifier> result = new HashSet<Modifier>(); Set<javax.lang.model.element.Modifier> flags = n.getFlags(); for (javax.lang.model.element.Modifier modifier : flags) { switch (modifier) { case PRIVATE: result.add(Modifier.PRIVATE); break; case PUBLIC: result.add(Modifier.PUBLIC); break; case PROTECTED: result.add(Modifier.PROTECTED); break; case STATIC: result.add(Modifier.STATIC); break; case ABSTRACT: result.add(Modifier.ABSTRACT); break; case FINAL: result.add(Modifier.FINAL); break; case VOLATILE: result.add(Modifier.VOLATILE); break; case TRANSIENT: result.add(Modifier.TRANSIENT); break; case NATIVE: result.add(Modifier.NATIVE); break; case STRICTFP: result.add(Modifier.STRICTFP); break; case SYNCHRONIZED: result.add(Modifier.SYNCHRONIZED); break; } } return result; } public void setTypes(javax.lang.model.util.Types types) { Context.setTypes(types); } } /** * Contains the informations passed down when visiting the Java tree. As for * now, this means all definition from the current context and a link to the * higher level one. A new context must be created when entering a class, a * method or a bloc. */ class Context { /* * * This javadoc seams out-dated: none of the original methods of this * class where used... * * When we hit an "import MyCustomClass" in Java, we make an * "import MyCustomClass" in GOOL: it must be passed on. Later as we visit * the Java tree downwards (from root to leafs), we propagate this context * information. This is because if something has been redefined in such a * MyCustomClass.java, it must be treated differently from something of the * same name without this context. For instance mycustom.List will be passed * on, whereas List might have been translated. */ /** * The link to the parent context (who's declarations are still in scope). */ private Context parent; /** * If this context was created at a class level, contains that class, null * otherwise. */ private ClassDef classDef; /** * Information on types, necessary for comparing them */ static private javax.lang.model.util.Types types; /** * Provides the class with a way to compare types * * @param types * The 'javax.lang.model.util.Type' given by the parser */ static public void setTypes(javax.lang.model.util.Types types) { Context.types = types; } /** * All the declaration defined at the current level sorted by identifier and * type */ private HashMap<String, HashMap<TypeMirror, Dec>> map; public Context(Context parent) { this(null, parent); } public Context(ClassDef classDef, Context parent) { map = new HashMap<String, HashMap<TypeMirror, Dec>>(); this.parent = parent; this.classDef = classDef; } /** * Register a declaration in the context * * @param dec * The GOOL declaration to register * @param name * The identifier associated with the declaration * @param type * The type of the declaration as returned by the java parser */ public void addDeclaration(Dec dec, String name, TypeMirror type) { HashMap<TypeMirror, Dec> identifier = map.get(name); if (identifier == null) { identifier = new HashMap<TypeMirror, Dec>(); map.put(name, identifier); } identifier.put(type, dec); } /** * Get a declaration from the current context or a parent. * * @param name * The identifier we are looking for * @param type * The type of the declaration we are looking for. For a method, * the arguments types must be compatible, not equal. * @return The declaration, or null if no declaration was found. */ public Dec getDeclaration(String name, TypeMirror type) { Dec ret = getDeclarationHere(name, type); if (ret != null) return ret; if (parent != null) return parent.getDeclaration(name, type); else return null; } /** * Get a declaration from the current context but not a parent. * * @param name * The identifier we are looking for * @param type * The type of the declaration we are looking for. For a method, * the arguments types must be compatible, not equal. * @return The declaration, or null if no declaration was found. */ public Dec getDeclarationHere(String name, TypeMirror type) { HashMap<TypeMirror, Dec> identifier = map.get(name); if (identifier != null) { for (Entry<TypeMirror, Dec> e : identifier.entrySet()) { if (isTypeMirrorCompatible(type, e.getKey())) return e.getValue(); } } return null; } /** * Get the context associated with the class we are visiting * * @return The top-level(?) context */ public Context getClassContext() { if (classDef != null) return this; else if (parent != null) return parent.getClassContext(); else return null; } /** * The definition of the class we are visiting * * @return the ClassDef associated with the top-level(?) context */ public ClassDef getClassDef() { if (classDef == null && parent != null) return parent.getClassDef(); else return classDef; } /** * Test if a instance and a declaration have compatible types.<br> * For fields-like types, the instance must be assignable to the * declaration. For methods types, it must be possible to call the * declaration with arguments who's types are the ones of the instance.<br> * * @param declaration * @param instance * @return 'true' if the types are compatibles or either-one is null. */ private boolean isTypeMirrorCompatible(TypeMirror declaration, TypeMirror instance) { if (declaration == null || instance == null) return true; if (declaration instanceof ExecutableType && instance instanceof ExecutableType) { return types.isSubsignature((ExecutableType) declaration, (ExecutableType) instance); } else { return types.isAssignable(declaration, instance); } } }