Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2012 Trillian Mobile 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 .
*/
package org.robovm.compiler;
import static org.robovm.compiler.Annotations.*;
import static org.robovm.compiler.Bro.*;
import static org.robovm.compiler.Functions.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.Type.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.robovm.compiler.Annotations.Visibility;
import org.robovm.compiler.MarshalerLookup.ArrayMarshalerMethod;
import org.robovm.compiler.MarshalerLookup.MarshalSite;
import org.robovm.compiler.MarshalerLookup.MarshalerMethod;
import org.robovm.compiler.MarshalerLookup.ValueMarshalerMethod;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.llvm.AggregateType;
import org.robovm.compiler.llvm.Alloca;
import org.robovm.compiler.llvm.ArrayType;
import org.robovm.compiler.llvm.Bitcast;
import org.robovm.compiler.llvm.ConstantBitcast;
import org.robovm.compiler.llvm.DataLayout;
import org.robovm.compiler.llvm.Fpext;
import org.robovm.compiler.llvm.Fptrunc;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.FunctionRef;
import org.robovm.compiler.llvm.FunctionType;
import org.robovm.compiler.llvm.GlobalRef;
import org.robovm.compiler.llvm.IntegerConstant;
import org.robovm.compiler.llvm.IntegerType;
import org.robovm.compiler.llvm.Inttoptr;
import org.robovm.compiler.llvm.Load;
import org.robovm.compiler.llvm.PointerType;
import org.robovm.compiler.llvm.PrimitiveType;
import org.robovm.compiler.llvm.Ptrtoint;
import org.robovm.compiler.llvm.Sext;
import org.robovm.compiler.llvm.Store;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Trunc;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.llvm.Variable;
import org.robovm.compiler.llvm.Zext;
import org.robovm.compiler.trampoline.Invokestatic;
import org.robovm.compiler.trampoline.LdcClass;
import org.robovm.compiler.trampoline.Trampoline;
import soot.DoubleType;
import soot.FloatType;
import soot.LongType;
import soot.PrimType;
import soot.RefType;
import soot.SootClass;
import soot.SootMethod;
import soot.VoidType;
/**
*
*/
public abstract class BroMethodCompiler extends AbstractMethodCompiler {
private final List cWrapperFunctions = new ArrayList<>();
public BroMethodCompiler(Config config) {
super(config);
}
@Override
public void reset(Clazz clazz) {
cWrapperFunctions.clear();
super.reset(clazz);
}
public List getCWrapperFunctions() {
return cWrapperFunctions;
}
protected boolean requiresCWrapper(SootMethod method) {
if (isPassByValue(method)) {
return true;
}
if (hasVariadicAnnotation(method)) {
return true;
}
for (int i = 0; i < method.getParameterCount(); i++) {
if (isPassByValue(method, i)) {
return true;
}
}
return false;
}
protected Value ldcClass(Function fn, String name, Value env) {
if (isArray(name) && isPrimitiveBaseType(name)) {
String primitiveDesc = name.substring(name.length() - 1);
Variable result = fn.newVariable(OBJECT_PTR);
fn.add(new Load(result, new ConstantBitcast(
new GlobalRef("array_" + primitiveDesc, CLASS_PTR), new PointerType(OBJECT_PTR))));
return result.ref();
} else {
FunctionRef ldcClassFn = null;
if (name.equals(this.className)) {
ldcClassFn = FunctionBuilder.ldcInternal(this.className).ref();
} else {
Trampoline trampoline = new LdcClass(this.className, name);
trampolines.add(trampoline);
ldcClassFn = trampoline.getFunctionRef();
}
return call(fn, ldcClassFn, env);
}
}
protected Value marshalNativeToPrimitive(Function fn, SootMethod method, int paramIndex, Value value) {
soot.Type type = method.getParameterType(paramIndex);
if (hasPointerAnnotation(method, paramIndex)) {
value = marshalPointerToLong(fn, value);
} else if (hasMachineSizedFloatAnnotation(method, paramIndex) && type.equals(DoubleType.v())) {
value = marshalMachineSizedFloatToDouble(fn, value);
} else if (hasMachineSizedFloatAnnotation(method, paramIndex) && type.equals(FloatType.v())) {
value = marshalMachineSizedFloatToFloat(fn, value);
} else if (hasMachineSizedSIntAnnotation(method, paramIndex) && type.equals(LongType.v())) {
value = marshalMachineSizedSIntToLong(fn, value);
} else if (hasMachineSizedUIntAnnotation(method, paramIndex) && type.equals(LongType.v())) {
value = marshalMachineSizedUIntToLong(fn, value);
}
return value;
}
protected Value marshalPrimitiveToNative(Function fn, SootMethod method, int paramIndex, Value value) {
soot.Type type = method.getParameterType(paramIndex);
if (hasPointerAnnotation(method, paramIndex)) {
value = marshalLongToPointer(fn, value);
} else if (hasMachineSizedFloatAnnotation(method, paramIndex) && type.equals(DoubleType.v())) {
value = marshalDoubleToMachineSizedFloat(fn, value);
} else if (hasMachineSizedFloatAnnotation(method, paramIndex) && type.equals(FloatType.v())) {
value = marshalFloatToMachineSizedFloat(fn, value);
} else if (hasMachineSizedSIntAnnotation(method, paramIndex) && type.equals(LongType.v())) {
value = marshalLongToMachineSizedInt(fn, value);
} else if (hasMachineSizedUIntAnnotation(method, paramIndex) && type.equals(LongType.v())) {
value = marshalLongToMachineSizedInt(fn, value);
}
return value;
}
protected Value marshalNativeToPrimitive(Function fn, SootMethod method, Value value) {
soot.Type type = method.getReturnType();
if (hasPointerAnnotation(method)) {
value = marshalPointerToLong(fn, value);
} else if (hasMachineSizedFloatAnnotation(method) && type.equals(DoubleType.v())) {
value = marshalMachineSizedFloatToDouble(fn, value);
} else if (hasMachineSizedFloatAnnotation(method) && type.equals(FloatType.v())) {
value = marshalMachineSizedFloatToFloat(fn, value);
} else if (hasMachineSizedSIntAnnotation(method) && type.equals(LongType.v())) {
value = marshalMachineSizedSIntToLong(fn, value);
} else if (hasMachineSizedUIntAnnotation(method) && type.equals(LongType.v())) {
value = marshalMachineSizedUIntToLong(fn, value);
}
return value;
}
protected Value marshalPrimitiveToNative(Function fn, SootMethod method, Value value) {
soot.Type type = method.getReturnType();
if (hasPointerAnnotation(method)) {
value = marshalLongToPointer(fn, value);
} else if (hasMachineSizedFloatAnnotation(method) && type.equals(DoubleType.v())) {
value = marshalDoubleToMachineSizedFloat(fn, value);
} else if (hasMachineSizedFloatAnnotation(method) && type.equals(FloatType.v())) {
value = marshalFloatToMachineSizedFloat(fn, value);
} else if (hasMachineSizedSIntAnnotation(method) && type.equals(LongType.v())) {
value = marshalLongToMachineSizedInt(fn, value);
} else if (hasMachineSizedUIntAnnotation(method) && type.equals(LongType.v())) {
value = marshalLongToMachineSizedInt(fn, value);
}
return value;
}
public static class MarshaledArg {
public Value object;
public Value handle;
public int paramIndex;
}
protected Value marshalNativeToObject(Function fn, MarshalerMethod marshalerMethod,
MarshaledArg marshaledArg, Value env, String valueClassName,
Value nativeValue, long flags) {
if (nativeValue.getType() instanceof StructureType) {
nativeValue = createStackCopy(fn, nativeValue);
}
Invokestatic invokestatic = marshalerMethod.getInvokeStatic(
sootMethod.getDeclaringClass());
trampolines.add(invokestatic);
Value valueClass = ldcClass(fn, valueClassName, env);
Variable handle = fn.newVariable(I64);
fn.add(new Ptrtoint(handle, nativeValue, I64));
Value object = call(fn, invokestatic.getFunctionRef(),
env, valueClass, handle.ref(), new IntegerConstant(flags));
if (marshaledArg != null) {
marshaledArg.handle = handle.ref();
marshaledArg.object = object;
}
return object;
}
protected Value createStackCopy(Function fn, Value value) {
Variable stackCopy = fn.newVariable(new PointerType(value.getType()));
fn.add(new Alloca(stackCopy, value.getType()));
fn.add(new Store(value, stackCopy.ref()));
return stackCopy.ref();
}
protected Value marshalNativeToValueObject(Function fn, MarshalerMethod marshalerMethod, Value env,
String valueClassName, Value nativeValue, long flags) {
Invokestatic invokeToObject = marshalerMethod.getInvokeStatic(
sootMethod.getDeclaringClass());
trampolines.add(invokeToObject);
Value valueClass = ldcClass(fn, valueClassName, env);
nativeValue = marshalNativeToPrimitive(fn, marshalerMethod.getMethod(), 1, nativeValue);
return call(fn, invokeToObject.getFunctionRef(), env, valueClass, nativeValue, new IntegerConstant(flags));
}
private List arrayDimensionsValues(int[] dimensions) {
List l = new ArrayList<>();
for (int i = 0; i < dimensions.length; i++) {
l.add(new IntegerConstant(dimensions[i]));
}
return l;
}
protected Value marshalNativeToArray(Function fn, MarshalerMethod marshalerMethod, Value env,
String arrayClassName, Value nativeValue, long flags, int[] dimensions) {
Invokestatic invokeToObject = marshalerMethod.getInvokeStatic(sootMethod.getDeclaringClass());
trampolines.add(invokeToObject);
Variable handle = fn.newVariable(I64);
fn.add(new Ptrtoint(handle, nativeValue, I64));
Value valueClass = ldcClass(fn, arrayClassName, env);
List args = new ArrayList<>();
args.add(env);
args.add(valueClass);
args.add(handle.ref());
args.add(new IntegerConstant(flags));
args.addAll(arrayDimensionsValues(dimensions));
return call(fn, invokeToObject.getFunctionRef(), args);
}
protected Value marshalPointerToLong(Function fn, Value pointer) {
Variable result = fn.newVariable(I64);
fn.add(new Ptrtoint(result, pointer, I64));
return result.ref();
}
protected Value marshalMachineSizedSIntToLong(Function fn, Value value) {
if (config.getArch().is32Bit()) {
Variable result = fn.newVariable(I64);
fn.add(new Sext(result, value, I64));
return result.ref();
} else {
return value;
}
}
protected Value marshalMachineSizedUIntToLong(Function fn, Value value) {
if (config.getArch().is32Bit()) {
Variable result = fn.newVariable(I64);
fn.add(new Zext(result, value, I64));
return result.ref();
} else {
return value;
}
}
protected Value marshalMachineSizedFloatToDouble(Function fn, Value value) {
if (config.getArch().is32Bit()) {
Variable result = fn.newVariable(DOUBLE);
fn.add(new Fpext(result, value, DOUBLE));
return result.ref();
} else {
return value;
}
}
protected Value marshalMachineSizedFloatToFloat(Function fn, Value value) {
if (!config.getArch().is32Bit()) {
Variable result = fn.newVariable(FLOAT);
fn.add(new Fptrunc(result, value, FLOAT));
return result.ref();
} else {
return value;
}
}
protected Value marshalObjectToNative(Function fn, MarshalerMethod marshalerMethod, MarshaledArg marshaledArg,
Type nativeType, Value env, Value object, long flags) {
Invokestatic invokestatic = marshalerMethod.getInvokeStatic(sootMethod.getDeclaringClass());
trampolines.add(invokestatic);
Value handle = call(fn, invokestatic.getFunctionRef(),
env, object, new IntegerConstant(flags));
Variable nativeValue = fn.newVariable(nativeType);
if (nativeType instanceof StructureType || nativeType instanceof ArrayType) {
Variable tmp = fn.newVariable(new PointerType(nativeType));
fn.add(new Inttoptr(tmp, handle, tmp.getType()));
fn.add(new Load(nativeValue, tmp.ref()));
} else {
fn.add(new Inttoptr(nativeValue, handle, nativeType));
}
if (marshaledArg != null) {
marshaledArg.handle = handle;
marshaledArg.object = object;
}
return nativeValue.ref();
}
protected Value marshalValueObjectToNative(Function fn, MarshalerMethod marshalerMethod,
Type nativeType, Value env, Value object, long flags) {
Invokestatic invokestatic = marshalerMethod.getInvokeStatic(
sootMethod.getDeclaringClass());
trampolines.add(invokestatic);
Value result = call(fn, invokestatic.getFunctionRef(), env, object, new IntegerConstant(flags));
return marshalPrimitiveToNative(fn, marshalerMethod.getMethod(), result);
}
protected void marshalArrayToNative(Function fn, MarshalerMethod marshalerMethod,
Value env, Value object, Value destPtr, long flags, int[] dimensions) {
Invokestatic invokestatic = marshalerMethod.getInvokeStatic(sootMethod.getDeclaringClass());
trampolines.add(invokestatic);
Variable handle = fn.newVariable(I64);
fn.add(new Ptrtoint(handle, destPtr, I64));
List args = new ArrayList<>();
args.add(env);
args.add(object);
args.add(handle.ref());
args.add(new IntegerConstant(flags));
args.addAll(arrayDimensionsValues(dimensions));
call(fn, invokestatic.getFunctionRef(), args);
}
protected Value marshalLongToPointer(Function fn, Value handle) {
Variable result = fn.newVariable(I8_PTR);
fn.add(new Inttoptr(result, handle, I8_PTR));
return result.ref();
}
protected Value marshalLongToMachineSizedInt(Function fn, Value value) {
if (config.getArch().is32Bit()) {
Variable result = fn.newVariable(I32);
fn.add(new Trunc(result, value, I32));
return result.ref();
} else {
return value;
}
}
protected Value marshalDoubleToMachineSizedFloat(Function fn, Value value) {
if (config.getArch().is32Bit()) {
Variable result = fn.newVariable(FLOAT);
fn.add(new Fptrunc(result, value, FLOAT));
return result.ref();
} else {
return value;
}
}
protected Value marshalFloatToMachineSizedFloat(Function fn, Value value) {
if (!config.getArch().is32Bit()) {
Variable result = fn.newVariable(DOUBLE);
fn.add(new Fpext(result, value, DOUBLE));
return result.ref();
} else {
return value;
}
}
private Type getReturnType(String anno, SootMethod method) {
soot.Type sootType = method.getReturnType();
if (hasPointerAnnotation(method)) {
if (!sootType.equals(LongType.v())) {
throw new IllegalArgumentException(anno + " annotated method "
+ method + " must return long when annotated with @Pointer");
}
return I8_PTR;
}
if (hasMachineSizedFloatAnnotation(method)) {
if (!sootType.equals(DoubleType.v()) && !sootType.equals(FloatType.v())) {
throw new IllegalArgumentException(anno + " annotated method "
+ method + " must return float or double when annotated with @MachineSizedFloat");
}
return config.getArch().is32Bit() ? FLOAT : DOUBLE;
}
if (hasMachineSizedSIntAnnotation(method) || hasMachineSizedUIntAnnotation(method)) {
if (!sootType.equals(LongType.v())) {
throw new IllegalArgumentException(anno + " annotated method "
+ method + " must return long when annotated with @MachineSizedSInt or @MachineSizedUInt");
}
return config.getArch().is32Bit() ? I32 : I64;
}
if (isStruct(sootType)) {
if (!isPassByValue(method)) {
// Structs are returned by reference by default
return new PointerType(getStructType(sootType));
}
return getStructType(sootType);
} else if (isNativeObject(sootType)) {
// NativeObjects are always returned by reference.
return I8_PTR;
} else if (sootType instanceof PrimType || sootType == VoidType.v()) {
return getType(sootType);
}
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
if (marshalerMethod instanceof ValueMarshalerMethod) {
return ((ValueMarshalerMethod) marshalerMethod).getNativeType(config.getArch());
} else {
return I8_PTR;
}
}
private Type getParameterType(String anno, SootMethod method, int i) {
soot.Type sootType = method.getParameterType(i);
if (hasPointerAnnotation(method, i)) {
if (!sootType.equals(LongType.v())) {
throw new IllegalArgumentException("Parameter " + (i + 1)
+ " of " + anno + " annotated method " + method
+ " must be of type long when annotated with @Pointer.");
}
return I8_PTR;
}
if (hasMachineSizedFloatAnnotation(method, i)) {
if (!sootType.equals(DoubleType.v()) && !sootType.equals(FloatType.v())) {
throw new IllegalArgumentException("Parameter " + (i + 1)
+ " of " + anno + " annotated method " + method
+ " must be of type float or double when annotated with @MachineSizedFloat.");
}
return config.getArch().is32Bit() ? FLOAT : DOUBLE;
}
if (hasMachineSizedSIntAnnotation(method, i) || hasMachineSizedUIntAnnotation(method, i)) {
if (!sootType.equals(LongType.v())) {
throw new IllegalArgumentException("Parameter " + (i + 1)
+ " of " + anno + " annotated method " + method
+ " must be of type long when annotated with "
+ "@MachineSizedSInt or @MachineSizedUInt");
}
return config.getArch().is32Bit() ? I32 : I64;
}
if (hasStructRetAnnotation(method, i)) {
if (i > 0) {
throw new IllegalArgumentException("Parameter " + (i + 1)
+ " of " + anno + " annotated method " + method
+ " cannot be annotated with @StructRet. Only the first"
+ " parameter may have this annotation.");
}
if (!isStruct(sootType)) {
throw new IllegalArgumentException("Parameter " + (i + 1)
+ " of " + anno + " annotated method " + method
+ " must be a sub class of Struct when annotated with @StructRet.");
}
// @StructRet implies pass by reference
return new PointerType(getStructType(sootType));
}
if (isStruct(sootType)) {
StructureType structType = getStructType(sootType);
if (hasByValAnnotation(method, i)) {
return getStructType(sootType);
}
return new PointerType(structType);
} else if (isNativeObject(sootType)) {
// NativeObjects are always passed by reference.
return I8_PTR;
} else if (sootType instanceof PrimType) {
return getType(sootType);
}
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, i));
if (marshalerMethod instanceof ValueMarshalerMethod) {
return ((ValueMarshalerMethod) marshalerMethod).getNativeType(config.getArch());
} else {
return I8_PTR;
}
}
public FunctionType getBridgeFunctionType(SootMethod method, boolean dynamic, boolean considerVariadic) {
return getBridgeOrCallbackFunctionType("@Bridge", method, dynamic, considerVariadic);
}
public FunctionType getCallbackFunctionType(SootMethod method, boolean considerVariadic) {
return getBridgeOrCallbackFunctionType("@Callback", method, false, considerVariadic);
}
private FunctionType getBridgeOrCallbackFunctionType(String anno, SootMethod method, boolean dynamic, boolean considerVariadic) {
Type returnType = getReturnType(anno, method);
boolean varargs = considerVariadic && hasVariadicAnnotation(method);
int variadicIndex = varargs ? getVariadicParameterIndex(method) : Integer.MAX_VALUE;
List paramTypes = new ArrayList<>();
for (int i = dynamic ? 1 : 0; i < method.getParameterCount(); i++) {
if (i == variadicIndex) {
break;
}
paramTypes.add(getParameterType(anno, method, i));
}
if (!method.isStatic()) {
int idx = hasStructRetAnnotation(method, 0) ? 1 : 0;
soot.Type sootType = method.getDeclaringClass().getType();
if (isStruct(sootType)) {
paramTypes.add(idx, new PointerType(getStructType(sootType)));
} else if (isNativeObject(sootType)) {
// NativeObjects are always passed by reference.
paramTypes.add(idx, I8_PTR);
} else {
throw new IllegalArgumentException("Receiver of non static "
+ anno + " method " + method
+ " must either be a Struct or a NativeObject");
}
}
return new FunctionType(returnType, varargs, paramTypes.toArray(new Type[paramTypes.size()]));
}
public StructureType getStructType(soot.Type t) {
return getStructType(((RefType) t).getSootClass());
}
public StructureType getStructType(SootClass clazz) {
return getStructType(clazz, true);
}
private StructureType getStructType(SootClass clazz, boolean checkEmpty) {
int n = 0;
for (SootMethod method : clazz.getMethods()) {
n = Math.max(getStructMemberOffset(method) + 1, n);
}
Type[] result = new Type[n + 1];
StructureType superType = null;
if (clazz.hasSuperclass()) {
SootClass superclass = clazz.getSuperclass();
if (!superclass.getName().equals("org.robovm.rt.bro.Struct")) {
superType = getStructType(superclass, false);
}
}
result[0] = superType != null ? superType : new StructureType();
for (SootMethod method : clazz.getMethods()) {
int offset = getStructMemberOffset(method);
if (offset != -1) {
if (!method.isNative() && !method.isStatic()) {
throw new IllegalArgumentException("@StructMember annotated method "
+ method + " must be native and not static");
}
Type type = null;
if (method.getParameterCount() == 0) {
soot.Type sootType = method.getReturnType();
// Possibly a getter
if (hasPointerAnnotation(method) && !sootType.equals(LongType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated getter " + method
+ " must be of type long when annotated with @Pointer");
}
if (hasMachineSizedFloatAnnotation(method) && !sootType.equals(DoubleType.v()) && !sootType.equals(FloatType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated getter " + method
+ " must be of type float or double when annotated with @MachineSizedFloat");
}
if ((hasMachineSizedSIntAnnotation(method) || hasMachineSizedUIntAnnotation(method)) && !sootType.equals(LongType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated getter " + method
+ " must be of type long when annotated with @MachineSizedSInt or @MachineSizedUInt");
}
if (sootType instanceof soot.ArrayType && !hasArrayAnnotation(method)) {
throw new IllegalArgumentException("@Array annotation expected on struct member getter " + method);
}
} else if (method.getParameterCount() == 1) {
soot.Type sootType = method.getParameterType(0);
if (hasPointerAnnotation(method, 0) && !sootType.equals(LongType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated setter " + method
+ " must be of type long when annotated with @Pointer");
}
if (hasMachineSizedFloatAnnotation(method, 0) && !sootType.equals(DoubleType.v()) && !sootType.equals(FloatType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated setter " + method
+ " must be of type float or double when annotated with @MachineSizedFloat");
}
if ((hasMachineSizedSIntAnnotation(method, 0) || hasMachineSizedUIntAnnotation(method)) && !sootType.equals(LongType.v())) {
throw new IllegalArgumentException("@StructMember(" + offset + ") annotated setter " + method
+ " must be of type long when annotated with @MachineSizedSInt or @MachineSizedUInt");
}
if (sootType instanceof soot.ArrayType && !hasArrayAnnotation(method, 0)) {
throw new IllegalArgumentException("@Array annotation expected on first parameter of struct member setter " + method);
}
soot.Type retType = method.getReturnType();
// The return type of the setter must be void or this
if (!retType.equals(VoidType.v()) && !(retType instanceof RefType && ((RefType) retType).getSootClass().equals(clazz))) {
throw new IllegalArgumentException("Setter " + method +" for "
+ "@StructMember(" + offset + ") "
+ " must either return nothing or return a " + clazz);
}
} else {
throw new IllegalArgumentException("@StructMember annotated method "
+ method + " has too many parameters");
}
type = getStructMemberType(method);
int index = offset + 1;
if (result[index] == null) {
result[index] = type;
} else if (type != result[index]) {
// Two members mapped to the same offset (union). Pick
// the type with the largest alignment and pad with bytes
// up to the largest size.
result[index] = mergeStructMemberTypes(config.getDataLayout(), type, result[index]);
}
}
}
for (int i = 1; i < result.length; i++) {
if (result[i] == null) {
throw new IllegalArgumentException("No @StructMember(" + i
+ ") defined in class " + clazz);
}
}
if (!clazz.isAbstract() && checkEmpty && n == 0 && superType == null) {
throw new IllegalArgumentException("Struct class " + clazz + " has no @StructMember annotated methods");
}
return new StructureType(result);
}
static Type mergeStructMemberTypes(DataLayout dataLayout, Type t1, Type t2) {
int align1 = dataLayout.getAlignment(t1);
int align2 = dataLayout.getAlignment(t2);
int size1 = dataLayout.getStoreSize(t1);
int size2 = dataLayout.getStoreSize(t2);
Type result = align1 < align2 ? t2 : t1;
int size = align1 < align2 ? size2 : size1;
int pad = Math.max(size1, size2) - size;
if (pad > 0) {
return new StructureType(result, new ArrayType(pad, I8));
} else {
return result;
}
}
protected static String getHiType(Type type) {
if (type == Type.VOID) {
return "void";
}
if (type instanceof PointerType || type instanceof AggregateType) {
return "void*";
} else if (type instanceof IntegerType) {
switch (((IntegerType) type).getBits()) {
case 8:
return "char";
case 16:
return "short";
case 32:
return "int";
case 64:
return "long long";
}
} else if (type == Type.FLOAT) {
return "float";
} else if (type == Type.DOUBLE) {
return "double";
}
throw new IllegalArgumentException("Cannot convert type " + type + " to C type");
}
protected static String getLoType(final Type type, String base, int index, Map structs) {
if (type instanceof StructureType) {
StringBuilder sb = new StringBuilder();
StructureType st = (StructureType) type;
sb.append("{");
String name = String.format("%s_%04d", base, index);
for (int i = 0; i < st.getTypeCount(); i++) {
Type t = st.getTypeAt(i);
if (i == 0 && t instanceof StructureType) {
if (((StructureType) t).getTypeCount() == 0) {
// Skip empty structs as first member
continue;
}
}
// Only support arrays embedded in structs
StringBuilder dims = new StringBuilder();
while (t instanceof ArrayType) {
ArrayType at = (ArrayType) t;
dims.append('[').append(at.getSize()).append(']');
t = ((ArrayType) t).getElementType();
}
sb.append(getLoType(t, name, i, structs)).append(" m" + i).append(dims).append(";");
}
sb.append("}");
structs.put(name, sb.toString());
return "struct " + name;
} else {
return getHiType(type);
}
}
public Type getStructMemberType(SootMethod method) {
String methodType = hasStructMemberAnnotation(method) ? "@StructMember" : "@GlobalValue";
SootMethod getter = method.getParameterCount() == 0 ? method : null;
SootMethod setter = getter == null ? method: null;
soot.Type type = getter != null
? getter.getReturnType() : setter.getParameterType(0);
Type memberType = null;
if (getter != null && hasPointerAnnotation(getter) || setter != null && hasPointerAnnotation(setter, 0)) {
memberType = I8_PTR;
} else if (getter != null && hasMachineSizedFloatAnnotation(getter) || setter != null && hasMachineSizedFloatAnnotation(setter, 0)) {
memberType = config.getArch().is32Bit() ? FLOAT : DOUBLE;
} else if (getter != null && (hasMachineSizedSIntAnnotation(getter) || hasMachineSizedUIntAnnotation(getter))
|| setter != null && (hasMachineSizedSIntAnnotation(setter, 0) || hasMachineSizedUIntAnnotation(setter, 0))) {
memberType = config.getArch().is32Bit() ? I32 : I64;
} else if (type instanceof PrimType) {
memberType = getType(type);
} else if (getter != null && hasArrayAnnotation(getter) || setter != null && hasArrayAnnotation(setter, 0)) {
int[] dimensions = getter != null ? getArrayDimensions(getter) : getArrayDimensions(setter, 0);
if (dimensions == null || dimensions.length == 0) {
throw new IllegalArgumentException("No dimensions specified for @Array annotation on " + methodType + " "
+ (getter != null ? "getter" : "setter") + " " + method);
}
if (type instanceof soot.ArrayType && ((soot.ArrayType) type).numDimensions != dimensions.length) {
throw new IllegalArgumentException("Mismatch in number of dimennsions for @Array annotation "
+ "and type on " + methodType + " "
+ (getter != null ? "getter" : "setter") + " " + method);
}
Type baseType = null;
if (type instanceof soot.ArrayType) {
soot.ArrayType arrayType = (soot.ArrayType) type;
if (isStruct(arrayType.baseType)) {
// ByVal is implied for arrays of structs
try {
baseType = getStructType(arrayType.baseType);
} catch (StackOverflowError e) {
throw new IllegalArgumentException("Struct type " + type + " refers to itself");
}
} else {
baseType = getType(arrayType.baseType);
}
} else if (isStruct(type)) {
// ByVal is implied
try {
baseType = getStructType(type);
} catch (StackOverflowError e) {
throw new IllegalArgumentException("Struct type " + type + " refers to itself");
}
} else if (type instanceof RefType) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup()
.findMarshalerMethod(getter != null ? new MarshalSite(getter) : new MarshalSite(setter, 0));
baseType = getType(((ArrayMarshalerMethod) marshalerMethod).getBaseType());
}
if (baseType == null) {
throw new IllegalArgumentException("Arrays of " + type + " is not supported");
}
long total = dimensions[0];
for (int i = 1; i < dimensions.length; i++) {
total *= dimensions[i];
}
memberType = new ArrayType(total, baseType);
} else if (isStruct(type)) {
boolean byVal = getter != null ? isPassByValue(getter) : isPassByValue(setter, 0);
if (!byVal) {
// NOTE: We use i8* instead of * to support pointers to recursive structs
memberType = I8_PTR;
} else {
try {
memberType = getStructType(type);
} catch (StackOverflowError e) {
throw new IllegalArgumentException("Struct type " + type + " refers to itself");
}
}
} else if (isNativeObject(type)) {
memberType = I8_PTR;
} else {
MarshalerMethod marshalerMethod = config.getMarshalerLookup()
.findMarshalerMethod(getter != null ? new MarshalSite(getter) : new MarshalSite(setter, 0));
if (marshalerMethod instanceof ValueMarshalerMethod) {
memberType = ((ValueMarshalerMethod) marshalerMethod).getNativeType(config.getArch());
} else {
memberType = I8_PTR;
}
}
return memberType;
}
protected SootMethod createFakeStructRetMethod(SootMethod originalMethod) {
// Create a new method with the same parameters but a @StructRet parameter inserted first
@SuppressWarnings("unchecked")
ArrayList newParameterTypes = new ArrayList(originalMethod.getParameterTypes());
newParameterTypes.add(0, originalMethod.getReturnType());
SootMethod method = new SootMethod(originalMethod.getName(), newParameterTypes, VoidType.v(), originalMethod.getModifiers());
method.setDeclaringClass(originalMethod.getDeclaringClass());
method.setDeclared(true);
// Copy all annotations from the original method
copyAnnotations(originalMethod, method, Visibility.Any);
// Copy all parameter annotations from the original method to the copy shifting by 1
copyParameterAnnotations(originalMethod, method, 0, originalMethod.getParameterCount(), 1, Visibility.Any);
// Add @StructRet to parameter 0
addRuntimeVisibleParameterAnnotation(method, 0, STRUCT_RET);
return method;
}
protected Value loadValueForGetter(SootMethod method, Function fn, Type memberType,
Value memberPtr, Value env, boolean dereference, long flags) {
soot.Type type = method.getReturnType();
Value result = null;
if (memberType instanceof StructureType) {
// The member is a child struct contained in the current struct
result = memberPtr;
} else if (memberType instanceof ArrayType) {
// The member is an array contained in the current struct
result = memberPtr;
} else if (dereference) {
Variable tmp = fn.newVariable(memberType);
fn.add(new Load(tmp, memberPtr));
result = tmp.ref();
} else {
// Do not dereference the pointer but use it as is. This is needed for
// global values such as _dispatch_main_q which is a struct and not a
// pointer which we should load. We want the address of the struct.
Variable tmp = fn.newVariable(memberType);
fn.add(new Bitcast(tmp, memberPtr, tmp.getType()));
result = tmp.ref();
}
if (needsMarshaler(type)) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method));
String targetClassName = getInternalName(type);
if (memberType instanceof PrimitiveType) {
// Value type wrapping a primitive value (e.g. Enum, Integer and Bits)
result = marshalNativeToValueObject(fn, marshalerMethod, env,
targetClassName, result, flags);
} else {
if (memberType instanceof ArrayType) {
// Array
result = marshalNativeToArray(fn, marshalerMethod, env,
targetClassName, result, flags,
getArrayDimensions(method));
} else {
result = marshalNativeToObject(fn, marshalerMethod, null, env,
targetClassName, result, flags);
}
}
} else {
result = marshalNativeToPrimitive(fn, method, result);
}
return result;
}
protected void storeValueForSetter(SootMethod method, Function function,
Type memberType, Value memberPtr, Value env, Value value, long flags) {
soot.Type type = method.getParameterType(0);
if (needsMarshaler(type)) {
MarshalerMethod marshalerMethod = config.getMarshalerLookup().findMarshalerMethod(new MarshalSite(method, 0));
if (memberType instanceof PrimitiveType) {
value = marshalValueObjectToNative(function, marshalerMethod, memberType, env,
value, flags);
} else {
if (memberType instanceof StructureType || memberType instanceof ArrayType) {
// The parameter must not be null. We assume that Structs
// never have a NULL handle so we just check that the Java
// Object isn't null.
call(function, CHECK_NULL, env, value);
}
if (memberType instanceof ArrayType) {
// Array
marshalArrayToNative(function, marshalerMethod, env, value, memberPtr,
flags, getArrayDimensions(method, 0));
value = null;
} else {
value = marshalObjectToNative(function, marshalerMethod, null, memberType, env, value,
flags);
}
}
} else {
value = marshalPrimitiveToNative(function, method, 0, value);
}
if (value != null) {
function.add(new Store(value, memberPtr));
}
}
}