diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/BroMethodCompiler.java b/compiler/compiler/src/main/java/org/robovm/compiler/BroMethodCompiler.java index 684cb4fa0..1ed36372c 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/BroMethodCompiler.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/BroMethodCompiler.java @@ -734,7 +734,7 @@ private StructureType getStructType(SootClass clazz, boolean checkEmpty) { } // find out if regular structure is simple wrapper around 8, 16 or 32 int - // (this is needed to find out if this structure will be retured by value on arm7 cpus) + // (this is needed to find out if this structure will be returned by value on arm7 CPUs) boolean singleIntStruct = result.length == 1 && result[0] instanceof IntegerType && ((IntegerType)result[0]).getBits() <= 32; if (!singleIntStruct) { attributes |= StructureType.ATTR_NOT_SINGLE_INT_STRUCT; @@ -990,7 +990,20 @@ public Type getStructMemberType(SootMethod method) { return memberType; } - + + /** + * returns own members offsets in bytes from start of struct + */ + public int[] getStructMemberOffsets(StructureType structType) { + // get offset of each struct member by calling llvm api + int membersCount = structType.getTypeCount() - structType.getOwnMembersOffset(); + int offset = structType.getOwnMembersOffset(); // inherited struct (if any) goes as member 0, own members starts 1 + int[] offsets = new int[membersCount]; + for (int idx = 0; idx < membersCount; idx++) + offsets[idx] = config.getDataLayout().getOffsetOfElement(structType, offset + idx); + return offsets; + } + protected SootMethod createFakeStructRetMethod(SootMethod originalMethod) { // Create a new method with the same parameters but a @StructRet parameter inserted first @SuppressWarnings("unchecked") diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/ClassCompiler.java b/compiler/compiler/src/main/java/org/robovm/compiler/ClassCompiler.java index c7807ccc6..0d3c22e6b 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/ClassCompiler.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/ClassCompiler.java @@ -832,6 +832,9 @@ private void compile(Clazz clazz, OutputStream out) throws IOException { // method that will provide meta flags for runtime to help finding out if stret is required SootMethod stretMeta = new SootMethod(STRUCT_ATTRIBUTES_METHOD, Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE); sootClass.addMethod(stretMeta); + // method that returns offset of struct member + SootMethod offset = new SootMethod("offsetOf", Collections.singletonList(IntType.v()), IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE); + sootClass.addMethod(offset); } mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch()))); @@ -877,8 +880,8 @@ private void compile(Clazz clazz, OutputStream out) throws IOException { function = method(method); } else if (hasGlobalValueAnnotation(method)) { function = globalValueMethod(method); - } else if (isStruct(sootClass) && ("_sizeOf".equals(name) - || "sizeOf".equals(name) || STRUCT_ATTRIBUTES_METHOD.equals(name) || hasStructMemberAnnotation(method))) { + } else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) || + "offsetOf".equals(name) || STRUCT_ATTRIBUTES_METHOD.equals(name) || hasStructMemberAnnotation(method))) { function = structMember(method); } else if (method.isNative()) { function = nativeMethod(method); diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/Functions.java b/compiler/compiler/src/main/java/org/robovm/compiler/Functions.java index 1311203a3..af23d6b6e 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/Functions.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/Functions.java @@ -66,7 +66,8 @@ public class Functions { public static final FunctionRef BC_THROW_INCOMPATIBLE_CLASS_CHANGE_ERROR = new FunctionRef("_bcThrowIncompatibleClassChangeError", new FunctionType(VOID, ENV_PTR, I8_PTR)); public static final FunctionRef BC_THROW_ABSTRACT_METHOD_ERROR = new FunctionRef("_bcThrowAbstractMethodError", new FunctionType(VOID, ENV_PTR, I8_PTR)); public static final FunctionRef BC_THROW_CLASS_CAST_EXCEPTION_ARRAY = new FunctionRef("_bcThrowClassCastExceptionArray", new FunctionType(VOID, ENV_PTR, CLASS_PTR, OBJECT_PTR)); - + public static final FunctionRef BC_THROW_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = new FunctionRef("_bcThrowArrayIndexOutOfBoundsException", new FunctionType(VOID, ENV_PTR, I32, I32)); + public static final FunctionRef BC_NEW_BOOLEAN_ARRAY = new FunctionRef("_bcNewBooleanArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32)); public static final FunctionRef BC_NEW_BYTE_ARRAY = new FunctionRef("_bcNewByteArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32)); public static final FunctionRef BC_NEW_CHAR_ARRAY = new FunctionRef("_bcNewCharArray", new FunctionType(OBJECT_PTR, ENV_PTR, I32)); diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/StructMemberMethodCompiler.java b/compiler/compiler/src/main/java/org/robovm/compiler/StructMemberMethodCompiler.java index 5124c06c0..c3c32ed54 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/StructMemberMethodCompiler.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/StructMemberMethodCompiler.java @@ -24,17 +24,21 @@ import org.robovm.compiler.Bro.MarshalerFlags; import org.robovm.compiler.clazz.Clazz; import org.robovm.compiler.config.Config; +import org.robovm.compiler.llvm.BasicBlockRef; import org.robovm.compiler.llvm.Bitcast; import org.robovm.compiler.llvm.Function; import org.robovm.compiler.llvm.Getelementptr; import org.robovm.compiler.llvm.IntegerConstant; import org.robovm.compiler.llvm.Inttoptr; +import org.robovm.compiler.llvm.Label; import org.robovm.compiler.llvm.Load; import org.robovm.compiler.llvm.PackedStructureType; import org.robovm.compiler.llvm.PointerType; import org.robovm.compiler.llvm.Ret; import org.robovm.compiler.llvm.StructureType; +import org.robovm.compiler.llvm.Switch; import org.robovm.compiler.llvm.Type; +import org.robovm.compiler.llvm.Unreachable; import org.robovm.compiler.llvm.Value; import org.robovm.compiler.llvm.Variable; import org.robovm.compiler.llvm.VariableRef; @@ -43,6 +47,9 @@ import soot.SootMethod; import soot.VoidType; +import java.util.HashMap; +import java.util.Map; + /** * @author niklas * @@ -68,6 +75,8 @@ public void reset(Clazz clazz) { protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) { if ("_sizeOf".equals(method.getName()) || "sizeOf".equals(method.getName())) { return structSizeOf(moduleBuilder, method); + } else if ("offsetOf".equals(method.getName())) { + return structOffsetOf(moduleBuilder, method); } else if (STRUCT_ATTRIBUTES_METHOD.equals(method.getName())) { return stretMeta(moduleBuilder, method); } else { @@ -82,6 +91,39 @@ private Function structSizeOf(ModuleBuilder moduleBuilder, SootMethod method) { return fn; } + private Function structOffsetOf(ModuleBuilder moduleBuilder, SootMethod method) { + Function fn = createMethodFunction(method); + moduleBuilder.addFunction(fn); + + int[] offsets = getStructMemberOffsets(structType); + if (offsets.length > 0 ) { + // function code -- basic switch of returns + Label[] switchLabels = new Label[offsets.length]; + Map targets = new HashMap(); + for (int idx = 0; idx < offsets.length; idx++) { + targets.put(new IntegerConstant(idx), fn.newBasicBlockRef(switchLabels[idx] = new Label(idx))); + } + + Value idxValue = fn.getParameterRef(1); // 'env' is parameter 0 + Label def = new Label(-1); + fn.add(new Switch(idxValue, fn.newBasicBlockRef(def), targets)); + + // cases + for (int idx = 0; idx < offsets.length; idx++) { + fn.newBasicBlock(switchLabels[idx]); + fn.add(new Ret(new IntegerConstant(offsets[idx]))); + } + + // default case -- array out of bounds exception + fn.newBasicBlock(def); + } + Functions.call(fn, Functions.BC_THROW_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION, fn.getParameterRef(0), + new IntegerConstant(offsets.length), fn.getParameterRef(1)); + fn.add(new Unreachable()); + + return fn; + } + private Function stretMeta(ModuleBuilder moduleBuilder, SootMethod method) { Function fn = createMethodFunction(method); moduleBuilder.addFunction(fn); diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/TrampolineCompiler.java b/compiler/compiler/src/main/java/org/robovm/compiler/TrampolineCompiler.java index 2f53c3260..fa5d11ce2 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/TrampolineCompiler.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/TrampolineCompiler.java @@ -592,6 +592,13 @@ private SootMethod resolveMethod(SootClass clazz, String name, String desc) { method.setDeclared(true); return method; } + if (name.equals("offsetOf") && isStruct(clazz)) { + method = new SootMethod("offsetOf", Collections.singletonList(IntType.v()), IntType.v(), + Modifier.PUBLIC | Modifier.STATIC); + method.setDeclaringClass(clazz); + method.setDeclared(true); + return method; + } SootClass c = !clazz.isInterface() && clazz.hasSuperclass() ? clazz.getSuperclass() : null; while (c != null) { diff --git a/compiler/compiler/src/main/java/org/robovm/compiler/llvm/DataLayout.java b/compiler/compiler/src/main/java/org/robovm/compiler/llvm/DataLayout.java index 3ce31a721..bb9b3f93b 100755 --- a/compiler/compiler/src/main/java/org/robovm/compiler/llvm/DataLayout.java +++ b/compiler/compiler/src/main/java/org/robovm/compiler/llvm/DataLayout.java @@ -99,7 +99,15 @@ Integer doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type) { } }); } - + + public int getOffsetOfElement(StructureType type, int idx) { + return runTypeQuery(type, new TypeCallback() { + Integer doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type) { + return (int) targetMachine.getDataLayout().getOffsetOfElement(type, idx); + } + }); + } + private static abstract class TypeCallback { abstract T doWithType(TargetMachine targetMachine, org.robovm.llvm.Type type); } diff --git a/compiler/compiler/src/test/java/org/robovm/compiler/StructMemberMethodCompilerTest.java b/compiler/compiler/src/test/java/org/robovm/compiler/StructMemberMethodCompilerTest.java index d76fe91f2..42b5b33f5 100755 --- a/compiler/compiler/src/test/java/org/robovm/compiler/StructMemberMethodCompilerTest.java +++ b/compiler/compiler/src/test/java/org/robovm/compiler/StructMemberMethodCompilerTest.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.IOException; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; /** @@ -111,6 +112,15 @@ public void testPackedStructures() { assertEquals(config.getDataLayout().getStoreSize(packed), 12); } + @Test + public void testStructMemberOffsets() { + StructMemberMethodCompiler compiler = new StructMemberMethodCompiler(config); + + StructureType packed = compiler.getStructType(toSootClass(StructForOffsetsPacked.class)); + int[] offsets = compiler.getStructMemberOffsets(packed); + assertArrayEquals(new int[]{0, 1, 3}, offsets); + } + @Test public void testBlockMember() { Clazz clz = toClazz(StructWithBlock.class); @@ -181,6 +191,13 @@ public static class StructWithBlock extends Struct { @StructMember(0) public native @Block Runnable getA(); } + @Packed(1) + public static class StructForOffsetsPacked extends Struct { + @StructMember(0) public native byte getA(); + @StructMember(1) public native short getB(); + @StructMember(2) public native int getC(); + } + public static class MockHome extends Home { public MockHome(File homeDir) { super(homeDir, false); diff --git a/compiler/llvm/src/main/java/org/robovm/llvm/DataLayout.java b/compiler/llvm/src/main/java/org/robovm/llvm/DataLayout.java index a660b4a16..56da6ae35 100755 --- a/compiler/llvm/src/main/java/org/robovm/llvm/DataLayout.java +++ b/compiler/llvm/src/main/java/org/robovm/llvm/DataLayout.java @@ -61,6 +61,11 @@ public long getTypeStoreSize(Type type) { return LLVM.StoreSizeOfType(ref, type.ref).longValue(); } + public long getOffsetOfElement(Type type, int idx) { + checkDisposed(); + return LLVM.OffsetOfElement(ref, type.ref, idx).longValue(); + } + @Override public int hashCode() { final int prime = 31;