diff --git a/jre_emul/Classes/IOSClass.m b/jre_emul/Classes/IOSClass.m index e32d15fdc5..98756117e6 100644 --- a/jre_emul/Classes/IOSClass.m +++ b/jre_emul/Classes/IOSClass.m @@ -51,6 +51,7 @@ #import "IOSProtocolClass.h" #import "IOSShortArray.h" #import "JavaMetadata.h" +#import "objc/message.h" #import "objc/runtime.h" @implementation IOSClass @@ -594,12 +595,10 @@ - (IOSObjectArray *)getDeclaredAnnotations { - (JavaClassMetadata *)getMetadata { Class cls = [self objcClass]; if (cls) { - SEL sel = @selector(__metadata); - if ([cls respondsToSelector:sel]) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - J2ObjcClassInfo *rawData = (ARCBRIDGE J2ObjcClassInfo *) [cls performSelector:sel]; -#pragma clang diagnostic pop + // Can't use respondsToSelector here because that will search superclasses. + Method metadataMethod = JreFindClassMethod(cls, "__metadata"); + if (metadataMethod) { + J2ObjcClassInfo *rawData = (ARCBRIDGE J2ObjcClassInfo *) method_invoke(cls, metadataMethod); return AUTORELEASE([[JavaClassMetadata alloc] initWithMetadata:rawData]); } } diff --git a/jre_emul/Classes/IOSConcreteClass.m b/jre_emul/Classes/IOSConcreteClass.m index f2ecba56bf..76185b66a2 100644 --- a/jre_emul/Classes/IOSConcreteClass.m +++ b/jre_emul/Classes/IOSConcreteClass.m @@ -18,6 +18,7 @@ // #import "IOSConcreteClass.h" +#import "IOSReflection.h" #import "JavaMetadata.h" #import "java/lang/ClassCastException.h" #import "java/lang/Enum.h" @@ -201,32 +202,15 @@ - (IOSObjectArray *)getConstructors { return getConstructorsImpl(self, YES); } -static SEL FindSelector(NSString *name, Class cls) { - unsigned int count; - SEL result = nil; - Method *methods = class_copyMethodList(cls, &count); - for (NSUInteger i = 0; i < count; i++) { - SEL sel = method_getName(methods[i]); - if ([name isEqualToString:NSStringFromSelector(sel)]) { - result = sel; - break; - } - } - free(methods); - return result; -} - - (JavaLangReflectMethod *)findMethodWithTranslatedName:(NSString *)objcName { - SEL selector = FindSelector(objcName, class_); - if (selector) { - JavaClassMetadata *metadata = [self getMetadata]; - return [JavaLangReflectMethod methodWithSelector:selector withClass:self - withMetadata:metadata ? [metadata findMethodInfo:objcName] : nil]; + const char *name = [objcName UTF8String]; + Method method = JreFindInstanceMethod(class_, name); + if (!method) { + method = JreFindClassMethod(class_, name); } - selector = FindSelector(objcName, object_getClass(class_)); - if (selector) { + if (method) { JavaClassMetadata *metadata = [self getMetadata]; - return [JavaLangReflectMethod methodWithSelector:selector withClass:self + return [JavaLangReflectMethod methodWithSelector:method_getName(method) withClass:self withMetadata:metadata ? [metadata findMethodInfo:objcName] : nil]; } return nil; @@ -235,12 +219,11 @@ - (JavaLangReflectMethod *)findMethodWithTranslatedName:(NSString *)objcName { static JavaLangReflectConstructor *GetConstructorImpl( IOSConcreteClass *iosClass, IOSObjectArray *paramTypes) { NSString *name = IOSClass_GetTranslatedMethodName(@"init", paramTypes); - SEL selector = FindSelector(name, iosClass->class_); - if (selector) { - NSString *objcName = NSStringFromSelector(selector); + Method method = JreFindInstanceMethod(iosClass->class_, [name UTF8String]); + if (method) { JavaClassMetadata *metadata = [iosClass getMetadata]; - return [JavaLangReflectConstructor constructorWithSelector:selector withClass:iosClass - withMetadata:metadata ? [metadata findMethodInfo:objcName] : nil]; + return [JavaLangReflectConstructor constructorWithSelector:method_getName(method) + withClass:iosClass withMetadata:metadata ? [metadata findMethodInfo:name] : nil]; } @throw AUTORELEASE([[JavaLangNoSuchMethodException alloc] init]); } diff --git a/jre_emul/Classes/IOSReflection.h b/jre_emul/Classes/IOSReflection.h index 56ca93372f..0322ac15b7 100644 --- a/jre_emul/Classes/IOSReflection.h +++ b/jre_emul/Classes/IOSReflection.h @@ -21,6 +21,7 @@ #define JreEmulation_IOSReflection_h #import +#import "objc/runtime.h" @protocol JavaLangReflectType; @@ -94,6 +95,7 @@ typedef struct J2ObjcMethodInfo { const char *javaName; const char *returnType; uint16_t modifiers; + const char *exceptions; } J2ObjcMethodInfo; typedef struct J2ObjcClassInfo { @@ -135,5 +137,7 @@ typedef union { } J2ObjcRawValue; extern id JreTypeForString(const char *typeStr); +extern Method JreFindInstanceMethod(Class cls, const char *name); +extern Method JreFindClassMethod(Class cls, const char *name); #endif // JreEmulation_IOSReflection_h diff --git a/jre_emul/Classes/IOSReflection.m b/jre_emul/Classes/IOSReflection.m index eac377cd3a..f585ca1777 100644 --- a/jre_emul/Classes/IOSReflection.m +++ b/jre_emul/Classes/IOSReflection.m @@ -19,7 +19,6 @@ #import "IOSReflection.h" -#import #import "IOSClass.h" #import "java/lang/AssertionError.h" #import "java/lang/reflect/TypeVariableImpl.h" @@ -41,3 +40,21 @@ NSString *msg = [NSString stringWithFormat:@"invalid type from metadata %s", typeStr]; @throw AUTORELEASE([[JavaLangAssertionError alloc] initWithNSString:msg]); } + +Method JreFindInstanceMethod(Class cls, const char *name) { + unsigned int count; + Method result = nil; + Method *methods = class_copyMethodList(cls, &count); + for (NSUInteger i = 0; i < count; i++) { + if (strcmp(name, sel_getName(method_getName(methods[i]))) == 0) { + result = methods[i]; + break; + } + } + free(methods); + return result; +} + +Method JreFindClassMethod(Class cls, const char *name) { + return JreFindInstanceMethod(object_getClass(cls), name); +} diff --git a/jre_emul/Classes/java/lang/reflect/ExecutableMember.h b/jre_emul/Classes/java/lang/reflect/ExecutableMember.h index af4d106ad4..4459ef8bbe 100644 --- a/jre_emul/Classes/java/lang/reflect/ExecutableMember.h +++ b/jre_emul/Classes/java/lang/reflect/ExecutableMember.h @@ -89,7 +89,6 @@ // Protected methods. - (NSString *)internalName; - (JavaLangReflectMethod *)getAnnotationsAccessor:(NSString *)methodName; -- (JavaLangReflectMethod *)getExceptionsAccessor:(NSString *)methodName; - (JavaLangReflectMethod *)getParameterAnnotationsAccessor:(NSString *)methodName; @end diff --git a/jre_emul/Classes/java/lang/reflect/ExecutableMember.m b/jre_emul/Classes/java/lang/reflect/ExecutableMember.m index d8c000e8ba..ab4aa179b1 100644 --- a/jre_emul/Classes/java/lang/reflect/ExecutableMember.m +++ b/jre_emul/Classes/java/lang/reflect/ExecutableMember.m @@ -190,10 +190,16 @@ - (BOOL)isSynthetic { } - (IOSObjectArray *)getExceptionTypes { - JavaLangReflectMethod *method = [self getExceptionsAccessor:[self internalName]]; - if (method) { - IOSObjectArray *noArgs = [IOSObjectArray arrayWithLength:0 type:[NSObject getClass]]; - return (IOSObjectArray *) [method invokeWithId:nil withNSObjectArray:noArgs]; + if (metadata_ && metadata_->exceptions) { + NSString *exceptionsStr = [NSString stringWithUTF8String:metadata_->exceptions]; + NSArray *exceptionsArray = [exceptionsStr componentsSeparatedByString:@";"]; + IOSObjectArray *result = + [IOSObjectArray arrayWithLength:[exceptionsArray count] type:[IOSClass getClass]]; + NSUInteger count = 0; + for (NSString *thrownException in exceptionsArray) { + IOSObjectArray_Set(result, count++, [IOSClass classForIosName:thrownException]); + } + return result; } else { return [IOSObjectArray arrayWithLength:0 type:[IOSClass getClass]]; } @@ -230,10 +236,6 @@ - (JavaLangReflectMethod *)getAnnotationsAccessor:(NSString *)methodName { return getAccessor(class_, methodName, @"annotations"); } -- (JavaLangReflectMethod *)getExceptionsAccessor:(NSString *)methodName { - return getAccessor(class_, methodName, @"exceptions"); -} - - (JavaLangReflectMethod *)getParameterAnnotationsAccessor:(NSString *)methodName { return [self getAnnotationsAccessor:[NSString stringWithFormat:@"%@_params", methodName]]; } diff --git a/jre_emul/apache_harmony/classlib/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/lang/ClassTest.java b/jre_emul/apache_harmony/classlib/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/lang/ClassTest.java index fb0c771954..6a537e61ee 100644 --- a/jre_emul/apache_harmony/classlib/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/lang/ClassTest.java +++ b/jre_emul/apache_harmony/classlib/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/java/lang/ClassTest.java @@ -354,8 +354,13 @@ public void test_getComponentType() { */ public void test_getConstructor$Ljava_lang_Class() throws NoSuchMethodException { - assertNotNull(TestClass.class.getConstructor(new Class[0])); - assertNotNull(TestClass.class.getConstructor(Object.class)); + TestClass.class.getConstructor(new Class[0]); + try { + TestClass.class.getConstructor(Object.class); + fail("Found private constructor"); + } catch (NoSuchMethodException e) { + // Correct - constructor with obj is private + } } /** @@ -363,7 +368,7 @@ public void test_getComponentType() { */ public void test_getConstructors() throws Exception { Constructor[] c = TestClass.class.getConstructors(); - assertEquals("Incorrect number of constructors returned", 2, c.length); + assertEquals("Incorrect number of constructors returned", 1, c.length); } /** diff --git a/translator/src/main/java/com/google/devtools/j2objc/gen/MetadataGenerator.java b/translator/src/main/java/com/google/devtools/j2objc/gen/MetadataGenerator.java index 6b93210134..7c5961f780 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/gen/MetadataGenerator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/gen/MetadataGenerator.java @@ -40,7 +40,6 @@ public class MetadataGenerator { private final AbstractTypeDeclaration typeNode; private final ITypeBinding type; private boolean generated = false; - private boolean hasMetadata = false; private int methodMetadataCount = 0; public MetadataGenerator(AbstractTypeDeclaration typeNode) { @@ -49,11 +48,6 @@ public MetadataGenerator(AbstractTypeDeclaration typeNode) { this.type = Types.getTypeBinding(typeNode); } - public boolean hasMetadata() { - ensureGenerated(); - return hasMetadata; - } - public String getMetadataSource() { ensureGenerated(); return builder.toString(); @@ -78,7 +72,6 @@ private void generateMetadata() { printf("NULL, "); } else { printf("\"%s\", ", pkgName); - hasMetadata = true; } printf("%s, ", getEnclosingName()); printf("0x%s, ", Integer.toHexString(getTypeModifiers())); @@ -95,7 +88,6 @@ private String getEnclosingName() { if (declaringType == null) { return "NULL"; } - hasMetadata = true; StringBuilder sb = new StringBuilder("\""); List types = Lists.newArrayList(); while (declaringType != null) { @@ -118,7 +110,6 @@ private void generateMethodsMetadata() { String metadata = getMethodMetadata(Types.getMethodBinding(decl)); if (metadata != null) { methodMetadata.add(metadata); - hasMetadata = true; } } if (methodMetadata.size() > 0) { @@ -132,8 +123,7 @@ private void generateMethodsMetadata() { } private String getMethodMetadata(IMethodBinding method) { - ITypeBinding[] paramTypes = method.getParameterTypes(); - if ((method.isConstructor() && paramTypes.length == 0) || method.isSynthetic()) { + if (method.isSynthetic()) { return null; } @@ -141,26 +131,45 @@ private String getMethodMetadata(IMethodBinding method) { int modifiers = getMethodModifiers(method); boolean needsMetadata = (modifiers & ~Modifier.STATIC) != Modifier.PUBLIC; - String returnTypeStr = "NULL"; + String returnTypeStr = null; if (!method.isConstructor()) { ITypeBinding returnType = method.getReturnType(); - returnTypeStr = String.format("\"%s\"", getTypeName(returnType)); + returnTypeStr = getTypeName(returnType); if (!returnType.isPrimitive() || returnType.getName().equals("boolean")) { needsMetadata = true; } } - String methodName = "NULL"; + String methodName = null; + ITypeBinding[] paramTypes = method.getParameterTypes(); // Most of the time the method name can be parsed from the ObjC selector // but not if the first parameter contains the substring "With". if (paramTypes.length > 0 && NameTable.parameterKeyword(paramTypes[0]).contains("With")) { - methodName = "\"" + method.getName() + "\""; + methodName = method.getName(); needsMetadata = true; } + String thrownExceptions = getThrownExceptions(method); + needsMetadata |= thrownExceptions != null; if (!needsMetadata) { return null; } - return String.format(" { \"%s\", %s, %s, 0x%x },\n", - NameTable.getMethodSelector(method), methodName, returnTypeStr, modifiers); + return String.format(" { \"%s\", %s, %s, 0x%x, %s },\n", + NameTable.getMethodSelector(method), cStr(methodName), cStr(returnTypeStr), modifiers, + cStr(thrownExceptions)); + } + + private String getThrownExceptions(IMethodBinding method) { + ITypeBinding[] exceptionTypes = method.getExceptionTypes(); + if (exceptionTypes.length == 0) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < exceptionTypes.length; i++) { + if (i != 0) { + sb.append(';'); + } + sb.append(NameTable.getFullName(exceptionTypes[i])); + } + return sb.toString(); } private int printSuperclassTypeArguments() { @@ -172,7 +181,6 @@ private int printSuperclassTypeArguments() { if (typeArgs.length == 0) { return 0; } - hasMetadata = true; print(" static const char *superclass_type_args[] = {"); for (int i = 0; i < typeArgs.length; i++) { if (i != 0) { @@ -215,7 +223,6 @@ private int getTypeModifiers() { if (type.isAnonymous()) { modifiers |= 0x8000; } - hasMetadata |= (modifiers & ~0x200) != Modifier.PUBLIC; return modifiers; } @@ -234,15 +241,19 @@ private static int getMethodModifiers(IMethodBinding type) { return modifiers; } - public void print(String s) { + private String cStr(String s) { + return s == null ? "NULL" : "\"" + s + "\""; + } + + private void print(String s) { builder.append(s); } - public void printf(String format, Object... args) { + private void printf(String format, Object... args) { builder.append(String.format(format, args)); } - public void println(String s) { + private void println(String s) { builder.append(s).append('\n'); } } diff --git a/translator/src/main/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGenerator.java b/translator/src/main/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGenerator.java index edabe2a546..5a811d3da8 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGenerator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGenerator.java @@ -19,7 +19,6 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.devtools.j2objc.J2ObjC; import com.google.devtools.j2objc.J2ObjC.Language; @@ -66,7 +65,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -82,8 +80,6 @@ public class ObjectiveCImplementationGenerator extends ObjectiveCSourceFileGener private final Set invokedConstructors = Sets.newHashSet(); private final ListMultimap blockComments = ArrayListMultimap.create(); - private final Map metadataGenerators = - Maps.newHashMap(); /** * Generate an Objective-C implementation file for each type declared in a @@ -136,15 +132,11 @@ private List collectTypes(CompilationUnit unit) { unit.accept(new ErrorReportingASTVisitor() { @Override public boolean visit(TypeDeclaration node) { - if (node.isInterface()) { - if (!getStaticFieldsNeedingAccessors( - Arrays.asList(node.getFields()), /* isInterface */ true).isEmpty()) { - types.add(node); - } else if (!Options.stripReflection() && hasMetadata(node)) { - types.add(node); - } - } else { - types.add(node); // always print concrete types + if (!node.isInterface() + || !getStaticFieldsNeedingAccessors( + Arrays.asList(node.getFields()), /* isInterface */ true).isEmpty() + || !Options.stripReflection()) { + types.add(node); } return false; } @@ -512,7 +504,7 @@ private void printStaticInterface(AbstractTypeDeclaration node, List staticFields = getStaticFieldsNeedingAccessors(fields, /* isInterface */ true); if (staticFields.isEmpty()) { - if (hasMetadata(node)) { + if (!Options.stripReflection()) { printf("\n@interface %s : NSObject\n@end\n", typeName); } else { return; @@ -673,33 +665,7 @@ protected String methodDeclaration(MethodDeclaration m) { if (methodBody == null) { return ""; } - return super.methodDeclaration(m) + " " + reindent(methodBody) + "\n\n" + - methodExceptionsFunction(m); - } - - protected String methodExceptionsFunction(MethodDeclaration m) { - if (m.thrownExceptions().isEmpty() || Options.stripReflection()) { - return ""; - } - IMethodBinding method = Types.getMethodBinding(m); - StringBuilder sb = new StringBuilder(); - sb.append("+ (IOSObjectArray *)__exceptions_"); - sb.append(methodKey(method)); - sb.append(" {\n"); - ITypeBinding[] exceptionTypes = method.getExceptionTypes(); - sb.append(" return [IOSObjectArray arrayWithObjects:(id[]) { "); - for (int i = 0; i < exceptionTypes.length; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append("[["); - sb.append(NameTable.getFullName(exceptionTypes[i])); - sb.append(" class] getClass]"); - } - sb.append(" } count:"); - sb.append(exceptionTypes.length); - sb.append(" type:[[IOSClass class] getClass]];\n}\n\n"); - return sb.toString(); + return super.methodDeclaration(m) + " " + reindent(methodBody) + "\n\n"; } private String generateNativeStub(MethodDeclaration m) { @@ -809,8 +775,7 @@ protected String constructorDeclaration(MethodDeclaration m) { + super.constructorDeclaration(m, false) + " {\n return " + generateStatement(createInnerConstructorInvocation(m), false) + ";\n}\n\n"; } else { - return super.constructorDeclaration(m, false) + " " + reindent(methodBody) + "\n\n" + - methodExceptionsFunction(m); + return super.constructorDeclaration(m, false) + " " + reindent(methodBody) + "\n\n"; } } @@ -1124,26 +1089,7 @@ private void printAnnotationValue(AST ast, Object value) { } } - private MetadataGenerator getMetadataGenerator(AbstractTypeDeclaration node) { - MetadataGenerator generator = metadataGenerators.get(node); - if (generator == null) { - generator = new MetadataGenerator(node); - metadataGenerators.put(node, generator); - } - return generator; - } - private void printMetadata(AbstractTypeDeclaration node) { - MetadataGenerator metadataGenerator = getMetadataGenerator(node); - if (metadataGenerator.hasMetadata()) { - print(metadataGenerator.getMetadataSource()); - } - } - - /** - * Does this type need a metadata method? - */ - private boolean hasMetadata(AbstractTypeDeclaration node) { - return getMetadataGenerator(node).hasMetadata(); + print(new MetadataGenerator(node).getMetadataSource()); } } diff --git a/translator/src/main/java/com/google/devtools/j2objc/util/NameTable.java b/translator/src/main/java/com/google/devtools/j2objc/util/NameTable.java index 7aca651e1d..8652669715 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/util/NameTable.java +++ b/translator/src/main/java/com/google/devtools/j2objc/util/NameTable.java @@ -382,7 +382,12 @@ public static String parameterKeyword(ITypeBinding type) { } public static String getMethodSelector(IMethodBinding method) { - StringBuilder sb = new StringBuilder(NameTable.getName(method)); + StringBuilder sb = new StringBuilder(); + if (method.isConstructor()) { + sb.append("init"); + } else { + sb.append(getName(method)); + } IOSMethod iosMethod = IOSMethodBinding.getIOSMethod(method); if (iosMethod != null) { List params = iosMethod.getParameters(); diff --git a/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java b/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java index c971dd18d7..dc7b02fd99 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java @@ -692,15 +692,6 @@ public void testTypeAnnotationWithParameter() throws IOException { "count:1 type:[IOSClass classWithProtocol:@protocol(JavaLangAnnotationAnnotation)]];"); } - public void testExceptionsMetadata() throws IOException { - String translation = translateSourceFile( - "class Test { void test() throws Exception, java.lang.Error {} }", "Test", "Test.m"); - assertTranslation(translation, "+ (IOSObjectArray *)__exceptions_test "); - assertTranslation(translation, - "return [IOSObjectArray arrayWithObjects:(id[]) { [[JavaLangException class] getClass], " + - "[[JavaLangError class] getClass] } count:2 type:[[IOSClass class] getClass]];"); - } - public void testFreeFormNativeCode() throws IOException { String translation = translateSourceFile( "class Test { void method1() {} /*-[ OCNI1 ]-*/ " + @@ -761,7 +752,7 @@ public void testSynchronizedNativeMethod() throws IOException { public void testMethodMetadata() throws IOException { String translation = translateSourceFile( // Separate methods are used so each only has one modifier. - "class Test { " + + "abstract class Test { " + " Object test1() { return null; }" + // package-private " private char test2() { return 'a'; }" + " protected void test3() { }" + @@ -769,18 +760,21 @@ public void testMethodMetadata() throws IOException { " synchronized boolean test5() { return false; }" + " String test6(String s, Object... args) { return null; }" + " native void test7() /*-[ exit(0); ]-*/; " + + " abstract void test8() throws InterruptedException, Error; " + " public void noMetadata1() {}" + " public static void noMetadata2() {}" + "}", "Test", "Test.m"); - assertTranslatedLines(translation, "{ \"test1\", NULL, \"LNSObject\", 0x0 },"); - assertTranslatedLines(translation, "{ \"test2\", NULL, \"C\", 0x2 },"); - assertTranslatedLines(translation, "{ \"test3\", NULL, \"V\", 0x4 },"); - assertTranslatedLines(translation, "{ \"test4\", NULL, \"J\", 0x10 },"); - assertTranslatedLines(translation, "{ \"test5\", NULL, \"Z\", 0x20 },"); - assertTranslatedLines(translation, - "{ \"test6WithNSString:withNSObjectArray:\", NULL, \"LNSString\", 0x80 }"); - assertTranslatedLines(translation, "{ \"test7\", NULL, \"V\", 0x100 },"); + assertTranslation(translation, "{ \"test1\", NULL, \"LNSObject\", 0x0, NULL },"); + assertTranslation(translation, "{ \"test2\", NULL, \"C\", 0x2, NULL },"); + assertTranslation(translation, "{ \"test3\", NULL, \"V\", 0x4, NULL },"); + assertTranslation(translation, "{ \"test4\", NULL, \"J\", 0x10, NULL },"); + assertTranslation(translation, "{ \"test5\", NULL, \"Z\", 0x20, NULL },"); + assertTranslation(translation, + "{ \"test6WithNSString:withNSObjectArray:\", NULL, \"LNSString\", 0x80, NULL }"); + assertTranslation(translation, "{ \"test7\", NULL, \"V\", 0x100, NULL },"); + assertTranslation(translation, + "{ \"test8\", NULL, \"V\", 0x400, \"JavaLangInterruptedException;JavaLangError\" },"); assertNotInTranslation(translation, "{ \"noMetadata1\""); assertNotInTranslation(translation, "{ \"noMetadata2\""); }