From c575ae7ccbf1fd259fc8339d63d0786fc4e16ad9 Mon Sep 17 00:00:00 2001 From: aFedasenka Date: Fri, 5 Oct 2012 00:21:12 +0200 Subject: [PATCH] Cpp header file generation --- .../devtools/j2cpp/CppGenerationTest.java | 17 +- .../j2cpp/gen/CppHeaderGenerator.java | 600 ++++++++++++++++++ .../j2cpp/gen/CppSourceFileGenerator.java | 353 +++++++++++ 3 files changed, 962 insertions(+), 8 deletions(-) create mode 100644 build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppHeaderGenerator.java create mode 100644 build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppSourceFileGenerator.java diff --git a/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/CppGenerationTest.java b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/CppGenerationTest.java index c7dfdb2249..bbe8096d47 100644 --- a/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/CppGenerationTest.java +++ b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/CppGenerationTest.java @@ -8,9 +8,9 @@ import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.CompilationUnit; import com.google.common.io.Files; +import com.google.devtools.j2cpp.gen.CppHeaderGenerator; import com.google.devtools.j2objc.J2ObjC.Language; -import com.google.devtools.j2objc.gen.ObjectiveCHeaderGenerator; -import com.google.devtools.j2objc.gen.ObjectiveCImplementationGenerator; +//import com.google.devtools.j2objc.gen.ObjectiveCImplementationGenerator; public abstract class CppGenerationTest extends GenerationTest { @@ -22,10 +22,11 @@ public abstract class CppGenerationTest extends GenerationTest { System.out.println("-----------------------------HEADER-----------------"); System.out.println(getTranslatedFile(type+".h")); System.out.println(); - System.out.println(); - System.out.println("------------------------------------------------"); - System.out.println("-------------------SOURCE-------------------"); - System.out.println(getTranslatedFile(type+".m")); +// TODO commented for testing a header only +// System.out.println(); +// System.out.println("------------------------------------------------"); +// System.out.println("-------------------SOURCE-------------------"); +// System.out.println(getTranslatedFile(type+".m")); } protected void assertNoCompilationErrors(CompilationUnit unit) { @@ -43,7 +44,7 @@ public abstract class CppGenerationTest extends GenerationTest { CompilationUnit unit = translateType(typeName, source); assertNoCompilationErrors(unit); String sourceName = typeName + ".java"; - ObjectiveCHeaderGenerator.generate(sourceName, source, unit); - ObjectiveCImplementationGenerator.generate(sourceName, Language.OBJECTIVE_C, unit, source); + CppHeaderGenerator.generate(sourceName, source, unit); +// ObjectiveCImplementationGenerator.generate(sourceName, Language.OBJECTIVE_C, unit, source); } } diff --git a/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppHeaderGenerator.java b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppHeaderGenerator.java new file mode 100644 index 0000000000..87a760e61d --- /dev/null +++ b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppHeaderGenerator.java @@ -0,0 +1,600 @@ +package com.google.devtools.j2cpp.gen; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.BlockComment; +import org.eclipse.jdt.core.dom.Comment; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.devtools.j2objc.J2ObjC; +import com.google.devtools.j2objc.Options; +import com.google.devtools.j2objc.types.HeaderImportCollector; +import com.google.devtools.j2objc.types.IOSMethod; +import com.google.devtools.j2objc.types.ImportCollector; +import com.google.devtools.j2objc.types.Types; +import com.google.devtools.j2objc.util.ErrorReportingASTVisitor; +import com.google.devtools.j2objc.util.NameTable; +import com.google.devtools.j2objc.util.UnicodeUtils; + +public class CppHeaderGenerator extends CppSourceFileGenerator{ + + /** + * Generate an C++ header file for each type declared in a specified + * compilation unit. + */ + public static void generate(String fileName, String source, CompilationUnit unit) { + CppHeaderGenerator headerGenerator = new CppHeaderGenerator(fileName, source, unit); + headerGenerator.generate(unit); + } + + private CppHeaderGenerator(String fileName, String source, CompilationUnit unit) { + super(fileName, source, unit, false); + } + + @Override + protected String getSuffix() { + return ".h"; + } + + public void generate(CompilationUnit unit) { + println(J2ObjC.getFileHeader(getSourceFileName())); + + @SuppressWarnings("unchecked") + List types = unit.types(); // safe by definition + Set moreForwardTypes = sortTypes(types); + //TODO + printImportsAndForwardReferences(unit, moreForwardTypes); + + for (AbstractTypeDeclaration type : types) { + newline(); + generate(type); + } + save(unit); + } + + @Override + public void generate(TypeDeclaration node) { + String typeName = NameTable.getFullName(node); + String superName = NameTable.getSuperClassName(node); + + //DONE if extends superclass other than Object + //TODO if implements interfaces + if ((superName != null) && (!superName.equals("NSObject"))) { + printf(": public %s", superName); + } + + printConstantDefines(node); + printf("class %s", typeName); + println(" {"); + printInstanceVariables(node.getFields()); + + MethodDeclaration[] allMethods = node.getMethods(); + List publicMethods = new ArrayList(); + List protectedMethods = new ArrayList(); + List privateMethods = new ArrayList(); + + for (MethodDeclaration m : allMethods) { + if (Modifier.isPublic(m.getModifiers())) { + publicMethods.add(m); + } else if (Modifier.isProtected(m.getModifiers())) { + protectedMethods.add(m); + } else if (Modifier.isPrivate(m.getModifiers())) { + privateMethods.add(m); + } + } + if (publicMethods.size() > 0) { + println("\npublic: "); + printMethods(publicMethods); + } + if (protectedMethods.size() > 0) { + println("\nprotected: "); + printMethods(protectedMethods); + } + if (privateMethods.size() > 0) { + println("\nprivate: "); + printMethods(privateMethods); + } + println("};\n"); + } + + @Override + protected void generate(AnnotationTypeDeclaration node) { + String typeName = NameTable.getFullName(node); + printf("@protocol %s < NSObject >\n@end\n", typeName); + } + + @Override + protected void generate(EnumDeclaration node) { + printConstantDefines(node); + String typeName = NameTable.getFullName(node); + @SuppressWarnings("unchecked") + List constants = node.enumConstants(); + + // C doesn't allow empty enum declarations. Java does, so we skip the + // C enum declaration and generate the type declaration. + if (!constants.isEmpty()) { + println("typedef enum {"); + + // Strip enum type suffix. + String bareTypeName = typeName.endsWith("Enum") ? + typeName.substring(0, typeName.length() - 4) : typeName; + + // Print C enum typedef. + indent(); + int ordinal = 0; + for (EnumConstantDeclaration constant : constants) { + printIndent(); + printf("%s_%s = %d,\n", bareTypeName, constant.getName().getIdentifier(), ordinal++); + } + unindent(); + printf("} %s;\n\n", bareTypeName); + } + + List fields = Lists.newArrayList(); + List methods = Lists.newArrayList(); + for (Object decl : node.bodyDeclarations()) { + if (decl instanceof FieldDeclaration) { + fields.add((FieldDeclaration) decl); + } else if (decl instanceof MethodDeclaration) { + methods.add((MethodDeclaration) decl); + } + } + + // Print enum type. + printf("@interface %s : JavaLangEnum < NSCopying", typeName); + ITypeBinding enumType = Types.getTypeBinding(node); + for (ITypeBinding intrface : enumType.getInterfaces()) { + if (!intrface.getName().equals(("Cloneable"))) { // Cloneable handled below. + printf(", %s", NameTable.getFullName(intrface)); + } + } + println(" > {"); + FieldDeclaration[] fieldDeclarations = fields.toArray(new FieldDeclaration[0]); + printInstanceVariables(fieldDeclarations); + println("}"); +// printProperties(fieldDeclarations); + for (EnumConstantDeclaration constant : constants) { + printf("+ (%s *)%s;\n", typeName, NameTable.getName(constant.getName())); + } + println("+ (IOSObjectArray *)values;"); + printf("+ (%s *)valueOfWithNSString:(NSString *)name;\n", typeName); + println("- (id)copyWithZone:(NSZone *)zone;"); + printMethods(methods); + println("@end"); + } + + @Override + protected String methodDeclaration(MethodDeclaration m) { + return super.methodDeclaration(m) + ";\n"; + } + + @Override + protected String mappedMethodDeclaration(MethodDeclaration method, IOSMethod mappedMethod) { + return super.mappedMethodDeclaration(method, mappedMethod) + ";\n"; + } + + @Override + protected String constructorDeclaration(MethodDeclaration m) { + return super.constructorDeclaration(m) + ";\n"; + } + + @Override + protected void printStaticConstructorDeclaration(MethodDeclaration m) { + // Don't do anything. + } + + @Override + protected void printMethod(MethodDeclaration m) { + IMethodBinding binding = Types.getMethodBinding(m); + if (!binding.isSynthetic()) { + super.printMethod(m); + } + } + + private void printImportsAndForwardReferences(CompilationUnit unit, Set forwards) { + HeaderImportCollector collector = new HeaderImportCollector(); + collector.collect(unit, getSourceFileName()); + Set imports = collector.getImports(); + Set superTypes = collector.getSuperTypes(); + + // Print forward declarations. + Set forwardStmts = Sets.newTreeSet(); + for (ImportCollector.Import imp : imports) { + forwardStmts.add(createForwardDeclaration(imp.getTypeName(), imp.isInterface())); + } + for (ITypeBinding forward : forwards) { + forwardStmts.add( + createForwardDeclaration(NameTable.getFullName(forward), forward.isInterface())); + } + if (!forwardStmts.isEmpty()) { + for (String stmt : forwardStmts) { + println(stmt); + } + newline(); + } + + // Print native imports. + int endOfImportText = unit.types().isEmpty() ? unit.getLength() + : ((ASTNode) unit.types().get(0)).getStartPosition(); + @SuppressWarnings("unchecked") + List comments = unit.getCommentList(); // safe by definition + for (Comment c : comments) { + int start = c.getStartPosition(); + if (start >= endOfImportText) { + break; + } + if (c instanceof BlockComment) { + String nativeImport = extractNativeCode(start, c.getLength()); + if (nativeImport != null) { // if it has a JSNI section + println(nativeImport.trim()); + } + } + } + + // Print collected imports. +// println("#import \"JreEmulation.h\""); + if (!superTypes.isEmpty()) { + Set importStmts = Sets.newTreeSet(); + for (ImportCollector.Import imp : superTypes) { + importStmts.add(createImport(imp)); + } + for (String stmt : importStmts) { + println(stmt); + } + } + } + + protected String createForwardDeclaration(String typeName, boolean isInterface) { + return String.format("@%s %s;", isInterface ? "protocol" : "class", typeName); + } + + protected String createImport(ImportCollector.Import imp) { + return String.format("#include \"%s.h\"", imp.getImportFileName()); + } + + /** + * If an inner type is defined before any of its super types are, put the + * super types first. Otherwise, keep the order as is, to make debugging + * easier. For field and method references to a following type, add a + * forward type. + * + * @return the set of forward types to declare. + */ + static Set sortTypes(List types) { + final List typesCopy = + new ArrayList(types); + + final Map index = Maps.newHashMap(); + for (AbstractTypeDeclaration type : typesCopy) { + index.put(Types.getTypeBinding(type).getBinaryName(), type); + } + + final Multimap references = HashMultimap.create(); + final Multimap superTypes = HashMultimap.create(); + + // Collect all references to other types, but track super types + // separately from other references. + for (AbstractTypeDeclaration type : typesCopy) { + final String typeName = Types.getTypeBinding(type).getBinaryName(); + + ErrorReportingASTVisitor collector = new ErrorReportingASTVisitor() { + protected void addSuperType(Type type) { + ITypeBinding binding = type == null ? null : Types.getTypeBinding(type); + if (binding != null && isMember(binding)) { + superTypes.put(typeName, binding.getBinaryName()); + } + } + + private void addReference(Type type) { + ITypeBinding binding = type == null ? null : Types.getTypeBinding(type); + if (binding != null && isMember(binding)) { + references.put(typeName, binding.getBinaryName()); + } + } + + // Only collect references to types members. + private boolean isMember(ITypeBinding binding) { + return index.containsKey(binding.getBinaryName()); + } + + @Override + public boolean visit(FieldDeclaration node) { + addReference(node.getType()); + return true; + } + + @Override + public boolean visit(MethodDeclaration node) { + addReference(node.getReturnType2()); + @SuppressWarnings("unchecked") + List params = node.parameters(); + for (SingleVariableDeclaration param : params) { + addReference(param.getType()); + } + return true; + } + + @Override + public boolean visit(TypeDeclaration node) { + ITypeBinding binding = Types.getTypeBinding(node); + if (binding.isEqualTo(Types.getNSObject())) { + return false; + } + addSuperType(node.getSuperclassType()); + for (Iterator iterator = node.superInterfaceTypes().iterator(); iterator.hasNext();) { + Object o = iterator.next(); + if (o instanceof Type) { + addSuperType((Type) o); + } else { + throw new AssertionError("unknown AST type: " + o.getClass()); + } + } + return true; + } + }; + collector.run(type); + } + + // Do a topological sort on the types declared in this unit, with an edge + // in the graph denoting a type inheritance. Super types will end up + // higher up in the sort. + + types.clear(); + LinkedHashSet rootTypes = Sets.newLinkedHashSet(); + for (AbstractTypeDeclaration type : typesCopy) { + String name = Types.getTypeBinding(type).getBinaryName(); + if (!superTypes.containsValue(name)) { + rootTypes.add(type); + } + } + + while (!rootTypes.isEmpty()) { + AbstractTypeDeclaration type = + (AbstractTypeDeclaration) rootTypes.toArray()[rootTypes.size() - 1]; + rootTypes.remove(type); + types.add(0, type); + + ITypeBinding binding = Types.getTypeBinding(type); + String typeName = binding.getBinaryName(); + // Copy the values to avoid a ConcurrentModificationException. + List values = Lists.newArrayList(superTypes.get(typeName)); + for (String superTypeName : values) { + superTypes.remove(typeName, superTypeName); + if (!superTypes.containsValue(superTypeName)) { + AbstractTypeDeclaration superType = index.get(superTypeName); + rootTypes.add(superType); + } + } + } + + assert types.size() == typesCopy.size(); + + // For all other references, if the referred to type is declared + // after the reference, add a forward reference. + final Set moreForwardTypes = Sets.newHashSet(); + for (Map.Entry entry : references.entries()) { + AbstractTypeDeclaration referrer = index.get(entry.getKey()); + AbstractTypeDeclaration referred = index.get(entry.getValue()); + if (types.indexOf(referred) > types.indexOf(referrer)) { + // Referred to type occurs after the reference; add a forward decl + moreForwardTypes.add(Types.getTypeBinding(referred)); + } + } + + return moreForwardTypes; + } + + private void printInstanceVariables(FieldDeclaration[] fields) { + indent(); + String lastAccess = "@protected"; + for (FieldDeclaration field : fields) { + if ((field.getModifiers() & Modifier.STATIC) == 0) { + @SuppressWarnings("unchecked") + List vars = field.fragments(); // safe by definition + assert !vars.isEmpty(); + VariableDeclarationFragment var = vars.get(0); + if (var.getName().getIdentifier().startsWith("this$") && superDefinesVariable(var)) { + // Don't print, as it shadows an inner field in a super class. + continue; + } + String access = accessScope(field.getModifiers()); + if (!access.equals(lastAccess)) { + print(' '); + println(access); + lastAccess = access; + } + printIndent(); + if (Types.isWeakReference(Types.getVariableBinding(var)) && Options.useARC()) { + print("__weak "); + } + ITypeBinding varType = Types.getTypeBinding(vars.get(0)); + String objcType = NameTable.javaRefToObjC(varType); + boolean needsAsterisk = !varType.isPrimitive() && !objcType.matches("id|id<.*>|Class"); + if (needsAsterisk && objcType.endsWith(" *")) { + // Strip pointer from type, as it will be added when appending fragment. + // This is necessary to create "Foo *one, *two;" declarations. + objcType = objcType.substring(0, objcType.length() - 2); + } + print(objcType); + print(' '); + for (Iterator it = field.fragments().iterator(); it.hasNext(); ) { + VariableDeclarationFragment f = (VariableDeclarationFragment) it.next(); + if (needsAsterisk) { + print('*'); + } + String name = NameTable.getName(f.getName()); + print(NameTable.javaFieldToObjC(name)); + if (it.hasNext()) { + print(", "); + } + } + println(";"); + } + } + unindent(); + } + + private void printProperties(FieldDeclaration[] fields) { + int nPrinted = 0; + for (FieldDeclaration field : fields) { + if ((field.getModifiers() & Modifier.STATIC) == 0) { + ITypeBinding type = Types.getTypeBinding(field.getType()); + @SuppressWarnings("unchecked") + List vars = field.fragments(); // safe by definition + for (VariableDeclarationFragment var : vars) { + if (var.getName().getIdentifier().startsWith("this$") && superDefinesVariable(var)) { + // Don't print, as it shadows an inner field in a super class. + continue; + } + print("@property (nonatomic, "); + IVariableBinding varBinding = Types.getVariableBinding(var); + if (type.isPrimitive()) { + print("assign"); + } else if (Types.isWeakReference(varBinding) || + (varBinding.getName().startsWith("this$") && + Types.hasWeakAnnotation(varBinding.getDeclaringClass()))) { + print(Options.useARC() ? "weak" : "assign"); + } else if (type.isEqualTo(Types.getNSString())) { + print("copy"); + } else { + print(Options.useARC() ? "strong" : "retain"); + } + String typeString = NameTable.javaRefToObjC(type); + if (!typeString.endsWith("*")) { + typeString += " "; + } + println(String.format(") %s%s;", typeString, NameTable.getName(var.getName()))); + nPrinted++; + } + } + } + if (nPrinted > 0) { + newline(); + } + } + + private void printConstantDefines(AbstractTypeDeclaration node) { + ITypeBinding type = Types.getTypeBinding(node); + boolean hadConstant = false; + for (IVariableBinding field : type.getDeclaredFields()) { + if (Types.isPrimitiveConstant(field)) { + printf("#define %s ", NameTable.getPrimitiveConstantName(field)); + Object value = field.getConstantValue(); + assert value != null; + if (value instanceof Boolean) { + println(((Boolean) value).booleanValue() ? "TRUE" : "FALSE"); + } else if (value instanceof Character) { + char c = ((Character) value).charValue(); + String convertedChar = UnicodeUtils.escapeCharacter(c); + if (convertedChar != null) { + if (convertedChar.equals("'") || convertedChar.equals("\\")) { + printf("'\\%s'\n", convertedChar); + } else { + printf("'%s'\n", convertedChar); + } + } else { + // The Java char constant is likely not a valid C character; just + // print it as an int. + printf("0x%4x\n", c & 0xffff); + } + } else if (value instanceof Long) { + long l = ((Long) value).longValue(); + if (l == Long.MIN_VALUE) { + println("-0x7fffffffffffffffLL - 1"); + } else { + println(value.toString()); + } + } else if (value instanceof Integer) { + long l = ((Integer) value).intValue(); + if (l == Integer.MIN_VALUE) { + println("-0x7fffffff - 1"); + } else { + println(value.toString()); + } + } else if (value instanceof Float) { + float f = ((Float) value).floatValue(); + if (Float.isNaN(f)) { + println("NAN"); + } else if (f == Float.POSITIVE_INFINITY) { + println("INFINITY"); + } else if (f == Float.NEGATIVE_INFINITY) { + // FP representations are symmetrical. + println("-INFINITY"); + } else if (f == Float.MAX_VALUE) { + println("__FLT_MAX__"); + } else if (f == Float.MIN_VALUE) { + println("__FLT_MIN__"); + } else { + println(value.toString()); + } + } else if (value instanceof Double) { + double d = ((Double) value).doubleValue(); + if (Double.isNaN(d)) { + println("NAN"); + } else if (d == Double.POSITIVE_INFINITY) { + println("INFINITY"); + } else if (d == Double.NEGATIVE_INFINITY) { + // FP representations are symmetrical. + println("-INFINITY"); + } else if (d == Double.MAX_VALUE) { + println("__DBL_MAX__"); + } else if (d == Double.MIN_VALUE) { + println("__DBL_MIN__"); + } else { + println(value.toString()); + } + } else { + println(value.toString()); + } + hadConstant = true; + } + } + if (hadConstant) { + newline(); + } + } + + private String accessScope(int modifiers) { + if (Options.inlineFieldAccess()) { + // Need direct access to fields possibly from inner classes that are + // promoted to top level classes, so must make all fields public. + return "@public"; + } + + if ((modifiers & Modifier.PUBLIC) > 0) { + return "@public"; + } + if ((modifiers & Modifier.PROTECTED) > 0) { + return "@protected"; + } + if ((modifiers & Modifier.PRIVATE) > 0) { + return "@private"; + } + return "@package"; + } + } diff --git a/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppSourceFileGenerator.java b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppSourceFileGenerator.java new file mode 100644 index 0000000000..2f621448ea --- /dev/null +++ b/build-scripts/net.osmand.translator/test/src/com/google/devtools/j2cpp/gen/CppSourceFileGenerator.java @@ -0,0 +1,353 @@ +package com.google.devtools.j2cpp.gen; + +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; + +import com.google.common.collect.Lists; +import com.google.devtools.j2objc.gen.SourceFileGenerator; +import com.google.devtools.j2objc.types.IOSMethod; +import com.google.devtools.j2objc.types.IOSParameter; +import com.google.devtools.j2objc.types.Types; +import com.google.devtools.j2objc.util.NameTable; + +public abstract class CppSourceFileGenerator extends SourceFileGenerator { + + /** + * Create a new generator. + * + * @param sourceFileName the name of the source file being translated + * @param outputDirectory the top-level directory for output file(s) + */ + protected CppSourceFileGenerator(String sourceFileName, String source, + CompilationUnit unit, boolean emitLineDirectives) { + super(sourceFileName, source, unit, emitLineDirectives); + } + + /** + * Generate an output source file from the specified type declaration. + */ + public void generate(AbstractTypeDeclaration node) { + if (node instanceof TypeDeclaration) { + generate((TypeDeclaration) node); + } else if (node instanceof EnumDeclaration) { + generate((EnumDeclaration) node); + } else if (node instanceof AnnotationTypeDeclaration) { + generate((AnnotationTypeDeclaration) node); + } + } + + protected abstract void generate(TypeDeclaration node); + + protected abstract void generate(EnumDeclaration node); + + protected abstract void generate(AnnotationTypeDeclaration node); + + public void save(CompilationUnit node) { + save(getOutputFileName(node)); + } + + /** + * Print a list of methods. + */ + protected void printMethods(List methods) { + for (MethodDeclaration m : methods) { + syncLineNumbers(m.getName()); // avoid doc-comment + IMethodBinding binding = Types.getMethodBinding(m); + IOSMethod iosMethod = Types.getMappedMethod(binding); + if (iosMethod != null) { + print(mappedMethodDeclaration(m, iosMethod)); + } else if (m.isConstructor()) { + print(constructorDeclaration(m)); + } else if (Modifier.isStatic(m.getModifiers()) && + NameTable.CLINIT_NAME.equals(m.getName().getIdentifier())) { + printStaticConstructorDeclaration(m); + } else if (!isMainMethod(m) && !isInterfaceConstantAccessor(binding)) { + printMethod(m); + } + } + } + + /** + * Returns true if the specified method binding describes an accessor for + * an interface constant. + */ + protected boolean isInterfaceConstantAccessor(IMethodBinding binding) { + return binding.getDeclaringClass().isInterface() + && !Modifier.isAbstract(binding.getModifiers()); + } + + /** + * Returns a list of those methods that define accessors to interface + * constants. For most interfaces, the returned list will be empty. + */ + protected List findInterfaceConstantAccessors( + List methods) { + List results = Lists.newArrayList(); + for (MethodDeclaration m : methods) { + if (isInterfaceConstantAccessor(Types.getMethodBinding(m))) { + results.add(m); + } + } + return results; + } + + protected void printMethod(MethodDeclaration m) { + print(methodDeclaration(m)); + } + + /** + * Create an C++ method or constructor declaration string for an + * inlined method. + */ + protected String mappedMethodDeclaration(MethodDeclaration method, IOSMethod mappedMethod) { + StringBuffer sb = new StringBuffer(); + boolean isStatic = (method.getModifiers() & Modifier.STATIC) > 0; + + // Explicitly test hashCode() because of NSObject's hash return value. + String baseDeclaration; + if (mappedMethod.getName().equals("hash")) { + baseDeclaration = "- (NSUInteger)hash"; + } else { + baseDeclaration = String.format("%s (%s)%s", isStatic ? "static" : "", + NameTable.javaRefToCpp(method.getReturnType2()), mappedMethod.getName()); + } + + sb.append(baseDeclaration); + Iterator iosParameters = mappedMethod.getParameters().iterator(); + if (iosParameters.hasNext()) { + @SuppressWarnings("unchecked") + List parameters = method.parameters(); + IOSParameter first = iosParameters.next(); + SingleVariableDeclaration var = parameters.get(first.getIndex()); + addTypeAndName(first, var, sb); + if (iosParameters.hasNext()) { + sb.append(mappedMethod.isVarArgs() ? ", " : " "); + IOSParameter next = iosParameters.next(); + sb.append(next.getParameterName()); + var = parameters.get(next.getIndex()); + addTypeAndName(next, var, sb); + } + } + return sb.toString(); + } + + private void addTypeAndName(IOSParameter iosParameter, SingleVariableDeclaration var, + StringBuffer sb) { + sb.append(":("); + sb.append(iosParameter.getType()); + sb.append(')'); + sb.append(var.getName().getIdentifier()); + } + + /** + * Create an C++ method declaration string. + */ + protected String methodDeclaration(MethodDeclaration m) { + assert !m.isConstructor(); + StringBuffer sb = new StringBuffer(); + boolean isStatic = Modifier.isStatic(m.getModifiers()); + IMethodBinding binding = Types.getMethodBinding(m); + String methodName = NameTable.getName(binding); + String baseDeclaration = String.format("\t %s %s %s", isStatic ? "static " : "", NameTable.javaRefToCpp(m.getReturnType2()), methodName); + sb.append(baseDeclaration); + @SuppressWarnings("unchecked") + List params = m.parameters(); // safe by definition + parametersDeclaration(Types.getOriginalMethodBinding(binding), params, baseDeclaration, sb); + return sb.toString(); + } + + /** + * Create an C++ constructor declaration string. + */ + protected String constructorDeclaration(MethodDeclaration m) { + assert m.isConstructor(); + StringBuffer sb = new StringBuffer(); + String baseDeclaration = "- (id)init"; + sb.append(baseDeclaration); + @SuppressWarnings("unchecked") + List params = m.parameters(); // safe by definition + parametersDeclaration(Types.getMethodBinding(m), params, baseDeclaration, sb); + return sb.toString(); + } + + /** + * Print an C++ constructor declaration string. + */ + protected abstract void printStaticConstructorDeclaration(MethodDeclaration m); + + private void parametersDeclaration(IMethodBinding method, List params, + String baseDeclaration, StringBuffer sb) throws AssertionError { + + if (!params.isEmpty()) { + ITypeBinding[] parameterTypes = method.getParameterTypes(); + int nParams = params.size(); + sb.append(" ("); + for (int i = 0; i < nParams; i++) { + SingleVariableDeclaration param = params.get(i); + String fieldName = getParameterName(param); + ITypeBinding typeBinding = parameterTypes[i]; + boolean isTypeVariable = typeBinding.isTypeVariable(); + String type = isTypeVariable ? + NameTable.getParameterTypeName(NameTable.ID_TYPE, typeBinding) : + NameTable.getParameterTypeName(NameTable.javaTypeToCpp(param.getType(), true), typeBinding); + sb.append(" ").append(type).append(" ").append(fieldName); + if (i args = m.parameters(); + if (args.size() == 1) { + SingleVariableDeclaration var = (SingleVariableDeclaration) args.get(0); + + // Use original binding, since we can't tell if it's a String + // array after translation, since IOSObjectArray just holds objects. + ITypeBinding type = var.resolveBinding().getType(); + ITypeBinding stringType = m.getAST().resolveWellKnownType("java.lang.String"); + return type.isArray() && type.getComponentType().isEqualTo(stringType); + } + } + } + return false; + } + + /** + * Returns a function declaration string from a specified class and method. + */ + protected String makeFunctionDeclaration(AbstractTypeDeclaration cls, + MethodDeclaration method) { + StringBuffer sb = new StringBuffer(); + Type returnType = method.getReturnType2(); + ITypeBinding binding = Types.getTypeBinding(returnType); + if (binding.isEnum()) { + sb.append(NameTable.javaTypeToCpp(returnType, true)); + } else { + sb.append(NameTable.javaRefToCpp(returnType)); + } + sb.append(' '); + sb.append(NameTable.makeFunctionName(cls, method)); + sb.append('('); + for (Iterator iterator = method.parameters().iterator(); iterator.hasNext(); ) { + Object o = iterator.next(); + if (o instanceof SingleVariableDeclaration) { + SingleVariableDeclaration param = (SingleVariableDeclaration) o; + String fieldType = NameTable.javaRefToCpp(param.getType()); + String fieldName = param.getName().getIdentifier(); + sb.append(String.format("%s %s", fieldType, fieldName)); + if (iterator.hasNext()) { + sb.append(", "); + } + } + } + sb.append(')'); + return sb.toString(); + } + + /** + * Returns true if a superclass also defines this variable. + */ + protected boolean superDefinesVariable(VariableDeclarationFragment var) { + IVariableBinding varBinding = Types.getVariableBinding(var); + ITypeBinding declaringClassBinding = varBinding.getDeclaringClass(); + TypeDeclaration declaringClass = + Types.getTypeDeclaration(declaringClassBinding, getUnit().types()); + if (declaringClass == null) { + return false; + } + String name = var.getName().getIdentifier(); + ITypeBinding type = varBinding.getType(); + return superDefinesVariable(declaringClass, name, type); + } + + private boolean superDefinesVariable(TypeDeclaration declaringClass, String name, + ITypeBinding type) { + ITypeBinding superClazzBinding = Types.getTypeBinding(declaringClass.getSuperclassType()); + TypeDeclaration superClazz = Types.getTypeDeclaration(superClazzBinding, getUnit().types()); + if (superClazz == null) { + return false; + } + for (FieldDeclaration field : superClazz.getFields()) { + @SuppressWarnings("unchecked") + List vars = field.fragments(); // safe by definition + for (VariableDeclarationFragment var : vars) { + if (var.getName().getIdentifier().equals(name)) { + ITypeBinding varType = Types.getTypeBinding(var); + if (varType.isEqualTo(type)) { + return true; + } + } + } + } + return superDefinesVariable(superClazz, name, type); + } + }