package com.oracle.truffle.llvm.runtime.pointer;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.utilities.TriState;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMFunction;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.except.LLVMLinkerException;
import com.oracle.truffle.llvm.runtime.interop.access.LLVMInteropType;
import com.oracle.truffle.llvm.runtime.interop.access.LLVMInteropType.Clazz;
import com.oracle.truffle.llvm.runtime.interop.access.LLVMInteropType.Method;
import com.oracle.truffle.llvm.runtime.interop.export.LLVMForeignGetIndexPointerNode;
import com.oracle.truffle.llvm.runtime.interop.export.LLVMForeignGetMemberPointerNode;
import com.oracle.truffle.llvm.runtime.interop.export.LLVMForeignReadNode;
import com.oracle.truffle.llvm.runtime.interop.export.LLVMForeignWriteNode;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMManagedReadLibrary;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMManagedWriteLibrary;
import com.oracle.truffle.llvm.runtime.nodes.op.LLVMAddressEqualsNode;
import com.oracle.truffle.llvm.runtime.nodes.others.LLVMDynAccessSymbolNode;
@ExportLibrary(value = InteropLibrary.class, receiverType = LLVMPointerImpl.class)
@ExportLibrary(value = LLVMManagedWriteLibrary.class, receiverType = LLVMPointerImpl.class)
@ExportLibrary(value = LLVMManagedReadLibrary.class, receiverType = LLVMPointerImpl.class)
@ExportLibrary(value = com.oracle.truffle.llvm.spi.ReferenceLibrary.class, receiverType = LLVMPointerImpl.class)
@SuppressWarnings({"static-method", "deprecation"})
abstract class CommonPointerLibraries {
@ExportMessage
static boolean isReadable(@SuppressWarnings("unused") LLVMPointerImpl receiver) {
return false;
}
@ExportMessage
static byte readI8(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type I8 directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static short readI16(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type I16 directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static int readI32(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type I32 directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static float readFloat(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type Float directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static long readI64(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type I64 directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static double readDouble(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type Double directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static LLVMPointer readPointer(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type Pointer directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static Object readGenericI64(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset) {
throw CompilerDirectives.shouldNotReachHere("Cannot read a value of type Object directly from a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static boolean isWritable(@SuppressWarnings("unused") LLVMPointerImpl receiver) {
return false;
}
@ExportMessage
static void writeI8(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") byte value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type I8 directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeI16(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") short value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type I16 directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeI32(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") int value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type I32 directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeFloat(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") float value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type Float directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeI64(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") long value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type I64 directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeGenericI64(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") Object value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type Object directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writeDouble(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") double value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type Double directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static void writePointer(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") long offset, @SuppressWarnings("unused") LLVMPointer value) {
throw CompilerDirectives.shouldNotReachHere("Cannot write a value of type Pointer directly to a pointer. Perhaps a getObject() call is missing.");
}
@ExportMessage
static boolean hasMembers(LLVMPointerImpl receiver) {
return receiver.getExportType() instanceof LLVMInteropType.Struct;
}
@ExportMessage
static Object getMembers(LLVMPointerImpl receiver, boolean includeInternal,
@Shared("isObject") @Cached ConditionProfile isObject) throws UnsupportedMessageException {
if (isObject.profile(receiver.getExportType() instanceof LLVMInteropType.Clazz)) {
LLVMInteropType.Clazz clazz = (LLVMInteropType.Clazz) receiver.getExportType();
return new ClassKeys(clazz);
} else if (isObject.profile(receiver.getExportType() instanceof LLVMInteropType.Struct)) {
LLVMInteropType.Struct struct = (LLVMInteropType.Struct) receiver.getExportType();
return new Keys(struct);
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage
static boolean isMemberReadable(LLVMPointerImpl receiver, String ident,
@Shared("isObject") @Cached ConditionProfile isObject) {
if (isObject.profile(receiver.getExportType() instanceof LLVMInteropType.Clazz)) {
LLVMInteropType.Clazz clazz = (LLVMInteropType.Clazz) receiver.getExportType();
LLVMInteropType.StructMember member = clazz.findMember(ident);
if (member == null) {
LLVMInteropType.Method method = clazz.findMethod(ident);
return method != null;
}
return member != null;
} else if (isObject.profile(receiver.getExportType() instanceof LLVMInteropType.Struct)) {
LLVMInteropType.Struct struct = (LLVMInteropType.Struct) receiver.getExportType();
LLVMInteropType.StructMember member = struct.findMember(ident);
return member != null;
} else {
return false;
}
}
@ExportMessage
static Object readMember(LLVMPointerImpl receiver, String ident,
@Shared("getMember") @Cached LLVMForeignGetMemberPointerNode getElementPointer,
@Exclusive @Cached LLVMForeignReadNode read) throws UnsupportedMessageException, UnknownIdentifierException {
LLVMPointer ptr = getElementPointer.execute(receiver.getExportType(), receiver, ident);
return read.execute(ptr, ptr.getExportType());
}
@ExportMessage
static boolean isMemberModifiable(LLVMPointerImpl receiver, String ident,
@Shared("isObject") @Cached ConditionProfile isObject) {
if (isObject.profile(receiver.getExportType() instanceof LLVMInteropType.Struct)) {
LLVMInteropType.Struct struct = (LLVMInteropType.Struct) receiver.getExportType();
LLVMInteropType.StructMember member = struct.findMember(ident);
if (member == null) {
return false;
} else {
return member.type instanceof LLVMInteropType.Value;
}
} else {
return false;
}
}
@ExportMessage
static boolean isMemberInvocable(LLVMPointerImpl receiver, String ident) {
LLVMInteropType type = receiver.getExportType();
if (type instanceof LLVMInteropType.Clazz) {
LLVMInteropType.Clazz clazz = (LLVMInteropType.Clazz) type;
return clazz.findMethod(ident) != null;
}
return false;
}
@ExportMessage
@ImportStatic(LLVMLanguage.class)
static class InvokeMember {
@Specialization(guards = {"asClazz(receiver)==clazz", "member.equals(methodName)", "argCount==arguments.length"}, assumptions = "getLanguage().singleContextAssumption")
static Object doCached(LLVMPointerImpl receiver, String member, Object[] arguments,
@CachedContext(LLVMLanguage.class) LLVMContext context,
@CachedLibrary(limit = "5") InteropLibrary interop,
@Cached(value = "asClazz(receiver)") LLVMInteropType.Clazz clazz,
@Cached(value = "clazz.findMethodByArguments(receiver, member, arguments)") Method method,
@Cached(value = "arguments.length") int argCount,
@Cached(value = "method.getName()") String methodName,
@Cached(value = "getLLVMFunction(context, method, clazz, member)") LLVMFunction llvmFunction)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
Object[] newArguments = addSelfObject(receiver, arguments);
return interop.execute(context.getSymbol(llvmFunction), newArguments);
}
@Specialization(replaces = "doCached")
static Object doResolve(LLVMPointerImpl receiver, String member, Object[] arguments,
@CachedContext(LLVMLanguage.class) LLVMContext context,
@CachedLibrary(limit = "5") InteropLibrary interop,
@Cached LLVMDynAccessSymbolNode dynAccessSymbolNode)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException, UnknownIdentifierException {
Object[] newArguments = addSelfObject(receiver, arguments);
LLVMInteropType.Clazz newClazz = asClazz(receiver);
Method newMethod = newClazz.findMethodByArguments(receiver, member, arguments);
LLVMFunction newLLVMFunction = getLLVMFunction(context, newMethod, newClazz, member);
Object newReceiver = dynAccessSymbolNode.execute(newLLVMFunction);
return interop.execute(newReceiver, newArguments);
}
}
static LLVMInteropType.Clazz asClazz(LLVMPointerImpl receiver) throws UnsupportedMessageException {
LLVMInteropType type = receiver.getExportType();
if (!(type instanceof LLVMInteropType.Clazz)) {
throw UnsupportedMessageException.create();
}
return (Clazz) type;
}
static Object[] addSelfObject(Object receiver, Object[] rawArgs) {
Object[] newArguments = new Object[rawArgs.length + 1];
newArguments[0] = receiver;
for (int i = 0; i < rawArgs.length; i++) {
newArguments[i + 1] = rawArgs[i];
}
return newArguments;
}
static LLVMFunction getLLVMFunction(LLVMContext context, Method method, LLVMInteropType.Clazz clazz, String member) throws UnknownIdentifierException {
if (method == null) {
throw UnknownIdentifierException.create(member);
}
LLVMFunction llvmFunction = context.getGlobalScope().getFunction(method.getLinkageName());
if (llvmFunction == null) {
CompilerDirectives.transferToInterpreter();
final String clazzName = clazz.toString().startsWith("class ") ? clazz.toString().substring(6) : clazz.toString();
final String msg = String.format("No implementation of declared method %s::%s (%s) found", clazzName, method.getName(), method.getLinkageName());
throw new LLVMLinkerException(msg);
}
return llvmFunction;
}
@ExportMessage
static boolean isMemberInsertable(LLVMPointerImpl receiver, String ident) {
return false;
}
@ExportMessage
static void writeMember(LLVMPointerImpl receiver, String ident, Object value,
@Shared("getMember") @Cached LLVMForeignGetMemberPointerNode getElementPointer,
@Exclusive @Cached LLVMForeignWriteNode write) throws UnsupportedMessageException, UnknownIdentifierException {
LLVMPointer ptr = getElementPointer.execute(receiver.getExportType(), receiver, ident);
write.execute(ptr, ptr.getExportType(), value);
}
@ExportMessage
static boolean hasArrayElements(LLVMPointerImpl receiver) {
return receiver.getExportType() instanceof LLVMInteropType.Array;
}
@ExportMessage
static long getArraySize(LLVMPointerImpl receiver,
@Shared("isArray") @Cached ConditionProfile isArray) throws UnsupportedMessageException {
if (isArray.profile(receiver.getExportType() instanceof LLVMInteropType.Array)) {
return ((LLVMInteropType.Array) receiver.getExportType()).length;
} else {
throw UnsupportedMessageException.create();
}
}
@ExportMessage
static boolean isArrayElementReadable(LLVMPointerImpl receiver, long idx,
@Shared("isArray") @Cached ConditionProfile isArray) {
if (isArray.profile(receiver.getExportType() instanceof LLVMInteropType.Array)) {
long length = ((LLVMInteropType.Array) receiver.getExportType()).length;
return Long.compareUnsigned(idx, length) < 0;
} else {
return false;
}
}
@ExportMessage
static Object readArrayElement(LLVMPointerImpl receiver, long idx,
@Shared("getIndex") @Cached LLVMForeignGetIndexPointerNode getElementPointer,
@Exclusive @Cached LLVMForeignReadNode read) throws UnsupportedMessageException, InvalidArrayIndexException {
LLVMPointer ptr = getElementPointer.execute(receiver.getExportType(), receiver, idx);
return read.execute(ptr, ptr.getExportType());
}
@ExportMessage
static boolean isArrayElementModifiable(LLVMPointerImpl receiver, long idx,
@Shared("isArray") @Cached ConditionProfile isArray) {
if (isArray.profile(receiver.getExportType() instanceof LLVMInteropType.Array)) {
LLVMInteropType.Array arrayType = (LLVMInteropType.Array) receiver.getExportType();
if (arrayType.elementType instanceof LLVMInteropType.Value) {
long length = arrayType.length;
return Long.compareUnsigned(idx, length) < 0;
} else {
return false;
}
} else {
return false;
}
}
@ExportMessage
static boolean isArrayElementInsertable(LLVMPointerImpl receiver, long idx) {
return false;
}
@ExportMessage
static void writeArrayElement(LLVMPointerImpl receiver, long idx, Object value,
@Shared("getIndex") @Cached LLVMForeignGetIndexPointerNode getElementPointer,
@Exclusive @Cached LLVMForeignWriteNode write) throws UnsupportedMessageException, InvalidArrayIndexException {
LLVMPointer ptr = getElementPointer.execute(receiver.getExportType(), receiver, idx);
write.execute(ptr, ptr.getExportType(), value);
}
@ExportLibrary(InteropLibrary.class)
public static final class Keys implements TruffleObject {
private final LLVMInteropType.Struct type;
private Keys(LLVMInteropType.Struct type) {
this.type = type;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return type.getMemberCount();
}
@ExportMessage
boolean isArrayElementReadable(long idx) {
return Long.compareUnsigned(idx, getArraySize()) < 0;
}
@ExportMessage
Object readArrayElement(long idx,
@Cached BranchProfile exception) throws InvalidArrayIndexException {
try {
return type.getMember((int) idx).name;
} catch (IndexOutOfBoundsException ex) {
exception.enter();
throw InvalidArrayIndexException.create(idx);
}
}
}
@ExportLibrary(InteropLibrary.class)
public static final class ClassKeys implements TruffleObject {
private final LLVMInteropType.Clazz type;
private ClassKeys(LLVMInteropType.Clazz type) {
this.type = type;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
long getArraySize() {
return type.getMemberCount() + type.getMethodCount();
}
@ExportMessage
boolean isArrayElementReadable(long idx) {
return Long.compareUnsigned(idx, getArraySize()) < 0;
}
@ExportMessage
Object readArrayElement(long idx,
@Cached BranchProfile exception) throws InvalidArrayIndexException {
try {
int index = (int) idx;
if (index < type.getMemberCount()) {
return type.getMember(index).name;
} else {
return type.getMethod(index - type.getMemberCount()).getName();
}
} catch (IndexOutOfBoundsException ex) {
exception.enter();
throw InvalidArrayIndexException.create(idx);
}
}
}
@ExportMessage
static class IsSame {
@Specialization
static boolean doNative(LLVMPointerImpl receiver, LLVMPointerImpl other,
@Cached LLVMAddressEqualsNode.Operation equals) {
return equals.executeWithTarget(receiver, other);
}
@Fallback
static boolean doOther(LLVMPointerImpl receiver, Object other) {
return false;
}
}
@ExportMessage
static boolean hasLanguage(LLVMPointerImpl receiver) {
return true;
}
@ExportMessage
static Class<? extends TruffleLanguage<?>> getLanguage(@SuppressWarnings("unused") LLVMPointerImpl receiver) {
return LLVMLanguage.class;
}
@ExportMessage
@TruffleBoundary
static String toDisplayString(LLVMPointerImpl receiver, @SuppressWarnings("unused") boolean allowSideEffects) {
return receiver.toString();
}
@ExportMessage
static Object getMetaObject(LLVMPointerImpl receiver) throws UnsupportedMessageException {
Object type = receiver.getExportType();
if (type != null) {
return type;
}
throw UnsupportedMessageException.create();
}
@ExportMessage
static boolean hasMetaObject(LLVMPointerImpl receiver) {
return receiver.getExportType() != null;
}
@ExportMessage
static class IsIdenticalOrUndefined {
@Specialization
static TriState doPointer(LLVMPointerImpl receiver, LLVMPointerImpl other,
@Cached LLVMAddressEqualsNode.Operation equals) {
return TriState.valueOf(equals.executeWithTarget(receiver, other));
}
@Fallback
static TriState doOther(@SuppressWarnings("unused") LLVMPointerImpl receiver, @SuppressWarnings("unused") Object other) {
return TriState.UNDEFINED;
}
}
@ExportMessage
static int identityHashCode(@SuppressWarnings("unused") LLVMPointerImpl receiver) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new AbstractMethodError();
}
}