/*
* Copyright (C) 2013 RoboVM AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import org.robovm.compiler.MarshalerLookup.MarshalSite;
import org.robovm.compiler.MarshalerLookup.Marshaler;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.config.Config.Home;
import org.robovm.compiler.log.Logger;
import org.robovm.rt.bro.ArrayMarshalers;
import org.robovm.rt.bro.MarshalerFlags;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Array;
import org.robovm.rt.bro.annotation.Bridge;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.Marshalers;
import org.robovm.rt.bro.annotation.MarshalsArray;
import org.robovm.rt.bro.annotation.MarshalsPointer;
import org.robovm.rt.bro.annotation.MarshalsValue;
import org.robovm.rt.bro.annotation.StructMember;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.options.Options;
/**
* Tests {@link MarshalerLookup}.
*/
public class MarshalerLookupTest {
private static Config config;
@BeforeClass
public static void initializeSoot() throws IOException {
soot.G.reset();
Options.v().set_output_format(Options.output_format_jimple);
Options.v().set_include_all(true);
Options.v().set_print_tags_in_output(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_soot_classpath(System.getProperty("sun.boot.class.path") +
":" + System.getProperty("java.class.path"));
Scene.v().loadNecessaryClasses();
Config.Builder configBuilder = new Config.Builder();
for (String p : System.getProperty("sun.boot.class.path").split(File.pathSeparator)) {
configBuilder.addBootClasspathEntry(new File(p));
}
for (String p : System.getProperty("java.class.path").split(File.pathSeparator)) {
configBuilder.addClasspathEntry(new File(p));
}
configBuilder.skipInstall(true);
configBuilder.skipLinking(true);
configBuilder.home(new MockHome(new File(System.getProperty("java.io.tmpdir"))));
configBuilder.logger(new Logger() {
public void warn(String format, Object... args) {
System.out.format("WARN: " + format, args);
System.out.println();
}
public void info(String format, Object... args) {
System.out.format("INFO: " + format, args);
System.out.println();
}
public void error(String format, Object... args) {
System.out.format("ERROR: " + format, args);
System.out.println();
}
public void debug(String format, Object... args) {
System.out.format("DEBUG: " + format, args);
System.out.println();
}
});
config = configBuilder.build();
}
private Clazz toClazz(Class<?> cls) {
return config.getClazzes().load(cls.getName().replace('.', '/'));
}
private SootClass toSootClass(Class<?> cls) {
return toClazz(cls).getSootClass();
}
@Test
public void testFindMarshalersBuiltins() {
MarshalerLookup lookup = new MarshalerLookup(config);
List<Marshaler> l = lookup.findMarshalers(toSootClass(String.class));
assertFalse(l.isEmpty());
assertSame(toClazz(ArrayMarshalers.ByteArrayMarshaler.class), l.get(0).getClazz());
assertSame(toClazz(ArrayMarshalers.ShortArrayMarshaler.class), l.get(1).getClazz());
}
@Test
public void testFindMarshalersSkipBuiltins() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
List<Marshaler> l = lookup.findMarshalers(toSootClass(C1.class));
assertEquals(1, l.size());
assertSame(toClazz(M1.class), l.get(0).getClazz());
}
@Test
public void testFindMarshalersSearchesSuperclasses() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
List<Marshaler> l = lookup.findMarshalers(toSootClass(C2.class));
assertEquals(1, l.size());
assertSame(toClazz(M1.class), l.get(0).getClazz());
}
@Test
public void testFindMarshalersSearchesInterfaces() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
List<Marshaler> l = lookup.findMarshalers(toSootClass(C4.class));
assertEquals(7, l.size());
assertSame(toClazz(M7.class), l.get(0).getClazz());
assertSame(toClazz(M1.class), l.get(1).getClazz());
assertSame(toClazz(M3.class), l.get(2).getClazz());
assertSame(toClazz(M6.class), l.get(3).getClazz());
assertSame(toClazz(M2.class), l.get(4).getClazz());
assertSame(toClazz(M4.class), l.get(5).getClazz());
assertSame(toClazz(M5.class), l.get(6).getClazz());
}
@Test
public void testFindMarshalersSearchesOuterClasses() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
List<Marshaler> l = lookup.findMarshalers(toSootClass(C2.Inner.InnerInner.class));
assertEquals(1, l.size());
assertSame(toClazz(M1.class), l.get(0).getClazz());
}
@Test
public void testFindMarshalerMethodUnsuccessfulSearchNoMarshalers() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo1.class).getMethodByName("foo");
try {
lookup.findMarshalerMethod(new MarshalSite(method));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(method, 0));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
@Test
public void testFindMarshalerMethodUnsuccessfulSearchNoMatchingMarshaler() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo2.class).getMethodByName("foo");
try {
lookup.findMarshalerMethod(new MarshalSite(method));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(method, 0));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
@Test
public void testFindMarshalerMethodUnsuccessfulWithMarshalerAnnotation() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo3.class).getMethodByName("foo");
try {
lookup.findMarshalerMethod(new MarshalSite(method));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(method, 0));
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
@Test
public void testFindMarshalerMethodClassMatchPointer() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo1");
assertEquals(toSootClass(M2.class).getMethodByName("stringToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("stringToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodClassMatchValue() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo2");
assertEquals(toSootClass(M2.class).getMethodByName("integerToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("integerToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodInterfaceMatchPointer() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo3");
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodSuperclassMatchValue() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo4");
assertEquals(toSootClass(M2.class).getMethodByName("numberToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("numberToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodUnsuccessfulSearchUnsupportedCallTypeCallback() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo5");
try {
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
@Test
public void testFindMarshalerMethodClassMatchArray() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo6");
assertEquals(toSootClass(M2.class).getMethodByName("stringArrayToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("stringArrayToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodSuperclassMatchArray() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo7");
assertEquals(toSootClass(M2.class).getMethodByName("numberArrayToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("numberArrayToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodInterfaceMatchArray() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo8");
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceArrayToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceArrayToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodInterfaceDirectMatchPointer() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod method = toSootClass(Foo4.class).getMethodByName("foo9");
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceToObject"),
lookup.findMarshalerMethod(new MarshalSite(method)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("charSequenceToNative"),
lookup.findMarshalerMethod(new MarshalSite(method, 0)).getMethod());
}
@Test
public void testFindMarshalerMethodUnsuccessfulSearchUnsupportedCallTypeStructMember() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV1");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV1");
try {
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
@Test
public void testFindMarshalerStructMemberPrimitiveArray1D() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV2");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV2");
assertEquals(toSootClass(M2.class).getMethodByName("byteArray1DToObject"),
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("byteArray1DToNative"),
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod());
}
@Test
public void testFindMarshalerStructMemberPrimitiveArray2D() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV3");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV3");
assertEquals(toSootClass(M2.class).getMethodByName("byteArray2DToObject"),
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("byteArray2DToNative"),
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod());
}
@Test
public void testFindMarshalerStructMemberPrimitiveArray3D() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV4");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV4");
assertEquals(toSootClass(M2.class).getMethodByName("byteArray3DToObject"),
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("byteArray3DToNative"),
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod());
}
@Test
public void testFindMarshalerStructMemberByteBuffer1D() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV5");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV5");
assertEquals(toSootClass(M2.class).getMethodByName("byteBuffer1DToObject"),
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod());
assertEquals(toSootClass(M2.class).getMethodByName("byteBuffer1DToNative"),
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod());
}
@Test
public void testFindMarshalerStructMemberUnsupportedArrayDimension() {
MarshalerLookup lookup = new MarshalerLookup(config).searchBuiltins(false);
SootMethod getter = toSootClass(TestStruct.class).getMethodByName("getV6");
SootMethod setter = toSootClass(TestStruct.class).getMethodByName("setV6");
try {
lookup.findMarshalerMethod(new MarshalSite(getter)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
try {
lookup.findMarshalerMethod(new MarshalSite(setter, 0)).getMethod();
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException e) {}
}
public static class M1 {}
public static class M2 {
@MarshalsPointer(supportedCallTypes = MarshalerFlags.CALL_TYPE_BRIDGE)
public static String stringToObject(Class<?> cls, long handle, long flags) {
return null;
}
@MarshalsPointer(supportedCallTypes = MarshalerFlags.CALL_TYPE_BRIDGE)
public static long stringToNative(String s, long flags) {
return 0L;
}
@MarshalsPointer
public static CharSequence charSequenceToObject(Class<?> cls, long handle, long flags) {
return null;
}
@MarshalsPointer
public static long charSequenceToNative(CharSequence s, long flags) {
return 0L;
}
@MarshalsValue
public static Integer integerToObject(Class<?> cls, int v, long flags) {
return null;
}
@MarshalsValue
public static int integerToNative(Integer s, long flags) {
return 0;
}
@MarshalsValue
public static Number numberToObject(Class<?> cls, double v, long flags) {
return null;
}
@MarshalsValue
public static double numberToNative(Number s, long flags) {
return 0;
}
@MarshalsPointer
public static String[] stringArrayToObject(Class<?> cls, long handle, long flags) {
return null;
}
@MarshalsPointer
public static long stringArrayToNative(String[] s, long flags) {
return 0L;
}
@MarshalsPointer
public static Number[] numberArrayToObject(Class<?> cls, long handle, long flags) {
return null;
}
@MarshalsPointer
public static long numberArrayToNative(Number[] s, long flags) {
return 0;
}
@MarshalsPointer
public static CharSequence[] charSequenceArrayToObject(Class<?> cls, long handle, long flags) {
return null;
}
@MarshalsPointer
public static long charSequenceArrayToNative(CharSequence[] s, long flags) {
return 0L;
}
@MarshalsArray
public static byte[] byteArray1DToObject(Class<?> cls, long handle, long flags, int d1) {
return null;
}
@MarshalsArray
public static void byteArray1DToNative(byte[] b, long handle, long flags, int d1) {
}
@MarshalsArray
public static byte[][] byteArray2DToObject(Class<?> cls, long handle, long flags, int d1, int d2) {
return null;
}
@MarshalsArray
public static void byteArray2DToNative(byte[][] b, long handle, long flags, int d1, int d2) {
}
@MarshalsArray
public static byte[][][] byteArray3DToObject(Class<?> cls, long handle, long flags, int d1, int d2, int d3) {
return null;
}
@MarshalsArray
public static void byteArray3DToNative(byte[][][] b, long handle, long flags, int d1, int d2, int d3) {
}
@MarshalsArray
public static ByteBuffer byteBuffer1DToObject(Class<?> cls, long handle, long flags, int d1) {
return null;
}
@MarshalsArray
public static void byteBuffer1DToNative(ByteBuffer b, long handle, long flags, int d1) {
}
}
public static class M3 {}
public static class M4 {}
public static class M5 {}
public static class M6 {}
public static class M7 {}
public static class M8 {}
@org.robovm.rt.bro.annotation.Marshaler(M1.class)
public static class C1 {
}
public static class C2 extends C1 {
public static class Inner {
public static class InnerInner {
}
}
}
@org.robovm.rt.bro.annotation.Marshaler(M2.class)
public interface I1 {}
@org.robovm.rt.bro.annotation.Marshaler(M3.class)
public interface I2 {}
@org.robovm.rt.bro.annotation.Marshaler(M4.class)
public interface I3 {}
@Marshalers({
@org.robovm.rt.bro.annotation.Marshaler(M5.class),
@org.robovm.rt.bro.annotation.Marshaler(M1.class)
})
public interface I4 {}
public interface I5 extends I3, I4 {}
@org.robovm.rt.bro.annotation.Marshaler(M6.class)
public interface I6 {}
public static class C3 extends C2 implements I1, I5 {
}
@org.robovm.rt.bro.annotation.Marshaler(M7.class)
public static class C4 extends C3 implements I2, I6 {
}
public static class Foo1 {
@Bridge
private static native String foo(String s);
}
@org.robovm.rt.bro.annotation.Marshaler(M1.class)
public static class Foo2 {
@Bridge
private static native String foo(String s);
}
@org.robovm.rt.bro.annotation.Marshaler(M2.class)
public static class Foo3 {
@Bridge
private static native @org.robovm.rt.bro.annotation.Marshaler(M1.class) String
foo(@org.robovm.rt.bro.annotation.Marshaler(M1.class) String s);
}
@org.robovm.rt.bro.annotation.Marshaler(M2.class)
public static class Foo4 {
@Bridge
private static native String foo1(String s);
@Bridge
private static native Integer foo2(Integer i);
@Bridge
private static native StringBuilder foo3(StringBuilder s);
@Bridge
private static native Double foo4(Double d);
@Callback
private static String foo5(String s) { return null; }
@Bridge
private static native String[] foo6(String[] s);
@Bridge
private static native Double[] foo7(Double[] s);
@Bridge
private static native StringBuilder[] foo8(StringBuilder[] s);
@Bridge
private static native CharSequence foo9(CharSequence s);
}
@org.robovm.rt.bro.annotation.Marshaler(M2.class)
public static class TestStruct extends Struct<TestStruct> {
@StructMember(0)
public native String getV1();
@StructMember(0)
public native void setV1(String s);
@StructMember(0)
public native @Array(10) byte[] getV2();
@StructMember(0)
public native void setV2(@Array(10) byte[] b);
@StructMember(0)
public native @Array({10, 20}) byte[][] getV3();
@StructMember(0)
public native void setV3(@Array({10, 20}) byte[][] b);
@StructMember(0)
public native @Array({10, 20, 30}) byte[][][] getV4();
@StructMember(0)
public native void setV4(@Array({10, 20, 30}) byte[][][] b);
@StructMember(0)
public native @Array(10) ByteBuffer getV5();
@StructMember(0)
public native void setV5(@Array(10) ByteBuffer b);
@StructMember(0)
public native @Array({10, 20}) ByteBuffer getV6();
@StructMember(0)
public native void setV6(@Array({10, 20}) ByteBuffer b);
}
public static class MockHome extends Home {
public MockHome(File homeDir) {
super(homeDir, false);
}
}
}