واجهة برمجة التطبيقات المُضمنة لـ C++
يوفر Node.js عددًا من واجهات برمجة التطبيقات بلغة C++ التي يمكن استخدامها لتنفيذ JavaScript في بيئة Node.js من برامج C++ الأخرى.
يمكن العثور على وثائق هذه واجهات برمجة التطبيقات في src/node.h ضمن شجرة مصدر Node.js. بالإضافة إلى واجهات برمجة التطبيقات التي يعرضها Node.js، يتم توفير بعض المفاهيم المطلوبة بواسطة واجهة برمجة التطبيقات المُضمنة لـ V8.
نظرًا لأن استخدام Node.js كمكتبة مُضمنة يختلف عن كتابة التعليمات البرمجية التي يتم تنفيذها بواسطة Node.js، فإن التغييرات الكبيرة لا تتبع سياسة إهمال Node.js النموذجية وقد تحدث في كل إصدار رئيسي من semver دون سابق إنذار.
تطبيق تضمين مثال
ستقدم الأقسام التالية لمحة عامة حول كيفية استخدام هذه واجهات برمجة التطبيقات لإنشاء تطبيق من البداية سيؤدي ما يعادل node -e <code\>
, أي أنه سيأخذ جزءًا من JavaScript وتشغيله في بيئة خاصة بـ Node.js.
يمكن العثور على التعليمات البرمجية الكاملة في شجرة مصدر Node.js.
إعداد حالة لكل عملية
يتطلب Node.js بعض إدارة حالة لكل عملية من أجل التشغيل:
- تحليل الوسائط لخيارات سطر الأوامر الخاصة بـ Node.js،
- متطلبات V8 لكل عملية، مثل مثيل
v8::Platform
.
يُظهر المثال التالي كيفية إعداد هذه العناصر. بعض أسماء الفئات من مسافات أسماء C++ node
و v8
، على التوالي.
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
// تحليل خيارات سطر الأوامر الخاصة بـ Node.js، وطباعة أي أخطاء حدثت أثناء
// محاولة تحليلها.
std::unique_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(args, {
node::ProcessInitializationFlags::kNoInitializeV8,
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
});
for (const std::string& error : result->errors())
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
if (result->early_return() != 0) {
return result->exit_code();
}
// إنشاء مثيل `v8::Platform`. `MultiIsolatePlatform::Create()` هي طريقة
// لإنشاء مثيل `v8::Platform` يمكن لـ Node.js استخدامه عند إنشاء
// مؤشرات ترابط عامل. عندما لا يكون هناك مثيل `MultiIsolatePlatform`،
// يتم تعطيل مؤشرات ترابط العامل.
std::unique_ptr<MultiIsolatePlatform> platform =
MultiIsolatePlatform::Create(4);
V8::InitializePlatform(platform.get());
V8::Initialize();
// انظر أدناه لمحتويات هذه الوظيفة.
int ret = RunNodeInstance(
platform.get(), result->args(), result->exec_args());
V8::Dispose();
V8::DisposePlatform();
node::TearDownOncePerProcess();
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
إعداد حالة لكل مثيل
[السجل]
الإصدار | التغييرات |
---|---|
v15.0.0 | تمت إضافة أدوات CommonEnvironmentSetup و SpinEventLoop . |
يحتوي Node.js على مفهوم "مثيل Node.js"، والذي يُشار إليه عادةً باسم node::Environment
. كل node::Environment
مرتبط بـ:
- واحد فقط من
v8::Isolate
، أي مثيل واحد لـمحرك JS، - واحد فقط من
uv_loop_t
، أي حلقة أحداث واحدة، - عدد من
v8::Context
، ولكن واحد فقط منv8::Context
الرئيسي، - مثيل واحد من
node::IsolateData
يحتوي على معلومات يمكن مشاركتها بين العديد منnode::Environment
s. يجب على المُدمج التأكد من أنnode::IsolateData
مُشار إليه فقط بينnode::Environment
s التي تستخدم نفسv8::Isolate
، لا يقوم Node.js بإجراء هذا الفحص.
من أجل إعداد v8::Isolate
، يجب توفير v8::ArrayBuffer::Allocator
. أحد الخيارات الممكنة هو مُخصص Node.js الافتراضي، والذي يمكن إنشاؤه من خلال node::ArrayBufferAllocator::Create()
، استخدام مُخصص Node.js يسمح بتحسينات طفيفة في الأداء عندما تستخدم الإضافات واجهة برمجة التطبيقات C++ Buffer
الخاصة بـNode.js، وهو مطلوب من أجل تتبع ذاكرة ArrayBuffer
في process.memoryUsage()
.
بالإضافة إلى ذلك، يجب تسجيل كل v8::Isolate
المُستخدم لمثيل Node.js وإلغاء تسجيله باستخدام مثيل MultiIsolatePlatform
، إذا كان قيد الاستخدام، لكي يعرف النظام الأساسي حلقة الأحداث التي يجب استخدامها للمهام المُجدولة بواسطة v8::Isolate
.
تقوم دالة المُساعد node::NewIsolate()
بإنشاء v8::Isolate
، وإعداده باستخدام بعض الـhooks الخاصة بـNode.js (مثل مُعالِج أخطاء Node.js)، وتسجيله تلقائيًا مع النظام الأساسي.
int RunNodeInstance(MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
int exit_code = 0;
// إعداد حلقة أحداث libuv، و v8::Isolate، وبيئة Node.js.
std::vector<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> setup =
CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
if (!setup) {
for (const std::string& err : errors)
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
return 1;
}
Isolate* isolate = setup->isolate();
Environment* env = setup->env();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
// يجب إدخال v8::Context عند استدعاء node::CreateEnvironment() و node::LoadEnvironment().
Context::Scope context_scope(setup->context());
// إعداد مثيل Node.js للتنفيذ، وتشغيل التعليمات البرمجية داخله.
// يوجد أيضًا مُتغيّر يأخذ مُستدعيًا ويُوفر له كائنات `require` و `process`، بحيث يمكنه تجميع وتشغيل البرامج النصية يدويًا حسب الحاجة.
// لا تصل دالة `require` داخل هذا البرنامج النصي إلى نظام الملفات، ولا يمكنها تحميل سوى وحدات Node.js المدمجة.
// يتم استخدام `module.createRequire()` لإنشاء واحد قادر على تحميل الملفات من القرص، ويستخدم مُحمِّل ملفات CommonJS القياسي بدلاً من دالة `require` الداخلية فقط.
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
env,
"const publicRequire ="
" require('node:module').createRequire(process.cwd() + '/');"
"globalThis.require = publicRequire;"
"require('node:vm').runInThisContext(process.argv[1]);");
if (loadenv_ret.IsEmpty()) // حدث استثناء JS.
return 1;
exit_code = node::SpinEventLoop(env).FromMaybe(1);
// يمكن استخدام node::Stop() لإيقاف حلقة الأحداث صراحةً ومنع المزيد من تشغيل JavaScript. يمكن استدعاء هذه الدالة من أي مؤشر ترابط، وستتصرف مثل worker.terminate() إذا تم استدعاءها من مؤشر ترابط آخر.
node::Stop(env);
}
return exit_code;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47