تضيف Free Pascal على Win32 خطًا سفليًا بادئًا لكل واردة cdecl; external تلقائيًا، بينما تصدّر public name السلسلة التي كتبتها حرفًا بحرف. وعلى HotPDF أن ترضي الاصطلاحين معًا في شجرة المصدر نفسها، لأن بناء Delphi يشحن أصلًا تعريفاتِ واردة تتهجى الخط السفلي يدويًا. والخطأ في هذا اللاتماثل ينتج أخطاء ربط تذكر رمزًا لم يكتبه أحد
ويوصف توسيع مكتبة Delphi إلى Free Pascal عادة بأنه مشكلة قابلية نقل، وعلى Win64 هو كذلك غالبًا. أما Win32 فمختلف. فالـ ABI لـ Windows على x86 ذي 32-بت يحمل ثلاثين عامًا من الاصطلاح المتراكم حول كيفية تهجئة رموز C، ومن ينظف المكدس، وأي مساعدات خاصة بالمترجم يحق لوحدة ترجمة أن تفترض، وكل واحد منها موضع يستطيع فيه مترجما Pascal يتفقان على اللغة أن يختلفا على ملف الكائنات
لماذا يُحل الرمز نفسه على Win64 ويفشل على Win32
لأن بادئة الخط السفلي اصطلاح 32-بت تطبقه Free Pascal على الواردات لا على الصادرات. صرّح function deflate(...): Integer; cdecl; external; وتبحث FPC عن _deflate في ملف الكائنات على Win32، وعن deflate على Win64. وهذا سلوك صحيح ويطابق ما يُخرجه مترجم C. أما الفخ فعلى الجانب الآخر من الجسر: روتين موسوم بـ public name 'deflate' يصدّر deflate بالضبط على الهدفين، بلا بادئة مضافة
والآن أضف التفصيلة التاريخية التي تجعل الأمر ملموسًا. فبناء Delphi يصرّح أصلًا ببعض نقاط الدخول هذه والخط السفلي مكتوب داخل الاسم، لأن ذلك ما تحويه ملفات كائناته هو. وقدّم التعريف نفسه إلى FPC على Win32 فيضيف المترجم البادئة مجددًا بأمانة، فيبحث الرابط عن __deflate، رمز لا يصدّره شيء. والحل البديهي — إضافة خط سفلي واحد في كل مكان — يكسر الواردات التي كانت مهجاة صحيحًا أصلًا
والذي يعمل زوجُ بادئتين ثابتتين لا واحدة. فـ HPDFFPCZLib وHPDFFPCCodecStubs تستخدمان بادئة للواردات C العادية وأخرى للواردات التي تحمل أصلًا بادئة من جانب Delphi، وعلى Win64 يكون الثابتان فارغين فتنجو أسماء الربط القائمة بلا مساس. ثابتان بدل واحد هو الإصلاح كله، ولا يبدو بديهيًا إلا بعد أن تفصل قاعدة الاستيراد عن قاعدة التصدير
// بادئتان لا واحدة: واردات C العادية وواردات تحمل أصلًا بادئة
// Delphi مكتوبة يدويًا تتزخرفان اختلافًا تحت FPC/Win32
const
{$IF DEFINED(FPC) and DEFINED(CPU32)}
CPrefix = '_'; // FPC يضيفها بنفسه لـ cdecl external
DelphiCName = ''; // مهجاة أصلًا بالخط السفلي في المصدر
{$ELSE}
CPrefix = '';
DelphiCName = '';
{$IFEND}
// جانب التصدير: 'public name' حرفي على كل هدف
procedure hpdf_codec_free(P: Pointer); cdecl;
public name 'hpdf_codec_free';
WIN32 يخبرك بالمعمارية لا بالـ ABI
هذا هو خطأ الترجمة الشرطية صاحب أطول ذيل تنقيح، ويستحق أن يُقال بوضوح: WIN32 وWIN64 تصفان معمارية الهدف ولا تقولان شيئًا عن أي مساعدات وقت تشغيل خاصة بالمترجم موجودة. فـ Free Pascal تعرّف الرمزين على هدفَي Windows الموافقين، تمامًا كما تفعل Delphi. والحارس المكتوب {$IFDEF WIN32} حول كود يستدعي مساعد وقت تشغيل Delphi يُصرَّف إذن تحت FPC ويفشل وقت الربط
وعمليًا هناك ثلاث عائلات من الكود تقع في هذا الفخ. فمسارات انتقال الأعداد الصحيحة 64-بت الخاصة بـ Delphi التي تُبلغ عبر مساعدات System.@_ll، وروتينات دعم التجميع MSVC لـ Win32، وخانات الاستيراد المرافقة لها، كلها موجودة لخدمة كائنات C مصرَّفة مسبقًا يربطها بناء Delphi. وFree Pascal لا تربط تلك الكائنات، فلا تحتاج أيًا من تلك الآلات، وعلى كل إشارة إليها أن تختفي. واللطيفة أن التعريف والتنفيذ يجب استثناءهما معًا. فاستثني واحدًا فقط وسيبلّغ المترجم عن شيء لا ينفع عن معرف لا يطابقه بأي شيء
والقاعدة التي تترتب قصيرة. احرس بالمترجم إذا كان السؤال عن ABI أو دعم وقت التشغيل، واحرس بالمعمارية إذا كان السؤال عن عرض المؤشر أو عدد السجلات، ولا تدع أحدهما ينوب عن الآخر أبدًا
حراسة التعريفات والتنفيذات معًا
الكتلة الشرطية في قسم الواجهة سهلة الوقوع فيها بلا انتباه، ورسالة الخطأ الناتجة تشير إلى أي مكان إلا السبب. أضف تعريف دالة إلى واجهة صنف والموضع الطبيعي لوضعها بجوار الدوال المرتبطة، وهذا لا بأس به حتى اللحظة التي يصادف فيها أولئك الجيران أنهم داخل كتلة {$IFDEF} قائمة أصلًا. فالتوجيهات الشرطية بلا إزاحة بادئة، فالكتلة التي فُتحت قبل أربعين سطرًا شبه غير مرئية وأنت تقرأ التعريفات المحيطة
وما يلي ذلك تصريف ينجح على سلسلة أدوات وينتج شلالًا على أخرى. فإن كان الحارس المحيط فحص إصدار Delphi لا تحققه Free Pascal، تختفي التعريفة عن FPC بينما يبقى التنفيذ غير الشرطي، ويبلّغ المترجم بقائمة طويلة من الشكاوى عن معرفات دوال توقعها ولم يجدها. ولا واحدة من الرسائل تذكر الكتلة الشرطية التي سببت ذلك
وعادتان تمنعان هذا الصنف كله من الفشل. قبل أن تُدرج شيئًا في قسم واجهة، انظر صعودًا عن أقرب شرط مفتوح بدل الوثوق بالتجميع البصري. واعتبر نجاح مجموعة اختبارات Delphi دليلًا على Delphi وحدها: بناء مكتبة Free Pascal بوابة منفصلة، والطريقة الوحيدة لمعرفة اجتيازها أن تشغّل build-Win32-Lib-FPC.cmd وbuild-Win64-Lib-FPC.cmd ضمن التغيير نفسه
ما الذي ينكسر في كود الحساب ذي 32-بت
قيد لغوي واحد يظهر تحديدًا في الكود الأقل استعدادًا للتغيير: Free Pascal ذو 32-بت لن يقبل UInt64 متغير تحكم لحلقة for. ففي وحدات المنحنيات الإهليلجية الحاملة لـ X25519 وX448، كُتبت الحلقات التي تمشي على مصفوفات الأطراف بعدّادات 64-بت لمجرد أن كل شيء آخر في الملف 64-بت
وعلى الإصلاح أن يكون جراحيًا، لأنه في حساب الحقول يكون عرض المتغير جزءًا من حجة الصحة. فهارس الحلقات تصير Integer، لأن مصفوفة أطراف تعد عناصرها بالكف ولا يقترب فهرس من المدى ذي 32-بت أبدًا. وكل ما يشارك في الحساب — الأطراف نفسها، وانتشار الحمل (carry)، والأقنعة — يبقى UInt64، لأن ضيّق أي منها يغير النتيجة بصمت بقياس أول الحقل
// FPC ذو 32-بت يرفض متغير حلقة UInt64. ضيّق الفهرس فقط؛
// الأطراف والأقنعة والحمل تحافظ على عرضها وإلا تغيرت حسابات الحقل
var
I: Integer; // كانت UInt64
Carry, Mask: UInt64;
begin
Carry := 0;
for I := 0 to High(Limbs) do
begin
Limbs[I] := Limbs[I] + Carry;
Carry := Limbs[I] shr 51;
Limbs[I] := Limbs[I] and Mask;
end;
end;
ولا يمكن أن يكون التحقق على تغيير كهذا اختبارًا ذهابًا وإيابًا. فالتشفير ثم فك التشفير بتنفيذ معطوب واحد يتفقان مع نفسيهما تمامًا، ولهذا فمتجهات الإجابة المعروفة غير قابلة للتفاوض هنا: شغّل متجهات اختبار X25519 وX448 المنشورة وقارن بايتات الإخراج بالضبط. هذا هو الفحص الوحيد الذي يفرّق بين تنفيذ صحيح وتنفيذ خاطئ متناسق مع نفسه، وهو ينطبق بالقدر نفسه على البدائيات المتماثلة التي تناقشها مقالة حدودي deflate وAES في برامج ترميز Free Pascal
ما قيمة بناء Free Pascal لـ Win32
والعائد العملي أن تطبيق Lazarus موجَّه إلى Windows ذي 32-بت يحصل على محرك الوثائق نفسه الذي يحصل عليه نظيره في Delphi، دون عقد ثنائي منفصل يُصان. وهذا أهم ما يكون عند النشرات التي قليلًا ما يُحدث عنها أحد: المتحكمات الصناعية، ونقاط البيع، وبرامج أعمال طويلة العمر حيث وقت التشغيل ذو 32-بت ليس خيارًا إرثيًا بل قيد عتادي
وقصة Win64 سبقت ومشروحة في دعم Free Pascal وLazarus على Win64. وWin32 ليس إعادة تشغيل لها. فـ Win64 يملك اصطلاح استدعاء واحد، ولا زخرفة أسماء، ولا مساعدات أعداد صحيحة خاصة بـ Delphi يجب التحايل عليها، فكل شيء في هذه المقالة تقريبًا خاص بالهدف ذي 32-بت. ووحدات الحساب التي احتاجت تغيير متغير الحلقة هي نفسها التي تصفها مقالة حساب Montgomery على منحنيات NIST، حيث يُشرح الانضباط العرضي بعمق أكبر
والعبرة العامة أن عمل قابلية النقل بين المترجمات لا يتعلق أساسًا بمزايا اللغة. فكلا المترجمين يقبل Object Pascal نفسه هنا. والاختلاف في ملف الكائنات: كيف تُهجى الرموز، وأي روتينات مساعدة يُفترض أن يوفرها وقت التشغيل، وأي كائنات مصرَّفة مسبقًا في الربط. وتشحن HotPDF حزمَي Free Pascal وLazarus إلى جانب حزم Delphi وC++Builder في مكوّن HotPDF Delphi PDF، فتغذي شجرة المصدر نفسها كل سلسلة أدوات بدل التفرع لكل مترجم