مقدمه
در برخی سناریوها باید توابع خارجی (Foreign Functions) را فراخوانی کنید که آرگومانهایشان شامل اشارهگر به توابع دیگر است. مثال معروف آن، تابع استاندارد C به نام qsort برای مرتبسازی عناصر یک آرایه است:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
این تابع چهار آرگومان دارد:
baseاشارهگری به اولین عنصر آرایهای است که باید مرتب شود.nmembتعداد عناصر آرایه را مشخص میکند.sizeاندازهی هر عنصر در آرایه (بر حسب بایت) است.comparاشارهگری به تابعی است که دو عنصر را مقایسه میکند.
تابع qsort به اشارهگر یک تابع مقایسهکننده نیاز دارد. از جاوا باید کد را بهعنوان Function Pointer به این تابع خارجی پاس دهید.
Upcall Stubs به شما امکان میدهند کد جاوا را بهعنوان Function Pointer به یک تابع بومی (Native) پاس دهید. در این آموزش نحوهی انجام این کار برای فراخوانی qsort در جاوا را بررسی میکنیم.
ساخت یک Downcall Method Handle برای تابع خارجی
ابتدا ببینیم متد مقایسهی دو عنصر — یعنی دو مقدار int — در جاوا چگونه مدلسازی میشود:
class Qsort {
static int qsortCompare(MemorySegment elem1, MemorySegment elem2) {
return Integer.compare(elem1.get(ValueLayout.JAVA_INT, 0), elem2.get(ValueLayout.JAVA_INT, 0));
}
}
در این متد، مقادیر int توسط اشیای MemorySegment نمایش داده میشوند. متد Integer.compare دو مقدار int را بهصورت عددی مقایسه میکند و برای این کار به دسترسی به مقادیر نیاز دارد.
برای این منظور، مثال روی هر MemorySegment متد get(ValueLayout.OfInt, long) را فراخوانی میکند. پارامتر دوم آفست بایتی نسبت به موقعیت آدرس حافظه است.
حال بیایید کد جاوا را با تابع خارجی qsort لینک کنیم. برای این کار یک Downcall Method Handle برای تابع qsort میسازیم:
// Obtain instance of native linker
static final Linker linker = Linker.nativeLinker();
static int[] qsortTest(int[] unsortedArray) throws Throwable {
int[] sorted = null;
// Create downcall handle for qsort
MethodHandle qsort = linker.downcallHandle(linker.defaultLookup().find("qsort").get(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG,
ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
علاوه بر آن، به یک Method Handle برای نمایش متد مقایسه Qsort::qsortCompare نیز نیاز دارید:
// Create method handle for qsortCompare
MethodHandle comparHandle = MethodHandles.lookup().findStatic(Qsort.class, "qsortCompare",
MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class));
متد MethodHandles.Lookup.findStatic(Class, String, MethodType) یک Method Handle برای یک متد استاتیک میسازد و سه آرگومان نیاز دارد:
Qsort.classکلاس متد است.qsortCompareنام متد است.- نوع متد: نوع مقدار بازگشتی و نوع آرگومانهای متد.
با داشتن این عناصر، یک توصیف جاوا از تابع C که توسط یک متد جاوا پیادهسازی شده میسازیم.
ساخت Function Pointer از Method Handle
در این مرحله باید به لینکر اجازه دهید توابع خارجی بتوانند Method Handle های جاوا را فراخوانی کنند.
برای این کار به یک Function Descriptor و یک Function Pointer برای qsortCompare نیاز دارید:
// Create a Java description of a C function implemented by a Java method
FunctionDescriptor qsortCompareDesc = FunctionDescriptor.of(ValueLayout.JAVA_INT,
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT),
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT));
// Create function pointer for qsortCompare
MemorySegment compareFunc = linker.upcallStub(comparHandle, qsortCompareDesc, Arena.ofAuto());
متد Linker.upcallStub(MethodHandle,FunctionDescriptor,Arena,Linker.Option...) سه آرگومان نیاز دارد:
comparHandleMethod Handleای است که Function Pointer از آن ساخته میشود.qsortCompareDescFunction Descriptor مربوط به Function Pointer است.Arena.ofAuto()Arenaای است که با Function Pointer مرتبط میشود.Arena.ofAuto()یک Arena جدید میسازد که بهصورت خودکار توسط Garbage Collector مدیریت میشود.
اکنون میتوانید تابع بومی qsort را برای مرتبسازی یک آرایه فراخوانی کنید.
فراخوانی تابع بومی
تابع بومی qsort به یک آرایه بهعنوان ورودی نیاز دارد.
میتوانید از MemorySegment برای تخصیص حافظهی Off-Heap و ذخیرهی آرایهی input در آن استفاده کنید:
try (Arena arena = Arena.ofConfined()) {
MemorySegment array = arena.allocateFrom(ValueLayout.JAVA_INT, input);
برای فراخوانی تابع qsort آرگومانهای جاوا مربوطه را پاس دهید:
qsort.invoke(array, (long)input.length, ValueLayout.JAVA_INT.byteSize(), compareFunc);
اگر میخواهید آرایهی مرتبشده را ببینید، مقادیر آن را از حافظهی Off-Heap به حافظهی On-Heap کپی کنید:
sorted = array.toArray(ValueLayout.JAVA_INT);
در ادامه، کد کامل و اجرایی این مثال آمده است:
import java.lang.foreign.*;
import java.lang.invoke.*;
public class InvokeQsort {
class Qsort {
static int qsortCompare(MemorySegment elem1, MemorySegment elem2) {
return Integer.compare(elem1.get(ValueLayout.JAVA_INT, 0), elem2.get(ValueLayout.JAVA_INT, 0));
}
}
// Obtain instance of native linker
final static Linker linker = Linker.nativeLinker();
static int[] qsortTest(int[] unsortedArray) throws Throwable {
int[] sorted = null;
// Create downcall handle for qsort
MethodHandle qsort = linker.downcallHandle(
linker.defaultLookup().find("qsort").get(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS));
// Create method handle for qsortCompare
MethodHandle comparHandle = MethodHandles.lookup()
.findStatic(Qsort.class,
"qsortCompare",
MethodType.methodType(int.class,
MemorySegment.class,
MemorySegment.class));
// Create a Java description of a C function implemented by a Java method
FunctionDescriptor qsortCompareDesc = FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT),
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT));
// Create function pointer for qsortCompare
MemorySegment compareFunc = linker.upcallStub(comparHandle,
qsortCompareDesc,
Arena.ofAuto());
try (Arena arena = Arena.ofConfined()) {
// Allocate off-heap memory and store unsortedArray in it
MemorySegment array = arena.allocateFrom(ValueLayout.JAVA_INT,
unsortedArray);
// Call qsort
qsort.invoke(array,
(long)unsortedArray.length,
ValueLayout.JAVA_INT.byteSize(),
compareFunc);
// Access off-heap memory
sorted = array.toArray(ValueLayout.JAVA_INT);
}
return sorted;
}
public static void main(String[] args) {
try {
int[] sortedArray = InvokeQsort.qsortTest(new int[] { 0, 9, 3, 4, 6, 5, 1, 8, 2, 7 });
for (int num : sortedArray) {
IO.print(num + " ");
}
IO.println();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
در نهایت میتوانید با اجرای دستور زیر، مثال کامل را بررسی کنید:
$ java InvokeQsort.java
WARNING: A restricted method in java.lang.foreign.Linker has been called
WARNING: java.lang.foreign.Linker::downcallHandle has been called by InvokeQsort in an unnamed module
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
0 1 2 3 4 5 6 7 8 9
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: منبع مستندات