Cpp header file generation
This commit is contained in:
parent
63308d89af
commit
c575ae7ccb
3 changed files with 962 additions and 8 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AbstractTypeDeclaration> types = unit.types(); // safe by definition
|
||||
Set<ITypeBinding> 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<MethodDeclaration> publicMethods = new ArrayList<MethodDeclaration>();
|
||||
List<MethodDeclaration> protectedMethods = new ArrayList<MethodDeclaration>();
|
||||
List<MethodDeclaration> privateMethods = new ArrayList<MethodDeclaration>();
|
||||
|
||||
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<EnumConstantDeclaration> 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<FieldDeclaration> fields = Lists.newArrayList();
|
||||
List<MethodDeclaration> 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<ITypeBinding> forwards) {
|
||||
HeaderImportCollector collector = new HeaderImportCollector();
|
||||
collector.collect(unit, getSourceFileName());
|
||||
Set<ImportCollector.Import> imports = collector.getImports();
|
||||
Set<ImportCollector.Import> superTypes = collector.getSuperTypes();
|
||||
|
||||
// Print forward declarations.
|
||||
Set<String> 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<Comment> 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<String> 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<ITypeBinding> sortTypes(List<AbstractTypeDeclaration> types) {
|
||||
final List<AbstractTypeDeclaration> typesCopy =
|
||||
new ArrayList<AbstractTypeDeclaration>(types);
|
||||
|
||||
final Map<String, AbstractTypeDeclaration> index = Maps.newHashMap();
|
||||
for (AbstractTypeDeclaration type : typesCopy) {
|
||||
index.put(Types.getTypeBinding(type).getBinaryName(), type);
|
||||
}
|
||||
|
||||
final Multimap<String, String> references = HashMultimap.create();
|
||||
final Multimap<String, String> 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<SingleVariableDeclaration> 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<AbstractTypeDeclaration> 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<String> 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<ITypeBinding> moreForwardTypes = Sets.newHashSet();
|
||||
for (Map.Entry<String, String> 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<VariableDeclarationFragment> 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<VariableDeclarationFragment> 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";
|
||||
}
|
||||
}
|
|
@ -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<MethodDeclaration> 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<MethodDeclaration> findInterfaceConstantAccessors(
|
||||
List<MethodDeclaration> methods) {
|
||||
List<MethodDeclaration> 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<IOSParameter> iosParameters = mappedMethod.getParameters().iterator();
|
||||
if (iosParameters.hasNext()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SingleVariableDeclaration> 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<SingleVariableDeclaration> 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<SingleVariableDeclaration> 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<SingleVariableDeclaration> 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<nParams-1) {
|
||||
sb.append(",");
|
||||
} else {
|
||||
sb.append(" )");
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
// if (method.isConstructor() && method.getDeclaringClass().isEnum()) {
|
||||
// // If enum constant type, append name and ordinal.
|
||||
// if (params.isEmpty()) {
|
||||
// sb.append("WithNSString:(NSString *)name withInt:(int)ordinal");
|
||||
// } else {
|
||||
// sb.append('\n');
|
||||
// String keyword = "withNSString";
|
||||
// sb.append(pad(baseDeclaration.length() - keyword.length()));
|
||||
// sb.append(keyword);
|
||||
// sb.append(":(NSString *)name\n");
|
||||
// keyword = "withInt";
|
||||
// sb.append(pad(baseDeclaration.length() - keyword.length()));
|
||||
// sb.append(keyword);
|
||||
// sb.append(":(int)ordinal");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
protected String getParameterName(SingleVariableDeclaration param) {
|
||||
String name = NameTable.getName(param.getName());
|
||||
if (NameTable.isReservedName(name)) {
|
||||
name += "Arg";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private String parameterKeyword(Type type, ITypeBinding typeBinding) {
|
||||
String typeName = NameTable.javaTypeToCpp(type, true);
|
||||
return parameterKeyword(typeName, typeBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a parameter name, which consists of a prefix ("with") and
|
||||
* a type name that doesn't conflict with core names. For example,
|
||||
* "Foo" returns "withFoo", "Long" returns "withLong", and "long"
|
||||
* returns "withLongInt", so as not to conflict with the previous
|
||||
* example.
|
||||
*
|
||||
* For array types, the name returned is the type of the array's
|
||||
* element followed by "Array".
|
||||
*/
|
||||
public static String parameterKeyword(String typeName, ITypeBinding typeBinding) {
|
||||
return "with" + NameTable.capitalize(NameTable.getParameterTypeName(typeName, typeBinding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified method declaration is for a Java main
|
||||
*/
|
||||
protected boolean isMainMethod(MethodDeclaration m) {
|
||||
int modifiers = m.getModifiers();
|
||||
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
|
||||
if (m.getName().getIdentifier().equals("main")) {
|
||||
List<?> 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<VariableDeclarationFragment> 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue