// We generate the runtime debug method -memDebugStrongReferences. // This method will return an array of information about a strong reference, // including pointer to object and name. private void printStrongReferencesMethod(List<VariableDeclarationFragment> properties) { if (Options.memoryDebug()) { if (!Options.useReferenceCounting()) { println("- (NSArray *)memDebugStrongReferences {"); println(" return nil;"); println("}"); return; } println("- (NSArray *)memDebugStrongReferences {"); println(" NSMutableArray *result ="); println(" [[[super memDebugStrongReferences] mutableCopy] autorelease];"); for (VariableDeclarationFragment property : properties) { String propName = NameTable.getName(property.getName()); String objCFieldName = NameTable.javaFieldToObjC(propName); if (isStrongReferenceProperty(property)) { println( String.format( " [result addObject:[JreMemDebugStrongReference " + "strongReferenceWithObject:%s name:@\"%s\"]];", objCFieldName, propName)); } } println(" return result;"); println("}\n"); } }
private void printAnnotationValue(AST ast, Object value) { if (value == null) { print("nil"); } else if (value instanceof IVariableBinding) { IVariableBinding var = (IVariableBinding) value; ITypeBinding declaringClass = var.getDeclaringClass(); printf("[%s %s]", NameTable.getFullName(declaringClass), var.getName()); } else if (value instanceof ITypeBinding) { ITypeBinding type = (ITypeBinding) value; printf("[[%s class] getClass]", NameTable.getFullName(type)); } else if (value instanceof String) { StringLiteral node = ast.newStringLiteral(); node.setLiteralValue((String) value); print(StatementGenerator.generateStringLiteral(node)); } else if (value instanceof Number || value instanceof Character || value instanceof Boolean) { print(value.toString()); } else if (value.getClass().isArray()) { print("[IOSObjectArray arrayWithObjects:(id[]) { "); Object[] array = (Object[]) value; for (int i = 0; i < array.length; i++) { if (i > 0) { print(", "); } printAnnotationValue(ast, array[i]); } printf(" } count:%d type:[[NSObject class] getClass]]", array.length); } else { assert false : "unknown annotation value type"; } }
@Override protected void printStaticFieldGetter(IVariableBinding var) { String name = BindingUtil.isPrimitiveConstant(var) ? NameTable.getPrimitiveConstantName(var) : NameTable.getStaticVarQualifiedName(var); printf("%s {\n return %s;\n}\n\n", staticFieldGetterSignature(var), name); }
private String methodKey(IMethodBinding method) { StringBuilder sb = new StringBuilder(); if (method.isConstructor()) { sb.append(NameTable.getFullName(method.getDeclaringClass())); } else { sb.append(NameTable.getName(method)); } sb.append(parameterKey(method)); return sb.toString(); }
private static String getSuperFunctionSignature(IMethodBinding method) { StringBuilder signature = new StringBuilder(NameTable.getPrimitiveObjCType(method.getReturnType())); signature.append(" (*%s)(id, SEL"); for (ITypeBinding paramType : method.getParameterTypes()) { signature.append(", ").append(NameTable.getPrimitiveObjCType(paramType)); } signature.append(")"); return signature.toString(); }
public void testCamelCaseQualifiedName() { String camelCaseName = NameTable.camelCaseQualifiedName("java.lang.Object"); assertEquals("JavaLangObject", camelCaseName); camelCaseName = NameTable.camelCaseQualifiedName("java.util.logging.Level"); assertEquals("JavaUtilLoggingLevel", camelCaseName); camelCaseName = NameTable.camelCaseQualifiedName("java"); assertEquals("Java", camelCaseName); camelCaseName = NameTable.camelCaseQualifiedName("Level"); assertEquals("Level", camelCaseName); camelCaseName = NameTable.camelCaseQualifiedName(""); assertEquals("", camelCaseName); }
@Override protected void printStaticFieldSetter(IVariableBinding var) { String fieldName = NameTable.getStaticVarQualifiedName(var); String paramName = NameTable.getName(var); String signature = staticFieldSetterSignature(var); if (Options.useReferenceCounting()) { printf( "%s {\n JreOperatorRetainedAssign(&%s, nil, %s);\n}\n\n", signature, fieldName, paramName); } else { printf("%s {\n %s = %s;\n}\n\n", signature, fieldName, paramName); } }
private String parameterKey(IMethodBinding method) { StringBuilder sb = new StringBuilder(); ITypeBinding[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (i == 0) { sb.append(NameTable.capitalize(NameTable.parameterKeyword(parameterTypes[i]))); } else { sb.append(NameTable.parameterKeyword(parameterTypes[i])); } sb.append('_'); } return sb.toString(); }
public void testMethodVarInSwitch() throws IOException { String source = "class Test { " + " enum E { ONE, TWO };" + " void foo(E e) { " + " switch (e) {" + " case ONE: {" + " final Integer i = 1;" + " Runnable r = new Runnable() { " + " public void run() { int j = i + 1; } }; }}}}"; // Verify method var in r1.run() isn't mistakenly made a field in r1. CompilationUnit unit = translateType("Test", source); List<TypeDeclaration> types = unit.types(); TypeDeclaration r1 = types.get(2); assertEquals("Test_$1", NameTable.getFullName(r1)); boolean found = false; for (FieldDeclaration field : r1.getFields()) { List<VariableDeclarationFragment> vars = field.fragments(); for (VariableDeclaration var : vars) { if (var.getName().getIdentifier().equals("val$i")) { found = true; } } } assertTrue("required field not found", found); // Verify constructor takes both outer field and var. ObjectiveCImplementationGenerator.generate("Test.java", Language.OBJECTIVE_C, unit, source); String translation = getTranslatedFile("Test.m"); assertTranslation( translation, "r = [[[Test_$1 alloc] " + "initWithTest:self withJavaLangInteger:i] autorelease]"); }
@Override protected void generate(AnnotationTypeDeclaration node) { syncLineNumbers(node.getName()); // avoid doc-comment String typeName = NameTable.getFullName(node); printf("@implementation %s\n", typeName); if (BindingUtil.isRuntimeAnnotation(Types.getTypeBinding(node))) { List<AnnotationTypeMemberDeclaration> members = Lists.newArrayList(); for (BodyDeclaration decl : ASTUtil.getBodyDeclarations(node)) { if (decl instanceof AnnotationTypeMemberDeclaration) { members.add((AnnotationTypeMemberDeclaration) decl); } } printAnnotationProperties(members); if (!members.isEmpty()) { printAnnotationConstructor(Types.getTypeBinding(node)); } printAnnotationAccessors(members); } List<FieldDeclaration> fields = ASTUtil.getFieldDeclarations(node); List<IVariableBinding> fieldsNeedingAccessors = getStaticFieldsNeedingAccessors(fields, /* isInterface */ true); printStaticVars(fields, /* isInterface */ true); printStaticFieldAccessors(fieldsNeedingAccessors, Collections.<MethodDeclaration>emptyList()); println("- (IOSClass *)annotationType {"); printf(" return [IOSClass classWithProtocol:@protocol(%s)];\n", typeName); println("}\n"); if (!Options.stripReflection()) { printTypeAnnotationsMethod(node); printMetadata(node); } println("@end\n"); }
@Override public void generate(TypeDeclaration node) { syncLineNumbers(node.getName()); // avoid doc-comment String typeName = NameTable.getFullName(node); List<FieldDeclaration> fields = Lists.newArrayList(node.getFields()); List<MethodDeclaration> methods = Lists.newArrayList(node.getMethods()); fieldHiders = HiddenFieldDetector.getFieldNameConflicts(node); if (node.isInterface()) { printStaticInterface(node, typeName, fields, methods); } else { printf("@implementation %s\n\n", typeName); printStaticReferencesMethod(fields); printStaticVars(fields, /* isInterface */ false); printStaticFieldAccessors(fields, methods, /* isInterface */ false); printMethods(node); if (!Options.stripReflection()) { printTypeAnnotationsMethod(node); printMethodAnnotationMethods(Lists.newArrayList(node.getMethods())); printFieldAnnotationMethods(Lists.newArrayList(node.getFields())); printMetadata(node); } println("@end"); } }
private void printAnnotationAccessors(List<AnnotationTypeMemberDeclaration> members) { int nPrinted = 0; for (AnnotationTypeMemberDeclaration member : members) { Expression deflt = member.getDefault(); if (deflt != null) { ITypeBinding type = Types.getTypeBinding(member.getType()); String typeString = NameTable.getSpecificObjCType(type); String propertyName = NameTable.getName(member.getName()); printf("+ (%s)%sDefault {\n", typeString, propertyName); printf(" return %s;\n", generateExpression(deflt)); println("}\n"); nPrinted++; } } if (nPrinted > 0) { newline(); } }
private void printAnnotationProperties(List<AnnotationTypeMemberDeclaration> members) { int nPrinted = 0; for (AnnotationTypeMemberDeclaration member : members) { println(String.format("@synthesize %s;", NameTable.getName(member.getName()))); nPrinted++; } if (nPrinted > 0) { newline(); } }
@Override protected void printStaticConstructorDeclaration(MethodDeclaration m) { String className = NameTable.getFullName(Types.getMethodBinding(m).getDeclaringClass()); StringBuffer sb = new StringBuffer(); sb.append("{\nif (self == [" + className + " class]) {\n"); for (Statement statement : ASTUtil.getStatements(m.getBody())) { sb.append(generateStatement(statement, false)); } sb.append("}\n}"); print("+ (void)initialize " + reindent(sb.toString()) + "\n\n"); }
private String getObjCMethodSignature(IMethodBinding method) { StringBuilder sb = new StringBuilder(method.getName()); boolean first = true; for (ITypeBinding paramType : method.getParameterTypes()) { String keyword = nameTable.parameterKeyword(paramType); if (first) { first = false; keyword = NameTable.capitalize(keyword); } sb.append(keyword + ":"); } return sb.toString(); }
// Prints an annotation's values as a constructor argument list. If // the annotation type declares default values, then for any value that // isn't specified in the annotation will use the default. private void printAnnotationParameters(Annotation annotation) { IAnnotationBinding binding = Types.getAnnotationBinding(annotation); IMemberValuePairBinding[] valueBindings = BindingUtil.getSortedMemberValuePairs(binding); for (int i = 0; i < valueBindings.length; i++) { if (i > 0) { print(' '); } IMemberValuePairBinding valueBinding = valueBindings[i]; print(i == 0 ? "With" : "with"); printf("%s:", NameTable.capitalize(valueBinding.getName())); Object value = valueBinding.getValue(); printAnnotationValue(annotation.getAST(), value); } }
private void printStaticVars(List<FieldDeclaration> fields, boolean isInterface) { boolean hadStaticVar = false; for (FieldDeclaration f : fields) { if (Modifier.isStatic(f.getModifiers()) || isInterface) { for (VariableDeclarationFragment var : ASTUtil.getFragments(f)) { IVariableBinding binding = Types.getVariableBinding(var); if (!BindingUtil.isPrimitiveConstant(binding)) { String name = NameTable.getStaticVarQualifiedName(binding); String objcType = NameTable.getObjCType(binding.getType()); Expression initializer = var.getInitializer(); if (initializer != null) { printf("static %s %s = %s;\n", objcType, name, generateExpression(initializer)); } else { printf("static %s %s;\n", objcType, name); } hadStaticVar = true; } } } } if (hadStaticVar) { newline(); } }
private void printAnnotations(List<Annotation> runtimeAnnotations) { boolean first = true; for (Annotation annotation : runtimeAnnotations) { if (first) { first = false; } else { print(", "); } if (Options.useReferenceCounting()) { print('['); } printf("[[%s alloc] init", NameTable.getFullName(Types.getTypeBinding(annotation))); printAnnotationParameters(annotation); print(']'); if (Options.useReferenceCounting()) { print(" autorelease]"); } } }
public void generate(CompilationUnit unit) { println(J2ObjC.getFileHeader(getSourceFileName())); List<AbstractTypeDeclaration> typesToGenerate = collectTypes(unit); if (!typesToGenerate.isEmpty()) { findBlockComments(unit, typesToGenerate); findInvokedConstructors(unit); printStart(getSourceFileName()); printImports(unit); pushIgnoreDeprecatedDeclarationsPragma(); for (AbstractTypeDeclaration type : typesToGenerate) { generate(type); } popIgnoreDeprecatedDeclarationsPragma(); } else { // Print a dummy C function so compiled object file is valid. List<AbstractTypeDeclaration> types = ASTUtil.getTypes(unit); if (!types.isEmpty()) { printf("void %s_unused() {}\n", NameTable.getFullName(types.get(0))); } } save(unit); }
// We generate the runtime debug method +memDebugStaticReferences. // This method will return an array of NSNumber containing pointers (casted into unsigned long) // to the objects referenced by a class variable with a strong reference. // It will be useful for debug purpose. // // Arrays returned by -memDebugStaticReferences and -memDebugStaticReferencesNames (see below) // must be the same size. // // In case of a Java enum, valuesVarNameis the name of the array of enum values. private void printStaticReferencesMethod(List<FieldDeclaration> fields, String valuesVarName) { if (Options.memoryDebug()) { if (!Options.useReferenceCounting()) { println("+ (NSArray *)memDebugStaticReferences {"); println(" return nil;"); println("}"); return; } println("+ (NSArray *)memDebugStaticReferences {"); println(" NSMutableArray *result = [NSMutableArray array];"); for (FieldDeclaration f : fields) { if (Modifier.isStatic(f.getModifiers())) { for (VariableDeclarationFragment var : ASTUtil.getFragments(f)) { IVariableBinding binding = Types.getVariableBinding(var); // All non-primitive static variables are strong references. if (!binding.getType().isPrimitive()) { String name = NameTable.getStaticVarQualifiedName(binding); println( String.format( " [result addObject:[JreMemDebugStrongReference " + "strongReferenceWithObject:%s name:@\"%s\"]];", name, name)); } } } } if (valuesVarName != null) { println( String.format( " [result addObject:[JreMemDebugStrongReference " + "strongReferenceWithObject:%s name:@\"enumValues\"]];", valuesVarName)); } println(" return result;"); println("}\n"); } }
/** * Class for processing GenerationUnits in minimum increments of one GenerationUnit. * * @author Tom Ball, Keith Stanger, Mike Thvedt */ abstract class FileProcessor { private static final Logger logger = Logger.getLogger(FileProcessor.class.getName()); private final JdtParser parser; protected final BuildClosureQueue closureQueue; private final NameTable.Factory nameTableFactory = NameTable.newFactory(); private final int batchSize = Options.batchTranslateMaximum(); private final Set<ProcessingContext> batchInputs = Sets.newLinkedHashSetWithExpectedSize(batchSize); private final boolean doBatching = batchSize > 0; public FileProcessor(JdtParser parser) { this.parser = Preconditions.checkNotNull(parser); if (Options.buildClosure()) { // Should be an error if the user specifies this with --build-closure assert !Options.shouldMapHeaders(); closureQueue = new BuildClosureQueue(); } else { closureQueue = null; } } public void processInputs(Iterable<ProcessingContext> inputs) { for (ProcessingContext input : inputs) { processInput(input); } processBatch(); } public void processBuildClosureDependencies() { if (closureQueue != null) { while (true) { InputFile file = closureQueue.getNextFile(); if (file == null) { processBatch(); file = closureQueue.getNextFile(); } if (file == null) { break; } processInput(ProcessingContext.fromFile(file)); } } } private void processInput(ProcessingContext input) { InputFile file = input.getFile(); if (isBatchable(file)) { batchInputs.add(input); if (batchInputs.size() == batchSize) { processBatch(); } return; } logger.finest("parsing " + file); String source = null; CompilationUnit compilationUnit = null; try { source = FileUtil.readFile(file); compilationUnit = parser.parseWithBindings(file.getUnitName(), source); } catch (IOException e) { ErrorUtil.error(e.getMessage()); } if (compilationUnit == null) { handleError(input); return; } processCompiledSource(input, source, compilationUnit); } protected boolean isBatchable(InputFile file) { return doBatching && file.getContainingPath().endsWith(".java"); } private void processBatch() { if (batchInputs.isEmpty()) { return; } List<String> paths = Lists.newArrayListWithCapacity(batchInputs.size()); final Map<String, ProcessingContext> inputMap = Maps.newHashMapWithExpectedSize(batchInputs.size()); for (ProcessingContext input : batchInputs) { String path = input.getFile().getPath(); paths.add(path); inputMap.put(path, input); } JdtParser.Handler handler = new JdtParser.Handler() { @Override public void handleParsedUnit(String path, CompilationUnit unit) { ProcessingContext input = inputMap.get(path); try { String source = FileUtil.readFile(input.getFile()); processCompiledSource(input, source, unit); batchInputs.remove(input); } catch (IOException e) { ErrorUtil.error(e.getMessage()); } } }; logger.finest("Processing batch of size " + batchInputs.size()); parser.parseFiles(paths, handler); // Any remaining files in batchFiles has some kind of error. for (ProcessingContext input : batchInputs) { handleError(input); } batchInputs.clear(); } private void processCompiledSource(ProcessingContext input, String source, CompilationUnit unit) { InputFile file = input.getFile(); if (closureQueue != null) { closureQueue.addProcessedName(FileUtil.getQualifiedMainTypeName(file, unit)); } com.google.devtools.j2objc.ast.CompilationUnit convertedUnit = TreeConverter.convertCompilationUnit( unit, input.getOriginalSourcePath(), FileUtil.getMainTypeName(file), source, nameTableFactory); processConvertedTree(input, convertedUnit); } protected abstract void processConvertedTree( ProcessingContext input, com.google.devtools.j2objc.ast.CompilationUnit unit); protected abstract void handleError(ProcessingContext input); }
public void testCapitalize() { assertEquals("Test", NameTable.capitalize("test")); assertEquals("123", NameTable.capitalize("123")); assertEquals("", NameTable.capitalize("")); }
private String generateNativeStub(MethodDeclaration m) { IMethodBinding binding = Types.getMethodBinding(m); String methodName = NameTable.getName(binding); return String.format("{\n @throw \"%s method not implemented\";\n}", methodName); }
@Override protected void generate(EnumDeclaration node) { List<EnumConstantDeclaration> constants = ASTUtil.getEnumConstants(node); List<MethodDeclaration> methods = Lists.newArrayList(); List<FieldDeclaration> fields = Lists.newArrayList(); MethodDeclaration initializeMethod = null; for (BodyDeclaration decl : ASTUtil.getBodyDeclarations(node)) { if (decl instanceof FieldDeclaration) { fields.add((FieldDeclaration) decl); } else if (decl instanceof MethodDeclaration) { MethodDeclaration md = (MethodDeclaration) decl; if (md.getName().getIdentifier().equals("initialize")) { initializeMethod = md; } else { methods.add(md); } } } syncLineNumbers(node.getName()); // avoid doc-comment String typeName = NameTable.getFullName(node); newline(); for (EnumConstantDeclaration constant : constants) { IVariableBinding var = Types.getVariableBinding(constant.getName()); printf("static %s *%s;\n", typeName, NameTable.getStaticVarQualifiedName(var)); } printf("IOSObjectArray *%s_values;\n", typeName); newline(); printf("@implementation %s\n\n", typeName); printStaticVars(fields, /* isInterface */ false); printStaticReferencesMethod(fields, typeName + "_values"); for (EnumConstantDeclaration constant : constants) { String name = NameTable.getName(constant.getName()); printf("+ (%s *)%s {\n", typeName, name); printf(" return %s_%s;\n", typeName, name); println("}"); } newline(); // Enum constants needs to implement NSCopying. Being singletons, they // can just return self, as long the retain count is incremented. String selfString = Options.useReferenceCounting() ? "[self retain]" : "self"; printf("- (id)copyWithZone:(NSZone *)zone {\n return %s;\n}\n\n", selfString); printStaticFieldAccessors(fields, methods, /* isInterface */ false); printMethodsAndOcni(node, methods, blockComments.get(node)); printf("+ (void)initialize {\n if (self == [%s class]) {\n", typeName); for (int i = 0; i < constants.size(); i++) { EnumConstantDeclaration constant = constants.get(i); List<Expression> args = ASTUtil.getArguments(constant); String name = NameTable.getName(constant.getName()); String constantTypeName = NameTable.getFullName(Types.getMethodBinding(constant).getDeclaringClass()); printf(" %s_%s = [[%s alloc] init", typeName, name, constantTypeName); if (args.isEmpty()) { print("With"); } else { print( StatementGenerator.generateArguments( Types.getMethodBinding(constant), args, fieldHiders, getBuilder().getSourcePosition())); print(" with"); } printf("NSString:@\"%s\" withInt:%d];\n", name, i); } printf(" %s_values = [[IOSObjectArray alloc] initWithObjects:(id[]){ ", typeName); for (EnumConstantDeclaration constant : constants) { printf("%s_%s, ", typeName, NameTable.getName(constant.getName())); } printf( "nil } count:%d type:[IOSClass classWithClass:[%s class]]];\n", constants.size(), typeName); if (initializeMethod != null) { for (Statement s : ASTUtil.getStatements(initializeMethod.getBody())) { printf( " %s", StatementGenerator.generate(s, fieldHiders, false, getBuilder().getSourcePosition())); } } println(" }\n}\n"); // Print generated values and valueOf methods. println("+ (IOSObjectArray *)values {"); printf(" return [IOSObjectArray arrayWithArray:%s_values];\n", typeName); println("}\n"); printf("+ (%s *)valueOfWithNSString:(NSString *)name {\n", typeName); printf(" for (int i = 0; i < [%s_values count]; i++) {\n", typeName); printf(" %s *e = %s_values->buffer_[i];\n", typeName, typeName); printf(" if ([name isEqual:[e name]]) {\n"); printf(" return e;\n"); printf(" }\n"); printf(" }\n"); if (Options.useReferenceCounting()) { printf( " @throw [[[JavaLangIllegalArgumentException alloc] initWithNSString:name]" + " autorelease];\n"); } else { printf(" @throw [[JavaLangIllegalArgumentException alloc] initWithNSString:name];\n"); } printf(" return nil;\n"); println("}\n"); if (!Options.stripReflection()) { printTypeAnnotationsMethod(node); printMetadata(node); } println("@end"); }
@Override protected void printStaticFieldReferenceGetter(IVariableBinding var) { printf( "%s {\n return &%s;\n}\n\n", staticFieldReferenceGetterSignature(var), NameTable.getStaticVarQualifiedName(var)); }