package com.oracle.truffle.llvm.runtime.interop.access;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceArrayLikeType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceBasicType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceClassLikeType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceFunctionType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceMemberType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourcePointerType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceStructLikeType;
import com.oracle.truffle.llvm.runtime.debug.type.LLVMSourceType;
import com.oracle.truffle.llvm.runtime.interop.convert.ForeignToLLVM.ForeignToLLVMType;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMAsForeignLibrary;
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Equivalence;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(LLVMAsForeignLibrary.class)
public abstract class LLVMInteropType implements TruffleObject {
public static final LLVMInteropType.Value UNKNOWN = Value.primitive(null, 0);
private final long size;
private LLVMInteropType(long size) {
this.size = size;
}
public long getSize() {
return size;
}
public LLVMInteropType.Array toArray(long length) {
return new Array(this, size, length);
}
@ExportMessage
boolean isForeign() {
return false;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isMetaObject() {
return true;
}
@ExportMessage(name = "getMetaSimpleName")
@ExportMessage(name = "getMetaQualifiedName")
@TruffleBoundary
Object getMetaSimpleName() {
return toString();
}
@ExportMessage
boolean isMetaInstance(Object instance) {
if (LLVMPointer.isInstance(instance)) {
return LLVMPointer.cast(instance).getExportType() == this;
}
return false;
}
@Override
@TruffleBoundary
public final String toString() {
return toString(EconomicSet.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE));
}
@ExportMessage
@SuppressWarnings("static-method")
final boolean hasLanguage() {
return true;
}
@ExportMessage
@SuppressWarnings({"static-method"})
final Class<? extends TruffleLanguage<?>> getLanguage() {
return LLVMLanguage.class;
}
@ExportMessage
@TruffleBoundary
final String toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
return toString();
}
protected abstract String toString(EconomicSet<LLVMInteropType> visited);
public enum ValueKind {
I1(ForeignToLLVMType.I1),
I8(ForeignToLLVMType.I8),
I16(ForeignToLLVMType.I16),
I32(ForeignToLLVMType.I32),
I64(ForeignToLLVMType.I64),
FLOAT(ForeignToLLVMType.FLOAT),
DOUBLE(ForeignToLLVMType.DOUBLE),
POINTER(ForeignToLLVMType.POINTER);
public final LLVMInteropType.Value type;
public final ForeignToLLVMType foreignToLLVMType;
ValueKind(ForeignToLLVMType foreignToLLVMType) {
this.foreignToLLVMType = foreignToLLVMType;
this.type = Value.primitive(this, foreignToLLVMType.getSizeInBytes());
}
}
public static final class Value extends LLVMInteropType {
public final ValueKind kind;
public final Structured baseType;
private static Value primitive(ValueKind kind, long size) {
return new Value(kind, null, size);
}
public static Value pointer(Structured baseType, long size) {
return new Value(ValueKind.POINTER, baseType, size);
}
private Value(ValueKind kind, Structured baseType, long size) {
super(size);
this.kind = kind;
this.baseType = baseType;
}
public int getSizeInBytes() {
return kind.foreignToLLVMType.getSizeInBytes();
}
@Override
@TruffleBoundary
protected String toString(EconomicSet<LLVMInteropType> visited) {
if (visited.contains(this)) {
return String.format("<recursive %s>", kind.name());
}
visited.add(this);
if (baseType == null) {
return kind.name();
} else {
return baseType.toString(visited) + "*";
}
}
}
public abstract static class Structured extends LLVMInteropType {
Structured(long size) {
super(size);
}
}
public static final class Array extends Structured {
public final LLVMInteropType elementType;
public final long elementSize;
public final long length;
Array(InteropTypeRegistry.Register elementType, long elementSize, long length) {
super(elementSize * length);
this.elementType = elementType.get(this);
this.elementSize = elementSize;
this.length = length;
}
private Array(LLVMInteropType elementType, long elementSize, long length) {
super(elementSize * length);
this.elementType = elementType;
this.elementSize = elementSize;
this.length = length;
}
@Override
@TruffleBoundary
protected String toString(EconomicSet<LLVMInteropType> visited) {
if (visited.contains(this)) {
return "<recursive array type>";
}
visited.add(this);
return String.format("%s[%d]", elementType.toString(visited), length);
}
}
public static class Struct extends Structured {
protected final String name;
@CompilationFinal(dimensions = 1) final StructMember[] members;
Struct(String name, StructMember[] members, long size) {
super(size);
this.name = name;
this.members = members;
}
public StructMember getMember(int i) {
return members[i];
}
@TruffleBoundary
public StructMember findMember(String memberName) {
for (StructMember member : members) {
if (member.name.equals(memberName)) {
return member;
}
}
return null;
}
public int getMemberCount() {
return members.length;
}
@Override
protected String toString(EconomicSet<LLVMInteropType> visited) {
return name;
}
}
public static final class Clazz extends Struct {
@CompilationFinal(dimensions = 1) final Method[] methods;
private Clazz superclass;
Clazz(String name, StructMember[] members, Method[] methods, long size) {
super(name, members, size);
this.methods = methods;
this.superclass = null;
}
public void setSuperClass(Clazz superclass) {
if (this.superclass == null) {
this.superclass = superclass;
}
}
public Method getMethod(int i) {
return methods[i];
}
public int getMethodCount() {
return methods.length;
}
@TruffleBoundary
public Method findMethod(String memberName) {
for (Method method : methods) {
if (method.getName().equals(memberName)) {
return method;
} else if (method.getLinkageName().equals(memberName)) {
return method;
}
}
if (superclass != null) {
return superclass.findMethod(memberName);
}
return null;
}
@TruffleBoundary
public Method findMethodByArgumentsWithSelf(String memberName, Object[] arguments) throws ArityException {
int expectedArgCount = -1;
for (Method method : methods) {
if (method.getName().equals(memberName)) {
LLVMInteropType[] types = method.parameterTypes;
if (types.length + 1 == arguments.length) {
return method;
} else {
expectedArgCount = types.length;
}
} else if (method.getLinkageName().equals(memberName)) {
return method;
}
}
if (superclass != null) {
return superclass.findMethodByArgumentsWithSelf(memberName, arguments);
} else if (expectedArgCount >= 0) {
throw ArityException.create(expectedArgCount, arguments.length - 1);
}
return null;
}
public Method findMethodByArguments(Object receiver, String memberName, Object[] arguments) throws ArityException, UnknownIdentifierException {
Object[] newArgs = new Object[arguments.length + 1];
newArgs[0] = receiver;
for (int i = 1; i < arguments.length; i++) {
newArgs[i] = arguments[i - 1];
}
Method method = findMethodByArgumentsWithSelf(memberName, newArgs);
if (method == null) {
throw UnknownIdentifierException.create(memberName);
}
return method;
}
}
public static final class StructMember {
public final Struct struct;
public final String name;
public final long startOffset;
public final long endOffset;
public final LLVMInteropType type;
StructMember(Struct struct, String name, long startOffset, long endOffset, LLVMInteropType type) {
this.struct = struct;
this.name = name;
this.startOffset = startOffset;
this.endOffset = endOffset;
this.type = type;
}
boolean contains(long offset) {
return startOffset <= offset && ((startOffset == endOffset) | offset < endOffset);
}
}
public static class Function extends Structured {
final LLVMInteropType returnType;
@CompilationFinal(dimensions = 1) final LLVMInteropType[] parameterTypes;
Function(InteropTypeRegistry.Register returnType, LLVMInteropType[] parameterTypes, boolean isMethod) {
super(0);
this.returnType = returnType.get(this, isMethod);
this.parameterTypes = parameterTypes;
}
@Override
@TruffleBoundary
protected String toString(EconomicSet<LLVMInteropType> visited) {
if (visited.contains(this)) {
return "<recursive function type>";
}
visited.add(this);
return String.format("%s(%s)", returnType == null ? "void" : returnType.toString(visited),
Arrays.stream(parameterTypes).map(t -> t == null ? "<null>" : t.toString(visited)).collect(Collectors.joining(", ")));
}
public LLVMInteropType getReturnType() {
return returnType;
}
public LLVMInteropType getParameter(int i) {
return parameterTypes[i];
}
public int getNumberOfParameters() {
return parameterTypes.length;
}
}
public static final class Method extends Function {
private final Clazz clazz;
private final String name;
private final String linkageName;
Method(Clazz clazz, String name, String linkageName, InteropTypeRegistry.Register returnType, LLVMInteropType[] parameterTypes) {
super(returnType, parameterTypes, true);
this.clazz = clazz;
this.name = name;
this.linkageName = linkageName;
}
public Clazz getObjectClass() {
return clazz;
}
public String getName() {
return name;
}
public String getLinkageName() {
return linkageName;
}
@Override
@TruffleBoundary
protected String toString(EconomicSet<LLVMInteropType> visited) {
if (visited.contains(this)) {
return "<recursive function type>";
}
visited.add(this);
return String.format("%s %s(%s)", returnType == null ? "void" : returnType.toString(visited), name,
Arrays.stream(parameterTypes).map(t -> t == null ? "<null>" : t.toString(visited)).collect(Collectors.joining(", ")));
}
}
public static final class InteropTypeRegistry {
private final EconomicMap<LLVMSourceType, LLVMInteropType> typeCache = EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE);
private final class Register {
private final LLVMSourceType source;
private final LLVMSourceType target;
private Register(LLVMSourceType source, LLVMSourceType target) {
this.source = source;
this.target = target;
}
LLVMInteropType get(LLVMInteropType self) {
return get(self, false);
}
LLVMInteropType get(LLVMInteropType self, boolean isMethod) {
if (!isMethod) {
assert !typeCache.containsKey(source);
typeCache.put(source, self);
}
return InteropTypeRegistry.this.get(target);
}
}
public synchronized LLVMInteropType get(LLVMSourceType type) {
if (type == null) {
return LLVMInteropType.UNKNOWN;
}
LLVMSourceType actual = type.getActualType();
if (typeCache.containsKey(actual)) {
return typeCache.get(actual);
} else {
LLVMInteropType ret = convert(actual);
typeCache.put(actual, ret);
return ret;
}
}
private LLVMInteropType convert(LLVMSourceType type) {
if (type instanceof LLVMSourcePointerType) {
return convertPointer((LLVMSourcePointerType) type);
} else if (type instanceof LLVMSourceBasicType) {
return convertBasic((LLVMSourceBasicType) type);
} else {
return convertStructured(type);
}
}
private Structured getStructured(LLVMSourceType type) {
LLVMSourceType actual = type.getActualType();
if (typeCache.containsKey(actual)) {
LLVMInteropType ret = typeCache.get(actual);
if (ret instanceof Structured) {
return (Structured) ret;
} else {
return null;
}
} else {
return convertStructured(actual);
}
}
private Structured convertStructured(LLVMSourceType type) {
if (type instanceof LLVMSourceArrayLikeType) {
return convertArray((LLVMSourceArrayLikeType) type);
} else if (type instanceof LLVMSourceClassLikeType) {
return convertClass((LLVMSourceClassLikeType) type);
} else if (type instanceof LLVMSourceStructLikeType) {
return convertStruct((LLVMSourceStructLikeType) type);
} else if (type instanceof LLVMSourceFunctionType) {
return convertFunction((LLVMSourceFunctionType) type);
} else {
return null;
}
}
private Array convertArray(LLVMSourceArrayLikeType type) {
LLVMSourceType base = type.getBaseType();
return new Array(new Register(type, base), base.getSize() / 8, type.getLength());
}
private Struct convertStruct(LLVMSourceStructLikeType type) {
Struct ret = new Struct(type.getName(), new StructMember[type.getDynamicElementCount()], type.getSize() / 8);
typeCache.put(type, ret);
for (int i = 0; i < ret.members.length; i++) {
LLVMSourceMemberType member = type.getDynamicElement(i);
LLVMSourceType memberType = member.getElementType();
long startOffset = member.getOffset() / 8;
long endOffset = startOffset + (memberType.getSize() + 7) / 8;
ret.members[i] = new StructMember(ret, member.getName(), startOffset, endOffset, get(memberType));
}
return ret;
}
private Clazz convertClass(LLVMSourceClassLikeType type) {
Clazz ret = new Clazz(type.getName(), new StructMember[type.getDynamicElementCount()], new Method[type.getMethodCount()], type.getSize() / 8);
typeCache.put(type, ret);
for (int i = 0; i < ret.members.length; i++) {
LLVMSourceMemberType member = type.getDynamicElement(i);
LLVMSourceType memberType = member.getElementType();
if (memberType instanceof LLVMSourceClassLikeType) {
LLVMSourceClassLikeType sourceSuperClazz = (LLVMSourceClassLikeType) memberType;
if (typeCache.containsKey(sourceSuperClazz)) {
Clazz superClazz = (Clazz) typeCache.get(sourceSuperClazz);
ret.setSuperClass(superClazz);
}
}
long startOffset = member.getOffset() / 8;
long endOffset = startOffset + (memberType.getSize() + 7) / 8;
ret.members[i] = new StructMember(ret, member.getName(), startOffset, endOffset, get(memberType));
}
for (int i = 0; i < ret.methods.length; i++) {
ret.methods[i] = convertMethod(type.getMethodName(i), type.getMethodLinkageName(i), type.getMethod(i), ret);
}
return ret;
}
private Function convertFunction(LLVMSourceFunctionType functionType) {
List<LLVMSourceType> parameterTypes = functionType.getParameterTypes();
LLVMInteropType[] interopParameterTypes = new LLVMInteropType[parameterTypes.size()];
Function interopFunctionType = new Function(new Register(functionType, functionType.getReturnType()), interopParameterTypes, false);
typeCache.put(functionType, interopFunctionType);
for (int i = 0; i < interopParameterTypes.length; i++) {
interopParameterTypes[i] = get(parameterTypes.get(i));
}
return interopFunctionType;
}
private Method convertMethod(String name, String linkageName, LLVMSourceFunctionType functionType, Clazz clazz) {
List<LLVMSourceType> parameterTypes = functionType.getParameterTypes();
LLVMInteropType[] interopParameterTypes = new LLVMInteropType[parameterTypes.size()];
Method interopMethodType = new Method(clazz, name, linkageName, new Register(functionType, functionType.getReturnType()), interopParameterTypes);
typeCache.put(functionType, interopMethodType);
for (int i = 0; i < interopParameterTypes.length; i++) {
interopParameterTypes[i] = get(parameterTypes.get(i));
}
return interopMethodType;
}
private static Value convertBasic(LLVMSourceBasicType type) {
switch (type.getKind()) {
case ADDRESS:
return ValueKind.POINTER.type;
case BOOLEAN:
return ValueKind.I1.type;
case FLOATING:
switch ((int) type.getSize()) {
case 32:
return ValueKind.FLOAT.type;
case 64:
return ValueKind.DOUBLE.type;
}
break;
case SIGNED:
case SIGNED_CHAR:
case UNSIGNED:
case UNSIGNED_CHAR:
switch ((int) type.getSize()) {
case 1:
return ValueKind.I1.type;
case 8:
return ValueKind.I8.type;
case 16:
return ValueKind.I16.type;
case 32:
return ValueKind.I32.type;
case 64:
return ValueKind.I64.type;
}
break;
}
return UNKNOWN;
}
private Value convertPointer(LLVMSourcePointerType type) {
return Value.pointer(getStructured(type.getBaseType()), type.getSize() / 8);
}
}
}