مقابض Downcall
استدعاء الدوال الأصلية
مقابض Downcall درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
استدعاء الدوال الأصلية
استدعاء downcall هو استدعاء من Java إلى داخل الشيفرة الأصلية. وتمثّل FFM كل دالة أصلية على هيئة MethodHandle مبني من خلال Linker.
يشرح هذا الدرس كيفية العثور على دالة C ووصفها واستدعائها.
الرابط الأصلي
تعيد Linker.nativeLinker() الرابط الخاص بالمنصة قيد التشغيل. فهو يعرف اصطلاح استدعاء C، ويمكنه إنشاء مقابض للدوال الأصلية.
import java.lang.foreign.Linker;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
System.out.println("Got native linker: " + linker);
}
}العثور على رمز
تحتاج إلى عنوان الدالة. يبحث linker.defaultLookup() في مكتبة C القياسية، بينما تحمّل SymbolLookup.libraryLookup(name, arena) مكتبة مشتركة محددة.
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment addr = stdlib.find("strlen").orElseThrow();
System.out.println("strlen address found: " + addr);
}
}وصف التوقيع
يصف FunctionDescriptor توقيع C من حيث التخطيطات. في حالة size_t strlen(const char*) تكون القيمة المرجعة من النوع long، وتكون الوسيطة مؤشرًا (ADDRESS).
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
FunctionDescriptor desc = FunctionDescriptor.of(
ValueLayout.JAVA_LONG, // return: size_t
ValueLayout.ADDRESS); // arg: const char*
System.out.println("Descriptor: " + desc);
}
}إنشاء المقبض
اجمع الرمز والواصف باستخدام downcallHandle للحصول على MethodHandle يمكنك استدعاؤه مثل أي أسلوب Java.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
System.out.println("Handle ready: " + strlen.type());
}
}استدعاء الدالة
لاستدعاء strlen، مرّر مؤشرًا إلى سلسلة أصلية. خصص السلسلة في Arena، ثم استدعِ المقبض باستخدام invoke. وتعود النتيجة على هيئة long في Java.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateUtf8String("Hello, FFM");
long len = (long) strlen.invoke(str);
System.out.println("Length = " + len);
}
}
}تعيين الأنواع
يقابل كل نوع من أنواع C تخطيط معين:
intيقابلهValueLayout.JAVA_INTlong/size_tيقابلهماJAVA_LONGdoubleيقابلهJAVA_DOUBLE- أي مؤشر يقابله
ADDRESS
من الضروري الحصول على هذه التعيينات بشكل صحيح، وإلا أفسد الاستدعاء البيانات.
الدوال التي تعيد مؤشرات
عندما تعيد دالة C مؤشرًا، يعيد المقبض MemorySegment بطول صفري. ولكي تقرأ بعد بدايته، يجب إعادة تفسيره بالحجم الصحيح باستخدام reinterpret(byteSize).
تمرير عدة وسيطات
يسرد FunctionDescriptor.of(returnLayout, arg1, arg2, ...) الوسيطات بالترتيب. وبالنسبة إلى دالة من النوع void، استخدم FunctionDescriptor.ofVoid(args...) من دون تخطيط للقيمة المرجعة.
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
// int (*)(const char*, int)
FunctionDescriptor two = FunctionDescriptor.of(
ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT);
// void (*)(int)
FunctionDescriptor noReturn = FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT);
System.out.println(two + " / " + noReturn);
}
}معالجة الأخطاء باستخدام throws
نظرًا إلى أن MethodHandle.invoke يعلن throws Throwable، يجب على أسلوب الاستدعاء معالجة هذا الاستثناء أو الإعلان عنه. ولهذا تعلن الأمثلة throws Throwable.
تخزين المقبض مؤقتًا
إنشاء مقبض downcall مكلف نسبيًا، لذا نفّذه مرة واحدة وأعد استخدامه. ومن الأنماط الشائعة ربط المقبض في حقل static final أثناء تهيئة الصنف، ثم استدعاؤه مرات عديدة.
والمقبض نفسه آمن لاستدعائه بالتزامن من مؤشرات ترابط متعددة.
تحقق سريع
تذكّر كيفية وصف وسيطة مؤشر C.
مراجعة
تعلمت كيفية استدعاء الدوال الأصلية:
- استخدم
Linker.nativeLinker()معSymbolLookupللعثور على الرموز - يصف
FunctionDescriptorالتوقيع باستخدام التخطيطات - ينتج
downcallHandleكائنMethodHandleتستدعيه باستخدامinvoke - تقابل المؤشراتَ
ADDRESS، ويطرحinvokeالاستثناءThrowable
التالي: وصف البيانات الأصلية المعقدة باستخدام التخطيطات والبنى.
الأسئلة الشائعة
هل درس «مقابض Downcall» مجاني؟
نعم — نص درس «مقابض Downcall» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «مقابض Downcall»؟
استدعاء الدوال الأصلية تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «مقابض Downcall»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.