مقدمة عن WebAssembly ودوره في Node.js
\n\nWebAssembly (WASM) هو تنسيق تعليمات ثنائي منخفض المستوى يتم تنفيذه في آلة افتراضية بأداء شبه أصلي. في نظام Node.js البيئي، يفتح WebAssembly آفاقًا جديدة: فهو يسمح بتنفيذ كود مكتوب بلغات C أو C++ أو Rust أو Go مباشرة في بيئة الخادم. هذا مهم بشكل خاص للمهام التي تتطلب أداءً عاليًا: معالجة الصور، التشفير، تحليل البيانات، العمل مع مصفوفات كبيرة من الأرقام.
\n\nفي هذه المقالة، سنستعرض دورة العمل الكاملة مع WebAssembly في Node.js: من ترجمة الكود المصدر إلى دمج الوحدة الجاهزة في تطبيق JavaScript.
\n\nالمفاهيم الأساسية وإعداد البيئة
\n\nما تحتاج معرفته قبل البدء
\nوحدة WebAssembly هي ملف ثنائي بامتداد .wasm. لا يمكنها الوصول مباشرة إلى DOM أو نظام الملفات — كل التفاعل يتم من خلال "دوال مستوردة" تمررها من JavaScript. في Node.js، يتم إزالة هذا القيد باستخدام أغلفة خاصة.
تثبيت الأدوات
\nلترجمة الكود إلى WASM، نحتاج إلى Emscripten SDK. التثبيت على Linux/macOS:
\ngit clone https://github.com/emscripten-core/emsdk.git\ncd emsdk\n./emsdk install latest\n./emsdk activate latest\nsource ./emsdk_env.sh\n\nلمشاريع Rust، استخدم wasm-pack:
npm install -g wasm-pack\n\nإنشاء وترجمة أول وحدة WASM
\n\nمثال بلغة C — دالة الجمع
\nلننشئ دالة بسيطة بلغة C تجمع رقمين:
\n// math.c\n#include <emscripten.h>\n\nEMSCRIPTEN_KEEPALIVE\nint add(int a, int b) {\n return a + b;\n}\n\nEMSCRIPTEN_KEEPALIVE\nint multiply(int a, int b) {\n return a * b;\n}\n\nنقوم بترجمتها إلى WASM باستخدام Emscripten:
\nemcc math.c -o math.js -s EXPORTED_FUNCTIONS="['_add', '_multiply']" -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="createMathModule"\n\nنحصل على ملفين: math.wasm و math.js — وهو غلاف للتحميل.
تحميل الوحدة في Node.js
\nالآن نستخدم الوحدة في JavaScript:
\n// index.js\nconst createMathModule = require('./math.js');\n\ncreateMathModule().then(module => {\n const add = module._add;\n const multiply = module._multiply;\n\n console.log('5 + 3 =', add(5, 3)); // 8\n console.log('4 * 7 =', multiply(4, 7)); // 28\n});\n\nنقوم بتشغيله:
\nnode index.js\n\nالعمل مع الذاكرة وأنواع البيانات المعقدة
\n\nتمرير السلاسل والمصفوفات
\nWASM يتعامل فقط مع الأرقام. لتمرير السلاسل أو المصفوفات، يجب تخصيص ذاكرة في الذاكرة الخطية للوحدة. لنلق نظرة على مثال بلغة C يقبل سلسلة ويعيد طولها:
\n// string_ops.c\n#include <string.h>\n#include <emscripten.h>\n\nEMSCRIPTEN_KEEPALIVE\nint string_length(char* str) {\n return strlen(str);\n}\n\nEMSCRIPTEN_KEEPALIVE\nvoid to_upper(char* str) {\n for (int i = 0; str[i]; i++) {\n if (str[i] >= 'a' && str[i] <= 'z') {\n str[i] -= 32;\n }\n }\n}\n\nنقوم بترجمته:
\nemcc string_ops.c -o string_ops.js -s EXPORTED_FUNCTIONS="['_string_length', '_to_upper']" -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="createStringModule"\n\nكود JavaScript للعمل مع الذاكرة:
\nconst createStringModule = require('./string_ops.js');\n\ncreateStringModule().then(module => {\n const { _string_length, _to_upper, heap } = module;\n \n // تخصيص ذاكرة للسلسلة\n const input = "hello world";\n const ptr = module._malloc(input.length + 1);\n \n // نسخ السلسلة إلى ذاكرة WASM\n for (let i = 0; i < input.length; i++) {\n heap[i + ptr] = input.charCodeAt(i);\n }\n heap[ptr + input.len