package com.oracle.svm.tutorial;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.ObjectHandle;
import org.graalvm.nativeimage.ObjectHandles;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.constant.CEnum;
import org.graalvm.nativeimage.c.constant.CEnumLookup;
import org.graalvm.nativeimage.c.constant.CEnumValue;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.function.CEntryPointLiteral;
import org.graalvm.nativeimage.c.function.CFunction;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer;
import org.graalvm.nativeimage.c.struct.AllowWideningCast;
import org.graalvm.nativeimage.c.struct.CField;
import org.graalvm.nativeimage.c.struct.CFieldAddress;
import org.graalvm.nativeimage.c.struct.CFieldOffset;
import org.graalvm.nativeimage.c.struct.CStruct;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CLongPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
import org.graalvm.word.PointerBase;
import org.graalvm.word.SignedWord;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.OS;
import com.oracle.svm.core.c.ProjectHeaderFile;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.tutorial.CInterfaceTutorial.CInterfaceTutorialDirectives;
@CContext(CInterfaceTutorialDirectives.class)
public class CInterfaceTutorial {
static class CInterfaceTutorialDirectives implements CContext.Directives {
@Override
public List<String> () {
return Collections.singletonList(ProjectHeaderFile.resolve("com.oracle.svm.tutorial", "native/mydata.h"));
}
}
@CConstant("DATA_ARRAY_LENGTH")
protected static native int getDataLength();
@CStruct("my_data")
interface MyData extends PointerBase {
@CField("f_primitive")
int getPrimitive();
@CField("f_primitive")
void setPrimitive(int value);
@CFieldAddress("f_array")
CIntPointer addressOfArray();
@CField("f_cstr")
CCharPointer getCString();
@CField("f_cstr")
void setCString(CCharPointer value);
@CField("f_java_object_handle")
ObjectHandle getJavaObject();
@CField("f_java_object_handle")
void setJavaObject(ObjectHandle value);
@CField("f_print_function")
PrintFunctionPointer getPrintFunction();
@CField("f_print_function")
void setPrintFunction(PrintFunctionPointer printFunction);
}
@CEnum("day_of_the_week_t")
enum DayOfTheWeek {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY;
@CEnumValue
public native int getCValue();
@CEnumLookup
public static native DayOfTheWeek fromCValue(int value);
}
interface PrintFunctionPointer extends CFunctionPointer {
@InvokeCFunctionPointer
void invoke(IsolateThread thread, CCharPointer cstr);
}
@CFunction
protected static native PointerBase memcpy(PointerBase dest, PointerBase src, UnsignedWord n);
@CFunction("c_print")
protected static native void printingInC(IsolateThread thread, CCharPointer cstr);
protected static final CEntryPointLiteral<PrintFunctionPointer> javaPrintFunction = CEntryPointLiteral.create(CInterfaceTutorial.class, "printingInJava", IsolateThread.class, CCharPointer.class);
@CEntryPoint
protected static void printingInJava(@SuppressWarnings("unused") IsolateThread thread, CCharPointer cstr) {
System.out.println("J: " + CTypeConversion.toJavaString(cstr));
}
protected static CCharPointerHolder pin;
protected static void dump(MyData data) {
System.out.format("**** In Java ****\n");
System.out.format("primitive: %d\n", data.getPrimitive());
System.out.format("length: %d\n", getDataLength());
for (int i = 0; i < getDataLength(); i++) {
System.out.format("%d ", data.addressOfArray().read(i));
}
System.out.format("\n");
IsolateThread currentThread = CurrentIsolate.getCurrentThread();
if (OS.getCurrent() != OS.WINDOWS) {
printingInC(currentThread, data.getCString());
}
data.getPrintFunction().invoke(currentThread, data.getCString());
}
@CEntryPoint(name = "java_entry_point")
protected static void javaEntryPoint(@SuppressWarnings("unused") IsolateThread thread, MyData data) {
MyData copy = StackValue.get(MyData.class);
int dataSize = SizeOf.get(MyData.class);
memcpy(copy, data, WordFactory.unsigned(dataSize));
dump(copy);
data.setPrimitive(99);
data.addressOfArray().write(1, 101);
String javaString = CTypeConversion.toJavaString(data.getCString()) + " at " + new Date();
pin = CTypeConversion.toCString(javaString);
CCharPointer cString = pin.get();
data.setCString(cString);
data.setPrintFunction(javaPrintFunction.getFunctionPointer());
data.setJavaObject(ObjectHandles.getGlobal().create(javaString));
}
@CEntryPoint(name = "java_release_data")
protected static void releaseData(@SuppressWarnings("unused") IsolateThread thread, MyData data) {
dump(data);
ObjectHandle handle = data.getJavaObject();
String javaObject = ObjectHandles.getGlobal().get(handle);
System.out.format("javaObject: %s\n", javaObject);
ObjectHandles.getGlobal().destroy(handle);
pin.close();
}
@CFunction("day_of_the_week_add")
protected static native DayOfTheWeek dayOfTheWeekAdd(DayOfTheWeek day, int offset);
@CEntryPoint(name = "java_print_day")
protected static void printDay(@SuppressWarnings("unused") IsolateThread thread, DayOfTheWeek day) {
System.out.format("Day: %s (Java ordinal: %d, C value: %d)%n", day.name(), day.ordinal(), day.getCValue());
if (OS.getCurrent() != OS.WINDOWS) {
System.out.format(" follows %s and %s%n", dayOfTheWeekAdd(day, -2), dayOfTheWeekAdd(day, -1));
System.out.format(" is followed by %s and %s%n", dayOfTheWeekAdd(day, +1), dayOfTheWeekAdd(day, +2));
}
}
@CStruct("h_t")
interface extends PointerBase {
@CField
byte ();
@CFieldAddress("name")
CCharPointer ();
@CFieldAddress("type")
CCharPointer ();
}
@CStruct("subdata_t")
interface Substruct1 extends Header {
@CFieldAddress
Header ();
@CFieldOffset("header")
static int () {
throw VMError.shouldNotReachHere("Calls to the method are replaced with a compile time constant for the offset, so this method body is not reachable.");
}
@CField
int f1();
@CFieldAddress
CIntPointer addressOff1();
@CFieldOffset
int offsetOff1();
Substruct1 addressOf(int index);
}
@CStruct("subdata_t")
interface Substruct2 extends PointerBase {
@CFieldAddress
Header ();
@CFieldOffset("header")
int ();
@CField
int f1();
@CFieldOffset
UnsignedWord offsetOff1();
}
@CEntryPoint(name = "java_entry_point2")
protected static void javaEntryPoint2(@SuppressWarnings("unused") IsolateThread thread, Substruct1 s1, Substruct2 s2) {
System.out.println("*** In Java, demonstrating inheritance with @CStruct class");
CCharPointer tp1 = s1.typePtr();
CCharPointer tp2 = s2.header().typePtr();
System.out.println("tp1 = 0x" + Long.toHexString(tp1.rawValue()) + " tp2 = 0x" + Long.toHexString(tp2.rawValue()));
System.out.println("&s1.header = 0x" + Long.toHexString(s1.header().rawValue()) + " &s2.header = 0x" + Long.toHexString(s2.header().rawValue()));
System.out.println("s1.f1 = " + s1.f1() + " s2.f2 = " + s2.f1());
System.out.println("&s1.f1 = 0x" + Long.toHexString(s1.addressOff1().rawValue()));
System.out.println("*&s1.f1 = " + s1.addressOff1().read());
System.out.println("offset_of(s1.f1) = " + s1.offsetOff1());
System.out.println("s1.header.type = " + s1.type() + " ((Header) s2).type = " + s2.header().type());
System.out.println("*** In Java, demonstrating @CFieldOffset");
System.out.println("offset_of(s1.header) = " + Substruct1.offsetOfHeader());
System.out.println("offset_of(s2.header) = " + s2.offsetOfHeader());
System.out.println("offset_of(s2.f1) = " + s2.offsetOff1().rawValue());
Substruct1 ps1 = s1.addressOf(0);
Substruct1 ps2 = s1.addressOf(1);
long s = ps2.rawValue() - ps1.rawValue();
System.out.print("sizeof(s1) =" + SizeOf.get(Substruct1.class));
System.out.print(" s1 = 0x" + Long.toHexString(s1.rawValue()));
System.out.print(" ps1 = 0x" + Long.toHexString(ps1.rawValue()));
System.out.println(" ps2 = 0x" + Long.toHexString(ps2.rawValue()));
System.out.println(" ps2 - ps1 " + (s == SizeOf.get(Substruct1.class) ? "=" : "!=") + " sizeof(substruct1)");
}
@CStruct("d1_t")
interface D1 extends PointerBase {
@CField("int_value")
int getIntValue();
@CFieldAddress("int_value")
CIntPointer getValuePointer();
@CField("int_pointer")
CIntPointer getIntPointer();
}
@CStruct("d2_t")
interface D2 extends PointerBase {
@CField("long_value")
long getLongValue();
@CFieldAddress("long_value")
CLongPointer getValuePointer();
@CField("long_pointer")
CLongPointer getLongPointer();
}
@CStruct("du_t")
interface DU extends PointerBase {
@CFieldAddress("d1")
D1 getD1();
@CFieldAddress("d2")
D2 getD2();
}
@CEntryPoint(name = "java_entry_point3")
protected static void javaEntryPoint3(@SuppressWarnings("unused") IsolateThread thread, DU du1, DU du2, D1 d1, D2 d2) {
System.out.println("*** In Java, demonstrating access to union type member with @CStruct class");
if (du1.getD1().notEqual(d1)) {
System.out.println("*** Error with Union test1: du1 should be equal to d1");
} else {
System.out.println("Union test 1 passed (0x" + Long.toHexString(d1.rawValue()).toLowerCase() + ")");
}
if (!du2.getD2().equal(d2)) {
System.out.println("*** Error with Union test2: du2 should be equal to d2");
} else {
System.out.println("Union test 2 passed (0x" + Long.toHexString(d2.rawValue()).toLowerCase() + ")");
}
if (d2.getValuePointer().notEqual(d2.getLongPointer())) {
System.out.println("*** Error with Union test3: d2.long_pointer != &d2.long_value");
} else {
System.out.println("Union test 3 passed (0x" + Long.toHexString(d2.getLongPointer().rawValue()).toLowerCase() + ")");
}
if (d2.getValuePointer().read() != d2.getLongValue()) {
System.out.println("*** Error with Union test4: *d2.long_pointer != d2.long_value");
} else {
System.out.println("Union test 4 passed (" + d2.getLongValue() + ")");
}
}
@CStruct("sudata_t")
interface SUData extends PointerBase {
@CField("f_ub1")
byte getUB1();
@AllowWideningCast
@CField("f_ub1")
UnsignedWord getUB1Unsigned();
@CField("f_sb1")
byte getSB1();
@AllowWideningCast
@CField("f_sb1")
SignedWord getSB1Signed();
}
@CEntryPoint(name = "getUB1_raw_value")
protected static long getUB1RawValue(@SuppressWarnings("unused") IsolateThread thread, SUData sudata) {
return sudata.getUB1();
}
@CEntryPoint(name = "getUB1_masked_raw_value")
protected static long getUB1MaskedRawValue(@SuppressWarnings("unused") IsolateThread thread, SUData sudata) {
return sudata.getUB1() & 0xFF;
}
@CEntryPoint(name = "getUB1_as_Unsigned_raw_value")
protected static long getUB1AsUnsignedRawValue(@SuppressWarnings("unused") IsolateThread thread, SUData sudata) {
return sudata.getUB1Unsigned().rawValue();
}
@CEntryPoint(name = "java_entry_point4")
protected static void javaEntryPoint4(@SuppressWarnings("unused") IsolateThread thread, SUData sudata) {
int i = 0;
UnsignedWord u = sudata.getUB1Unsigned();
SignedWord s = sudata.getSB1Signed();
i = sudata.getUB1();
System.out.println(" getUB1() = " + i + (i < 0 ? "<" : ">=") + " 0");
System.out.println(" getUB1Unsigned() = " + u.rawValue() + (u.rawValue() < 0 ? "<" : ">=") + " 0");
i = sudata.getSB1();
System.out.println(" getSB1() = " + i + (i < 0 ? "<" : ">=") + " 0");
System.out.println(" getSB1Signed() = " + s.rawValue() + (s.rawValue() < 0 ? "<" : ">=") + " 0");
System.out.println("(byte) 245 = " + ((byte) 245));
System.out.println("(byte) 245 & 0xFF = " + (((byte) 245) & 0xFF));
System.out.println("sudata.getUB1Unsigned().aboveOrEqual(220) = " + sudata.getUB1Unsigned().aboveOrEqual(220));
System.out.println("sudata.getUB1Unsigned().aboveOrEqual(245) = " + sudata.getUB1Unsigned().aboveOrEqual(245));
System.out.println("sudata.getUB1Unsigned().aboveOrEqual((byte)220) = " + sudata.getUB1Unsigned().aboveOrEqual((byte) 220));
System.out.println("sudata.getUB1Unsigned().aboveOrEqual((byte)245) = " + sudata.getUB1Unsigned().aboveOrEqual((byte) 245));
System.out.println("sudata.getUB1() && 0xFF > 220 = " + ((sudata.getUB1() & 0xFF) > 220));
System.out.println("sudata.getUB1() && 0xFF > 245 = " + ((sudata.getUB1() & 0xFF) > 245));
}
}