/*
* Copyright (C) 2012 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.robovm.compiler.llvm.Type.*;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.robovm.compiler.config.Arch;
import org.robovm.compiler.config.OS;
import org.robovm.compiler.llvm.AggregateType;
import org.robovm.compiler.llvm.Bitcast;
import org.robovm.compiler.llvm.Constant;
import org.robovm.compiler.llvm.ConstantGetelementptr;
import org.robovm.compiler.llvm.ConstantPtrtoint;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.FunctionType;
import org.robovm.compiler.llvm.Getelementptr;
import org.robovm.compiler.llvm.IntegerType;
import org.robovm.compiler.llvm.NullConstant;
import org.robovm.compiler.llvm.OpaqueType;
import org.robovm.compiler.llvm.PackedStructureType;
import org.robovm.compiler.llvm.PointerType;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.llvm.Variable;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.LongType;
import soot.PrimType;
import soot.RefLikeType;
import soot.RefType;
import soot.ShortType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.VoidType;
import soot.jimple.toolkits.typing.fast.BottomType;
/**
* @author niklas
*
*/
public class Types {
public static final StructureType GATEWAY_FRAME = new StructureType("GatewayFrame", I8_PTR, I8_PTR, I8_PTR);
public static final Type GATEWAY_FRAME_PTR = new PointerType(GATEWAY_FRAME);
// Dummy TrycatchContext type definition. The real one is in header-<os>-<arch>.ll
public static final StructureType TRYCATCH_CONTEXT = new StructureType("TrycatchContext", I8_PTR);
public static final Type TRYCATCH_CONTEXT_PTR = new PointerType(TRYCATCH_CONTEXT);
public static final StructureType BC_TRYCATCH_CONTEXT = new StructureType("BcTrycatchContext", TRYCATCH_CONTEXT, I8_PTR);
public static final Type BC_TRYCATCH_CONTEXT_PTR = new PointerType(BC_TRYCATCH_CONTEXT);
public static final Type ENV_PTR = new PointerType(new StructureType("Env", I8_PTR, I8_PTR, I8_PTR,
I8_PTR, I8_PTR, I8_PTR, I8_PTR, I8_PTR, I32));
// Dummy Class type definition. The real one is in header.ll
public static final StructureType CLASS = new StructureType("Class", I8_PTR);
public static final Type CLASS_PTR = new PointerType(CLASS);
// Dummy Object type definition. The real one is in header.ll
public static final StructureType OBJECT = new StructureType("Object", CLASS_PTR);
// Dummy DataObject type definition. The real one is in header.ll
public static final StructureType DATA_OBJECT = new StructureType("DataObject", OBJECT);
// Dummy VITable type definition. The real one is in header.ll
public static final StructureType VITABLE = new StructureType("VITable", I8_PTR);
public static final Type VITABLE_PTR = new PointerType(VITABLE);
public static final Type OBJECT_PTR = new PointerType(OBJECT);
public static final Type METHOD_PTR = new PointerType(new OpaqueType("Method"));
public static final Type FIELD_PTR = new PointerType(new OpaqueType("Field"));
public static Type getType(String desc) {
switch (desc.charAt(0)) {
case 'Z': return I8;
case 'B': return I8;
case 'S': return I16;
case 'C': return I16;
case 'I': return I32;
case 'J': return I64;
case 'F': return FLOAT;
case 'D': return DOUBLE;
case 'V': return VOID;
case 'L': return OBJECT_PTR;
case '[': return OBJECT_PTR;
}
throw new IllegalArgumentException();
}
private static Type getType(CharBuffer desc) {
switch (desc.get()) {
case 'Z': return I8;
case 'B': return I8;
case 'S': return I16;
case 'C': return I16;
case 'I': return I32;
case 'J': return I64;
case 'F': return FLOAT;
case 'D': return DOUBLE;
case 'V': return VOID;
case 'L':
while (desc.get() != ';')
;
return OBJECT_PTR;
case '[':
getType(desc);
return OBJECT_PTR;
}
throw new IllegalArgumentException();
}
public static Type getLocalType(soot.Type sootType) {
Type t = getType(sootType);
if (t instanceof IntegerType && ((IntegerType) t).getBits() < 32) {
return I32;
}
return t;
}
public static Type getType(soot.Type sootType) {
if (sootType.equals(soot.BooleanType.v())) {
return Type.I8;
} else if (sootType.equals(soot.ByteType.v())) {
return Type.I8;
} else if (sootType.equals(soot.ShortType.v())) {
return Type.I16;
} else if (sootType.equals(soot.CharType.v())) {
return Type.I16;
} else if (sootType.equals(soot.IntType.v())) {
return Type.I32;
} else if (sootType.equals(soot.LongType.v())) {
return Type.I64;
} else if (sootType.equals(soot.FloatType.v())) {
return Type.FLOAT;
} else if (sootType.equals(soot.DoubleType.v())) {
return Type.DOUBLE;
} else if (sootType.equals(soot.VoidType.v())) {
return Type.VOID;
} else if (sootType instanceof soot.RefLikeType || sootType.equals(BottomType.v())) {
return OBJECT_PTR;
} else {
throw new IllegalArgumentException("Unknown Type: " + sootType);
}
}
public static boolean isUnsigned(soot.Type type) {
return type.equals(CharType.v());
}
public static String getInternalName(soot.Type t) {
if (t instanceof soot.ArrayType) {
return getDescriptor(t);
} else if (t instanceof soot.RefType) {
RefType rt = (RefType) t;
return rt.getClassName().replace('.', '/');
} else {
throw new IllegalArgumentException();
}
}
public static String getInternalName(SootClass sc) {
return sc.getName().replace('.', '/');
}
public static String getInternalNameFromDescriptor(String desc) {
if (!desc.startsWith("L")) {
throw new IllegalArgumentException(desc + " is not a class descriptor");
}
return desc.substring(1, desc.length() - 1);
}
public static String getDescriptor(Type t) {
if (t instanceof PointerType) {
return "J";
}
if (t == I8) {
return "B";
}
if (t == I16) {
return "S";
}
if (t == I32) {
return "I";
}
if (t == I64) {
return "J";
}
if (t == FLOAT) {
return "F";
}
if (t == DOUBLE) {
return "D";
}
throw new IllegalArgumentException(t.toString());
}
public static String getDescriptor(soot.Type t) {
if (t instanceof PrimType) {
if (t.equals(BooleanType.v())) {
return "Z";
} else if (t.equals(ByteType.v())) {
return "B";
} else if (t.equals(ShortType.v())) {
return "S";
} else if (t.equals(CharType.v())) {
return "C";
} else if (t.equals(IntType.v())) {
return "I";
} else if (t.equals(LongType.v())) {
return "J";
} else if (t.equals(FloatType.v())) {
return "F";
} else {
// DoubleType
return "D";
}
} else if (t.equals(VoidType.v())) {
return "V";
} else if (t instanceof soot.ArrayType) {
soot.ArrayType at = (soot.ArrayType) t;
return "[" + getDescriptor(at.getElementType());
} else {
// RefType
RefType rt = (RefType) t;
return "L" + rt.getClassName().replace('.', '/') + ";";
}
}
public static String getDescriptor(SootField field) {
return getDescriptor(field.getType());
}
public static String getDescriptor(SootMethod method) {
return getDescriptor(method.makeRef());
}
@SuppressWarnings("unchecked")
public static String getDescriptor(SootMethodRef methodRef) {
return getDescriptor(methodRef.parameterTypes(), methodRef.returnType());
}
public static String getDescriptor(List<soot.Type> paramTypes, soot.Type returnType) {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (soot.Type t : paramTypes) {
sb.append(getDescriptor(t));
}
sb.append(')');
sb.append(getDescriptor(returnType));
return sb.toString();
}
private static void nextDescriptor(CharBuffer cb, StringBuilder sb) {
char c = cb.get();
sb.append(c);
if (c == 'L') {
do {
c = cb.get();
sb.append(c);
} while (c != ';');
} else if (c == '[') {
nextDescriptor(cb, sb);
} else {
// Must be a primitive
}
}
public static String getReturnTypeDescriptor(String methodDescriptor) {
return methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
}
public static List<String> getParameterDescriptors(String methodDescriptor) {
return getParameterDescriptors(CharBuffer.wrap(methodDescriptor));
}
private static List<String> getParameterDescriptors(CharBuffer cb) {
List<String> result = new ArrayList<String>();
cb.get(); // Skip the initial '('
while (cb.hasRemaining() && cb.get(cb.position()) != ')') {
StringBuilder sb = new StringBuilder();
nextDescriptor(cb, sb);
result.add(sb.toString());
}
return result;
}
public static boolean isEnum(soot.Type t) {
if (t instanceof RefType) {
return isEnum(((RefType) t).getSootClass());
}
return false;
}
public static boolean isEnum(SootClass sc) {
return sc.hasSuperclass() && sc.getSuperclass().getName().equals("java.lang.Enum");
}
public static boolean isPrimitive(String descriptor) {
return descriptor.length() == 1;
}
public static boolean isArray(String descriptor) {
return descriptor.charAt(0) == '[';
}
public static boolean isNativeObject(soot.Type t) {
if (t instanceof RefType) {
return isNativeObject(((RefType) t).getSootClass());
}
return false;
}
public static boolean isNativeObject(SootClass sc) {
return isSubclass(sc, "org.robovm.rt.bro.NativeObject");
}
public static boolean isStruct(soot.Type t) {
if (t instanceof RefType) {
return isStruct(((RefType) t).getSootClass());
}
return false;
}
public static boolean isStruct(SootClass sc) {
return isSubclass(sc, "org.robovm.rt.bro.Struct");
}
public static boolean isPrimitiveComponentType(String descriptor) {
if (!isArray(descriptor)) {
throw new IllegalArgumentException("Not an array");
}
return descriptor.length() == 2;
}
public static boolean isPrimitiveBaseType(String descriptor) {
if (!isArray(descriptor)) {
throw new IllegalArgumentException("Not an array");
}
return descriptor.charAt(descriptor.length() - 1) != ';';
}
/**
* Returns the internal name of the base type for an array of references
* ([[Ljava/lang/String; => java/lang/String) or plain reference
* (Ljava/lang/String => java/lang/String).
*/
public static String getBaseType(String descriptor) {
if (!isArray(descriptor) || descriptor.charAt(descriptor.length() - 1) != ';') {
throw new IllegalArgumentException("Not an array or base type is primitive");
}
int start = descriptor.lastIndexOf('[') + 1;
if (descriptor.charAt(start) != 'L') {
throw new IllegalArgumentException("Invalid descriptor: " + descriptor);
}
return descriptor.substring(start + 1, descriptor.length() - 1);
}
public static FunctionType getFunctionType(SootMethod method) {
return getFunctionType(method.makeRef());
}
@SuppressWarnings("unchecked")
public static FunctionType getFunctionType(SootMethodRef methodRef) {
Type returnType = getType(methodRef.returnType());
Type[] paramTypes = new Type[(methodRef.isStatic() ? 1 : 2) + methodRef.parameterTypes().size()];
int i = 0;
paramTypes[i++] = ENV_PTR;
if (!methodRef.isStatic()) {
paramTypes[i++] = OBJECT_PTR;
}
for (soot.Type t : (List<soot.Type>) methodRef.parameterTypes()) {
paramTypes[i++] = getType(t);
}
return new FunctionType(returnType, paramTypes);
}
public static FunctionType getFunctionType(String methodDesc, boolean ztatic) {
return getFunctionType(methodDesc, ztatic, false);
}
public static FunctionType getNativeFunctionType(String methodDesc, boolean ztatic) {
return getFunctionType(methodDesc, ztatic, true);
}
private static FunctionType getFunctionType(String methodDesc, boolean ztatic, boolean nativ) {
List<Type> paramTypes = new ArrayList<Type>();
paramTypes.add(ENV_PTR);
if (!ztatic) {
paramTypes.add(OBJECT_PTR);
} else if (nativ) {
paramTypes.add(OBJECT_PTR);
}
CharBuffer buffer = CharBuffer.wrap(methodDesc);
buffer.get(); // Skip initial (
while (buffer.get(buffer.position()) != ')') {
paramTypes.add(getType(buffer));
}
buffer.get(); // Skip ending )
Type returnType = getType(buffer);
return new FunctionType(returnType, paramTypes.toArray(new Type[paramTypes.size()]));
}
public static boolean isSubclass(SootClass sc, String className) {
SootClass clazz = sc;
while (clazz.hasSuperclass()) {
clazz = clazz.getSuperclass();
if (className.equals(clazz.getName())) {
return true;
}
}
return false;
}
public static boolean isInstanceOfClass(soot.Type t, String className) {
if (t instanceof RefType) {
return isInstanceOfClass(((RefType) t).getSootClass(), className);
}
return false;
}
public static boolean isInstanceOfClass(SootClass sc, String className) {
SootClass clazz = sc;
if (className.equals(clazz.getName())) {
return true;
}
return isSubclass(sc, className);
}
public static Constant sizeof(AggregateType type) {
return new ConstantPtrtoint(
new ConstantGetelementptr(new NullConstant(
new PointerType(type)), 1), I32);
}
public static Constant offsetof(AggregateType type, int ... idx) {
int[] i = new int[idx.length + 1];
i[0] = 0;
System.arraycopy(idx, 0, i, 1, idx.length);
return new ConstantPtrtoint(
new ConstantGetelementptr(new NullConstant(
new PointerType(type)), i), I32);
}
private static PackedStructureType padType(Type t, int padding) {
// t = i64, padding = 3 => {{i8, i8, i8}, i64}
// t = i8, padding = 0 => {{}, i8}
List<Type> paddingType = new ArrayList<Type>();
for (int i = 0; i < padding; i++) {
paddingType.add(I8);
}
return new PackedStructureType(new PackedStructureType(paddingType.toArray(new Type[paddingType.size()])), t);
}
private static PackedStructureType getInstanceType0(OS os, Arch arch, SootClass clazz, int subClassAlignment, int[] superSize) {
List<Type> types = new ArrayList<Type>();
List<SootField> fields = getInstanceFields(os, arch, clazz);
int superAlignment = 1;
if (!fields.isEmpty()) {
// Pad the super type so that the first field is aligned properly
SootField field = fields.get(0);
superAlignment = getFieldAlignment(os, arch, field);
}
if (clazz.hasSuperclass()) {
types.add(getInstanceType0(os, arch, clazz.getSuperclass(), superAlignment, superSize));
}
int offset = superSize[0];
for (SootField field : fields) {
int falign = getFieldAlignment(os, arch, field);
int padding = (offset & (falign - 1)) != 0 ? (falign - (offset & (falign - 1))) : 0;
types.add(padType(getType(field.getType()), padding));
offset += padding + getFieldSize(arch, field);
}
int padding = (offset & (subClassAlignment - 1)) != 0
? (subClassAlignment - (offset & (subClassAlignment - 1))) : 0;
for (int i = 0; i < padding; i++) {
types.add(I8);
offset++;
}
superSize[0] = offset;
return new PackedStructureType(types.toArray(new Type[types.size()]));
}
public static StructureType getClassType(OS os, Arch arch, SootClass clazz) {
List<Type> types = new ArrayList<Type>();
int offset = 0;
for (SootField field : getClassFields(os, arch, clazz)) {
int falign = getFieldAlignment(os, arch, field);
int padding = (offset & (falign - 1)) != 0 ? (falign - (offset & (falign - 1))) : 0;
types.add(padType(getType(field.getType()), padding));
offset += padding + getFieldSize(arch, field);
}
return new StructureType(CLASS, new StructureType(types.toArray(new Type[types.size()])));
}
public static StructureType getInstanceType(OS os, Arch arch, SootClass clazz) {
return new StructureType(DATA_OBJECT, getInstanceType0(os, arch, clazz, 1, new int[] {0}));
}
public static int getFieldAlignment(OS os, Arch arch, SootField f) {
soot.Type t = f.getType();
if (arch.is32Bit() && arch.isArm()) {
if (LongType.v().equals(t)) {
/*
* On ARM 32-bit volatile longs must be 8 byte aligned. Also,
* the Java Memory Model mandates that final instance fields are
* written to using volatile semantics. So we need to return 8
* here for volatile long static/instance fields and final long
* instance fields.
*
* But due to sun.misc.Unsafe's getLongVolatile() and
* putLongVolatile(), which can be used for
* non-volatile/non-final long fields (e.g. RxJava does this,
* see #987), we always make sure long fields are 8 byte
* aligned to avoid the app crashing when using those methods.
*/
return 8;
}
}
if (LongType.v().equals(t) || DoubleType.v().equals(t)) {
return arch.is32Bit() ? 4 : 8;
}
return getFieldSize(arch, f);
}
public static int getFieldSize(Arch arch, SootField f) {
soot.Type t = f.getType();
if (LongType.v().equals(t) || DoubleType.v().equals(t)) {
return 8;
}
if (IntType.v().equals(t) || FloatType.v().equals(t)) {
return 4;
}
if (t instanceof RefLikeType) {
return arch.is32Bit() ? 4 : 8;
}
if (ShortType.v().equals(t) || CharType.v().equals(t)) {
return 2;
}
if (ByteType.v().equals(t) || BooleanType.v().equals(t)) {
return 1;
}
throw new IllegalArgumentException("Unknown Type: " + t);
}
public static Value getFieldPtr(Function f, Value base, Constant offset, Type fieldType) {
Variable baseI8Ptr = f.newVariable(I8_PTR);
f.add(new Bitcast(baseI8Ptr, base, I8_PTR));
Variable fieldI8Ptr = f.newVariable(I8_PTR);
f.add(new Getelementptr(fieldI8Ptr, baseI8Ptr.ref(), offset));
Variable fieldPtr = f.newVariable(new PointerType(fieldType));
f.add(new Bitcast(fieldPtr, fieldI8Ptr.ref(), fieldPtr.getType()));
return fieldPtr.ref();
}
public static List<SootField> getFields(final OS os, final Arch arch, SootClass clazz, boolean ztatic) {
List<SootField> l = new ArrayList<SootField>();
for (SootField f : clazz.getFields()) {
if (ztatic == f.isStatic()) {
l.add(f);
}
}
// Sort the fields. references, volatile long, double, long, float, int, char, short, boolean, byte.
// Fields of same type are sorted by name.
Collections.sort(l, new Comparator<SootField>() {
@Override
public int compare(SootField o1, SootField o2) {
soot.Type t1 = o1.getType();
soot.Type t2 = o2.getType();
if (t1 instanceof RefLikeType) {
if (!(t2 instanceof RefLikeType)) {
return -1;
}
}
if (t2 instanceof RefLikeType) {
if (!(t1 instanceof RefLikeType)) {
return 1;
}
}
// Compare alignment. Higher first.
int align1 = getFieldAlignment(os, arch, o1);
int align2 = getFieldAlignment(os, arch, o2);
int c = new Integer(align2).compareTo(align1);
if (c == 0) {
// Compare size. Larger first.
int size1 = getFieldSize(arch, o1);
int size2 = getFieldSize(arch, o2);
c = new Integer(size2).compareTo(size1);
if (c == 0) {
// Compare type name.
c = t1.getClass().getSimpleName().compareTo(t2.getClass().getSimpleName());
if (c == 0) {
// Compare name.
c = o1.getName().compareTo(o2.getName());
}
}
}
return c;
}
});
return l;
}
public static List<SootField> getClassFields(OS os, Arch arch, SootClass clazz) {
return getFields(os, arch, clazz, true);
}
public static List<SootField> getInstanceFields(OS os, Arch arch, SootClass clazz) {
return getFields(os, arch, clazz, false);
}
}