Skip to content

Commit

Permalink
Generate package mappings.
Browse files Browse the repository at this point in the history
	Change on 2018/07/18 by antoniocortes <antoniocortes@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=205136794
  • Loading branch information
antonio-cortes-perez authored and Tom Ball committed Jul 31, 2018
1 parent 898cf3d commit d08db63
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 68 deletions.
39 changes: 22 additions & 17 deletions jre_emul/Classes/IOSClass.m
Expand Up @@ -518,30 +518,30 @@ + (IOSClass *)classForIosName:(NSString *)iosName {
} }


// The __j2objc_aliases custom data segment is built by the linker (along with these start // The __j2objc_aliases custom data segment is built by the linker (along with these start
// and end section symbols) from structures defined by the J2OBJC_CLASS_NAME_MAPPING macro. // and end section symbols) from structures defined by the J2OBJC_NAME_MAPPING macro.
// This data defines mapping for Java class names to the actual iOS names, and so is only // This data defines mapping for Java names to the actual iOS names, and so is only
// necessary when loading classes by name. // necessary when loading classes by name.
static NSDictionary *FetchClassMappings() { static NSDictionary *FetchNameMappings() {
extern J2ObjcClassNameMapping start_alias_section __asm("section$start$__DATA$__j2objc_aliases"); extern J2ObjcNameMapping start_alias_section __asm("section$start$__DATA$__j2objc_aliases");
extern J2ObjcClassNameMapping end_alias_section __asm("section$end$__DATA$__j2objc_aliases"); extern J2ObjcNameMapping end_alias_section __asm("section$end$__DATA$__j2objc_aliases");
NSUInteger nMappings = (NSUInteger)(&end_alias_section - &start_alias_section); NSUInteger nMappings = (NSUInteger)(&end_alias_section - &start_alias_section);
NSMutableDictionary *mappedClasses = [[NSMutableDictionary alloc] initWithCapacity:nMappings]; NSMutableDictionary *mappedNames = [[NSMutableDictionary alloc] initWithCapacity:nMappings];
for (long i = 0; i < nMappings; i++) { for (long i = 0; i < nMappings; i++) {
J2ObjcClassNameMapping* mapping = (&start_alias_section) + i; J2ObjcNameMapping* mapping = (&start_alias_section) + i;
[mappedClasses setObject:@(mapping->ios_name) forKey:@(mapping->java_name)]; [mappedNames setObject:@(mapping->ios_name) forKey:@(mapping->java_name)];
} }
return mappedClasses; return mappedNames;
} }


static IOSClass *ClassForJavaName(NSString *name) { static IOSClass *ClassForJavaName(NSString *name) {
static NSDictionary *mappedClasses; static NSDictionary *mappedNames;
static dispatch_once_t once; static dispatch_once_t once;
dispatch_once(&once, ^{ dispatch_once(&once, ^{
mappedClasses = FetchClassMappings(); mappedNames = FetchNameMappings();
}); });


// First check if this is a mapped name. // First check if this is a mapped name.
NSString *mappedName = [mappedClasses objectForKey:name]; NSString *mappedName = [mappedNames objectForKey:name];
if (mappedName) { if (mappedName) {
return ClassForIosName(mappedName); return ClassForIosName(mappedName);
} }
Expand All @@ -555,7 +555,7 @@ + (IOSClass *)classForIosName:(NSString *)iosName {
break; break;
} }
NSString *prefix = [name substringToIndex:lastDollar]; NSString *prefix = [name substringToIndex:lastDollar];
NSString *mappedName = [mappedClasses objectForKey:prefix]; NSString *mappedName = [mappedNames objectForKey:prefix];
if (mappedName) { if (mappedName) {
NSString *suffix = JavaToIosName([name substringFromIndex:lastDollar]); NSString *suffix = JavaToIosName([name substringFromIndex:lastDollar]);
return ClassForIosName([mappedName stringByAppendingString:suffix]); return ClassForIosName([mappedName stringByAppendingString:suffix]);
Expand All @@ -569,17 +569,22 @@ + (IOSClass *)classForIosName:(NSString *)iosName {
return ClassForIosName(JavaToIosName(name)); return ClassForIosName(JavaToIosName(name));
} }
NSString *package = [name substringToIndex:lastDot]; NSString *package = [name substringToIndex:lastDot];
NSString *suffix = JavaToIosName([name substringFromIndex:lastDot + 1]); NSString *clazz = JavaToIosName([name substringFromIndex:lastDot + 1]);
// First check if the class can be found with the default camel case package. This avoids the // First check if the class can be found with the default camel case package. This avoids the
// expensive FindRenamedPackagePrefix if possible. // expensive FindRenamedPackagePrefix if possible.
IOSClass *cls = ClassForIosName([CamelCasePackage(package) stringByAppendingString:suffix]); IOSClass *cls = ClassForIosName([CamelCasePackage(package) stringByAppendingString:clazz]);
if (cls) { if (cls) {
return cls; return cls;
} }
// Check if the package has a mapped name.
mappedName = [mappedNames objectForKey:package];
if (mappedName) {
return ClassForIosName([mappedName stringByAppendingString:clazz]);
}
// Check if the package has a renamed prefix. // Check if the package has a renamed prefix.
NSString *renamedPackage = FindRenamedPackagePrefix(package); NSString *renamedPackage = FindRenamedPackagePrefix(package);
if (renamedPackage) { if (renamedPackage) {
return ClassForIosName([renamedPackage stringByAppendingString:suffix]); return ClassForIosName([renamedPackage stringByAppendingString:clazz]);
} }
return nil; return nil;
} }
Expand Down Expand Up @@ -1410,4 +1415,4 @@ - (void)dealloc {


J2OBJC_CLASS_TYPE_LITERAL_SOURCE(IOSClass) J2OBJC_CLASS_TYPE_LITERAL_SOURCE(IOSClass)


J2OBJC_CLASS_NAME_MAPPING(IOSClass, "java.lang.Class", "IOSClass") J2OBJC_NAME_MAPPING(IOSClass, "java.lang.Class", "IOSClass")
14 changes: 7 additions & 7 deletions jre_emul/Classes/J2ObjC_source.h
Expand Up @@ -80,20 +80,20 @@ __attribute__((always_inline)) inline void JreCheckFinalize(id self, Class cls)
} }


/*! /*!
* Defines a mapping of a Java class name to its iOS equivalent. These are defined * Defines a mapping of a Java name to its iOS equivalent. These are defined for
* for any class that has an iOS name that doesn't follow the default camel-cased * any Java name that has an iOS name that doesn't follow the default camel-cased
* name mangling pattern. * name mangling pattern.
*/ */
typedef struct J2ObjcClassNameMapping { typedef struct J2ObjcNameMapping {
const char * const java_name; const char * const java_name;
const char * const ios_name; const char * const ios_name;
} J2ObjcClassNameMapping; } J2ObjcNameMapping;


/*! /*!
* Defines a mapping between Java and iOS class names, using a custom data segment. * Defines a mapping between Java and iOS names, using a custom data segment.
*/ */
#define J2OBJC_CLASS_NAME_MAPPING(CLASS, JAVANAME, IOSNAME) \ #define J2OBJC_NAME_MAPPING(CLASS, JAVANAME, IOSNAME) \
static J2ObjcClassNameMapping CLASS##_mapping __attribute__((used,\ static J2ObjcNameMapping CLASS##_mapping __attribute__((used,\
section("__DATA,__j2objc_aliases"))) = { JAVANAME, IOSNAME }; section("__DATA,__j2objc_aliases"))) = { JAVANAME, IOSNAME };




Expand Down
2 changes: 1 addition & 1 deletion jre_emul/Classes/NSCopying+JavaCloneable.m
Expand Up @@ -33,4 +33,4 @@ + (const J2ObjcClassInfo *)__metadata {


J2OBJC_INTERFACE_TYPE_LITERAL_SOURCE(NSCopying) J2OBJC_INTERFACE_TYPE_LITERAL_SOURCE(NSCopying)


J2OBJC_CLASS_NAME_MAPPING(NSCopying, "java.lang.Cloneable", "NSCopying") J2OBJC_NAME_MAPPING(NSCopying, "java.lang.Cloneable", "NSCopying")
2 changes: 1 addition & 1 deletion jre_emul/Classes/NSNumber+JavaNumber.m
Expand Up @@ -59,7 +59,7 @@ + (const J2ObjcClassInfo *)__metadata {


J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSNumber) J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSNumber)


J2OBJC_CLASS_NAME_MAPPING(NSNumber, "java.lang.Number", "NSNumber") J2OBJC_NAME_MAPPING(NSNumber, "java.lang.Number", "NSNumber")


// Empty class to force category to be loaded. // Empty class to force category to be loaded.
@implementation JreNumberCategoryDummy @implementation JreNumberCategoryDummy
Expand Down
2 changes: 1 addition & 1 deletion jre_emul/Classes/NSObject+JavaObject.m
Expand Up @@ -220,4 +220,4 @@ @implementation JreObjectCategoryDummy


J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSObject) J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSObject)


J2OBJC_CLASS_NAME_MAPPING(NSObject, "java.lang.Object", "NSObject") J2OBJC_NAME_MAPPING(NSObject, "java.lang.Object", "NSObject")
2 changes: 1 addition & 1 deletion jre_emul/Classes/NSString+JavaString.m
Expand Up @@ -1212,4 +1212,4 @@ + (void)initialize {


J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSString) J2OBJC_CLASS_TYPE_LITERAL_SOURCE(NSString)


J2OBJC_CLASS_NAME_MAPPING(NSString, "java.lang.String", "NSString") J2OBJC_NAME_MAPPING(NSString, "java.lang.String", "NSString")
Expand Up @@ -35,6 +35,7 @@
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement; import javax.lang.model.element.VariableElement;
Expand Down Expand Up @@ -98,7 +99,7 @@ protected void generate() {


printOuterDeclarations(); printOuterDeclarations();
printTypeLiteralImplementation(); printTypeLiteralImplementation();
printClassNameMapping(); printNameMapping();
} }


private void printInitFlagDefinition() { private void printInitFlagDefinition() {
Expand Down Expand Up @@ -228,16 +229,13 @@ private void printTypeLiteralImplementation() {
} }
} }


private void printClassNameMapping() { private void printNameMapping() {
String defaultObjectiveCName = nameTable.getDefaultObjectiveCName(typeElement); if (!options.stripClassNameMapping()) {
if (!ElementUtil.isPackageInfo(typeElement) Optional<String> mapping = nameTable.getNameMapping(typeElement, typeName);
&& !ElementUtil.isLambda(typeElement) if (mapping.isPresent()) {
&& !typeName.equals(defaultObjectiveCName) newline();
&& !options.stripClassNameMapping()) { printf(mapping.get());
newline(); }
printf(
"J2OBJC_CLASS_NAME_MAPPING(%s, \"%s\", \"%s\")\n",
typeName, elementUtil.getBinaryName(typeElement), typeName);
} }
} }


Expand Down
Expand Up @@ -36,6 +36,7 @@
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
Expand Down Expand Up @@ -679,4 +680,32 @@ public String getDefaultObjectiveCName(TypeElement element) {
String binaryName = elementUtil.getBinaryName(element); String binaryName = elementUtil.getBinaryName(element);
return camelCaseQualifiedName(binaryName).replace('$', '_'); return camelCaseQualifiedName(binaryName).replace('$', '_');
} }

public Optional<String> getNameMapping(TypeElement typeElement, String typeName) {
final String mappingFormat = "J2OBJC_NAME_MAPPING(%s, \"%s\", \"%s\")\n";
// No mapping is needed if the default Objective-C name was not modified.
if (typeName.equals(getDefaultObjectiveCName(typeElement))) {
return Optional.empty();
}

// Return a class mapping only if there is a explicit rename.
AnnotationMirror annotation = ElementUtil.getAnnotation(typeElement, ObjectiveCName.class);
String mappedName = classMappings.get(ElementUtil.getQualifiedName(typeElement));
if (annotation != null || mappedName != null) {
return Optional.of(
String.format(mappingFormat, typeName, elementUtil.getBinaryName(typeElement), typeName));
}

// Otherwise, there was a package rename. Because only one package mapping is needed per
// generation unit, it is safe to generate it together with a public class.
if (ElementUtil.isTopLevel(typeElement) && ElementUtil.isPublic(typeElement)) {
PackageElement packageElement = ElementUtil.getPackage(typeElement);
String packageName = packageElement.getQualifiedName().toString();
String mappedPackageName = getPrefix(packageElement);
return Optional.of(
String.format(mappingFormat, mappedPackageName, packageName, mappedPackageName));
}

return Optional.empty();
}
} }
Expand Up @@ -699,7 +699,7 @@ public void testPackageInfoPrefixAnnotation() throws IOException {
translation = getTranslatedFile("foo/bar/mumble/Test.m"); translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest"); assertTranslation(translation, "@implementation FBMTest");
assertTranslation(translation, assertTranslation(translation,
"J2OBJC_CLASS_NAME_MAPPING(FBMTest, \"foo.bar.mumble.Test\", \"FBMTest\")"); "J2OBJC_NAME_MAPPING(FBM, \"foo.bar.mumble\", \"FBM\")");
assertNotInTranslation(translation, "FooBarMumbleTest"); assertNotInTranslation(translation, "FooBarMumbleTest");
} }


Expand All @@ -719,7 +719,7 @@ public void testPackageInfoPreprocessing() throws IOException {
translation = getTranslatedFile("foo/bar/mumble/Test.m"); translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest"); assertTranslation(translation, "@implementation FBMTest");
assertTranslation(translation, assertTranslation(translation,
"J2OBJC_CLASS_NAME_MAPPING(FBMTest, \"foo.bar.mumble.Test\", \"FBMTest\")"); "J2OBJC_NAME_MAPPING(FBM, \"foo.bar.mumble\", \"FBM\")");
assertNotInTranslation(translation, "FooBarMumbleTest"); assertNotInTranslation(translation, "FooBarMumbleTest");
} }


Expand All @@ -743,7 +743,7 @@ public void testPackageInfoOnClasspath() throws IOException {
translation = getTranslatedFile("foo/bar/mumble/Test.m"); translation = getTranslatedFile("foo/bar/mumble/Test.m");
assertTranslation(translation, "@implementation FBMTest"); assertTranslation(translation, "@implementation FBMTest");
assertTranslation(translation, assertTranslation(translation,
"J2OBJC_CLASS_NAME_MAPPING(FBMTest, \"foo.bar.mumble.Test\", \"FBMTest\")"); "J2OBJC_NAME_MAPPING(FBM, \"foo.bar.mumble\", \"FBM\")");
assertNotInTranslation(translation, "FooBarMumbleTest"); assertNotInTranslation(translation, "FooBarMumbleTest");
} }


Expand Down Expand Up @@ -832,24 +832,24 @@ public void testForwardDeclarationForPrivateAbstractDeclaration() throws IOExcep
assertTranslation(translation, "@class JavaLangInteger;"); assertTranslation(translation, "@class JavaLangInteger;");
} }


public void testClassMappingStripped() throws IOException { public void testNameMappingStripped() throws IOException {
options.setStripReflection(true); options.setStripReflection(true);
String translation = translateSourceFile( String translation = translateSourceFile(
"@com.google.j2objc.annotations.ObjectiveCName(\"NSTest\") public class Test {}", "@com.google.j2objc.annotations.ObjectiveCName(\"NSTest\") public class Test {}",
"Test", "Test.m"); "Test", "Test.m");
assertNotInTranslation(translation, "J2OBJC_CLASS_NAME_MAPPING"); assertNotInTranslation(translation, "J2OBJC_NAME_MAPPING");
} }


public void testClassMappingNotStripped() throws IOException { public void testNameMappingNotStripped() throws IOException {
options.setStripReflection(true); options.setStripReflection(true);
options.setStripClassNameMapping(false); options.setStripClassNameMapping(false);
String translation = translateSourceFile( String translation = translateSourceFile(
"@com.google.j2objc.annotations.ObjectiveCName(\"NSTest\") public class Test {}", "@com.google.j2objc.annotations.ObjectiveCName(\"NSTest\") public class Test {}",
"Test", "Test.m"); "Test", "Test.m");
assertTranslation(translation, "J2OBJC_CLASS_NAME_MAPPING"); assertTranslation(translation, "J2OBJC_NAME_MAPPING(NSTest, \"Test\", \"NSTest\")");
} }


public void testClassMappingNestedClass() throws IOException { public void testNameMappingNestedClass() throws IOException {
options.setStripClassNameMapping(false); options.setStripClassNameMapping(false);
String hFile = String hFile =
translateSourceFile( translateSourceFile(
Expand All @@ -868,37 +868,47 @@ public void testClassMappingNestedClass() throws IOException {
assertTranslation(hFile, "@interface Renamed"); assertTranslation(hFile, "@interface Renamed");
assertTranslation(mFile, "@implementation Renamed"); assertTranslation(mFile, "@implementation Renamed");
assertTranslation( assertTranslation(
mFile, "J2OBJC_CLASS_NAME_MAPPING(Renamed, \"foo.Outer$Inner\", \"Renamed\")"); mFile, "J2OBJC_NAME_MAPPING(Renamed, \"foo.Outer$Inner\", \"Renamed\")");
assertNotInTranslation(mFile, "FooOuter_Inner"); assertNotInTranslation(mFile, "FooOuter_Inner");
// Make sure that the only class mapping is the one for the inner class. // Make sure that the only class mapping is the one for the inner class.
assertOccurrences(mFile, "J2OBJC_CLASS_NAME_MAPPING", 1); assertOccurrences(mFile, "J2OBJC_NAME_MAPPING", 1);
} }


public void testLambdaDoesNotGenerateClassMapping() throws IOException { /**
* The following scenarios should not generate name mappings: package-info files, lambdas and
* anonymous classes.
*/
public void testCasesWithoutNameMapping() throws IOException {
options.setStripClassNameMapping(false); options.setStripClassNameMapping(false);
addSourceFile(
"@ObjectiveCName(\"FBM\")\n"
+ "package foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"foo/bar/mumble/package-info.java");
String mFile = String mFile =
translateSourceFile( translateSourceFile(
"package foo; " "package foo.bar.mumble; "
+ "public class Bar { " + "public class Test { "
+ " Runnable r = () -> {}; " + " Runnable r = () -> {}; " // lambda
+ " Runnable s = new Runnable() { " // anonymous
+ " @Override public void run() {} "
+ " }; "
+ " public void method() { "
+ " Runnable unused = new Runnable() {" // local anonymous
+ " @Override public void run() {} "
+ " };"
+ " } "
+ "}", + "}",
"foo.Bar", "foo.bar.mumble.Test",
"foo/Bar.m"); "foo/bar/mumble/Test.m");
assertNotInTranslation(mFile, "J2OBJC_CLASS_NAME_MAPPING"); assertTranslation(
} mFile, "J2OBJC_NAME_MAPPING(FBM, \"foo.bar.mumble\", \"FBM\")");

assertOccurrences(mFile, "J2OBJC_NAME_MAPPING", 1);
public void testPackageInfoDoesNotGenerateClassMapping() throws IOException {
options.setStripClassNameMapping(false);
String translation =
translateSourceFile(
"@ObjectiveCName(\"FBM\")\n"
+ "package foo.bar.mumble;\n"
+ "import com.google.j2objc.annotations.ObjectiveCName;",
"foo.bar.mumble.package-info",
"foo/bar/mumble/package-info.m");
// The ObjectiveCName annotation affects classes in the package but not the package itself. // The ObjectiveCName annotation affects classes in the package but not the package itself.
String translation =
translateSourceFile("foo.bar.mumble.package-info", "foo/bar/mumble/package-info.m");
assertTranslation(translation, "FooBarMumblepackage_info"); assertTranslation(translation, "FooBarMumblepackage_info");
assertNotInTranslation(translation, "FBMpackage_info"); assertNotInTranslation(translation, "FBMpackage_info");
assertNotInTranslation(translation, "J2OBJC_CLASS_NAME_MAPPING"); assertNotInTranslation(translation, "J2OBJC_NAME_MAPPING");
} }
} }

0 comments on commit d08db63

Please sign in to comment.