VM (تنفيذ JavaScript) في Node.js
مؤشر الاستقرار: 2 - مستقر.
توفر الوحدة vm
واجهات تطبيقات لترجمة وتشغيل الشيفرات البرمجية ضمن سياقات آلآت V8 الافتراضية.
ويمكن ترجمة شيفرة JavaScript البرمجية وتشغيلها فورًا أو تجميعها وحفظها وتشغيلها لاحقًا.
من الاستخدامات الشائعة، تشغيل التعليمات البرمجية في بيئة تجريبية معزولة (sandbox environment). وتُستخدم شيفرة sandbox سياق V8 مختلف، مما يعني انه يحتوي علي كائن عام مختلف عن باقي الشيفرة البرمجية.
ويمكن توفير السياق عن طريق وضع كائن sandbox المعزول في السياق. وتعامل الشيفرةُ المعزولة أي خاصية في بيئة sandbox على أنها متغير عام. وتنعكس أية تغييرات علي المتغيرات العامة الحادثة بسبب الشيفرة المعزولة على كائن sandbox.
const vm = require('vm');
const x = 1;
const sandbox = { x: 2 };
vm.createContext(sandbox); // وضع الـ sandbox في السياق.
const code = 'x += 40; var y = 17;';
// x و y متغيرات عامة في بيئة معزولة.
// في البداية، تحتوي x على القيمة 2 لأن هذه هي قيمة sandbox.x.
vm.runInContext(code, sandbox);
console.log(sandbox.x); // 42
console.log(sandbox.y); // 17
console.log(x); // 1; y غير مُعرَّف.
ولا تُعد الوحدة vm آلية أمان. ولا يجب استخدامها لتشغيل شيفرة برمجية غير موثوق بها.
الصنف: vm.SourceTextModule
أضيفت مع الإصدار: v9.6.0.
مؤشر الاستقرار: 1 - تجريبي.
تتوفر هذه الميزة فقط عند تفعيل راية الأمر --experimental-vm-modules
.
ويوفر الصنف vm.SourceTextModule
واجهة منخفضة المستوى لاستخدام وحدات ECMAScript في سياقات VM. وهو نظير للصنف vm.Script
الذي يعكس سجلات وحدة نَّص المصدر (Source Text Module Records) كما هو محدد في مواصفات ECMAScript.
ومع ذلك، علي عكس vm.Script
يرتبط كل كائن vm.SourceTextModule
إلى سياق من إنشائه. وتكون العمليات على كائنات vm.SourceTextModule
في جوهرها غير متزامنة، في تناقض مع الطبيعة المتزامنة لكائنات vm.Script
بيد أن التلاعب بكائنات vm.SourceTextModule
يكون واضحا إلى حد ما بمساعدة الدوال المتزامنة.
يتطلب استخدام كائن vm.SourceTextModule
أربع خطوات: إنشاء/تحليل, ربط، عمل مثيل، والتقييم. هذه الخطوات الأربع موضحة في المثال التالي.
يكمن هذا التنفيذ في مستوى أقل من مُحمِـل وحدة ECMAScript. كما لا يوجد حاليا أي وسيلة للتفاعل مع المُحمِّـل، على الرغم من التخطيط لدعمه.
const vm = require('vm');
const contextifiedSandbox = vm.createContext({ secret: 42 });
(async () => {
// الخطوة 1
//
// إنشاء وحدة عن طريق بناء كائن `vm.SourceTextModule` جديد.
// يحلل هذا نص المصدر المعطى ويطلق `SyntaxError` إذا حدث أي خطأ.
// بشكل افتراضي، تُنشأ وحدة في أعلى السياق. ولكن هنا،
// نُحدد `contextifiedSandbox` كسياق تنتمي له هذه الوحدة.
//
// هنا نحاول الحصول على التصدير الافتراضي من وحدة "foo"،
// ووضعه في "secret" الربط المحلية.
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
// الخطوة 2
//
// "ربط" التبعيات المستوردة لهذه الوحدة بها.
//
// تقبل دالةُ رد الاتصال المُعطاة ("الرابطة") وسيطين:
// الوحدة الأصل ('bar' في هذه الحالة) والسلسلة التي تحدد
// الوحدة المستوردة. ومن المتوقع أن تعيد دالة رد الاتصال الوحدة التي
// تتوافق مع المحدد المعطى، مع بعض المتطلبات الموثقة
// في `module.link()`.
//
// إذا لم يبدأ الربط للوحدة المُعادة، ستُستدعى نفس دالة رد الاتصال الرابطة
// على الوحدة المُعادة.
//
// ويجب ربط حتى الوحدات ذات المستوي الأعلى الخالية من التبعيات بشكل صريح.
// وعلى الرغم من ذلك، لن تستدعى دالة رد الاتصال المعطاة.
//
ويعيد التابع link() الوعد (Promise) والذي سيُحل عندما
تُحل جميع الوعود Promises المُعادة بواسطة الرابط.
//
// ملاحظة: هذا المثال مُعَد كمثال على أن دالة الرابط تُنشئ
// وحدة "foo" جديدة في كل مرة تستدعى فيها. في نظام وحدة متكامل،
// قد تستخدم ذاكرة التخزين المؤقت على الأرجح لتجنب تكرار الوحدات.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// يشير المتغير "secret" إلى المتغير العام الذي أضفناه إلى
// "contextifiedSandbox" عند إنشاء السياق.
export default secret;
`, { context: referencingModule.context });
// استخدام `contextifiedSandbox` بدلا من `referencingModule.context`
// هنا سوف يعمل أيضًا.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);
// الخطوة 3
//
// إنشاء مثيل الوحدة في المستوي الأعلى.
//
// فقط الوحدة في المستوي الأعلى تحتاج إلى إنشاء مثيل بشكل صريح؛
// وستُنشأ مثيلات لتبعياتها بشكل متكرر من قِبَل instantiate().
bar.instantiate();
// الخطوة 4
//
// تقييم الوحدة. يُعيد التابع evaluate() وعد Promise مع
// خاصية "result" منفردة والتي تحتوي علي نتيجة آخر عبارة
// نُفِذَّت في الوحدة. في حالة `bar`، يكون حرف `s` هو الذي يشير إلى
// التصدير الافتراضي للوحدة 'foo'، و 'secret' الذي ضُبِط
// في بداية بالقيمة 42.
const { result } = await bar.evaluate();
console.log(result);
// طباعة 42.
})();
المُنشئ: new vm.SourceTextModule(code[, options])
code
من النوع<string>
وهو شيفرة وحدة JavaScript المراد تحليلها.options
url
من النوع<string>
وهو URL المستخدم في حل الوحدة وتتبعات المكدس. القيمة الافتراضية: 'vm:module(i)'
حيث i مؤشر تصاعدي خاص بالسياق.context
من النوع<Object>
وهو الكائن الموضوع في السياق كما أُعيد من قبل التابعvm.createContext()
، لترجمته وتقييمه داخلModule
.lineoffset
من النوع<integer>
وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي تنتجها الوحدةModule
.columnoffset
من النوع<integer>
وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي تنتجها الوحدةModule
.initalizeImportMeta
من النوع<Function>
وهو يُستدعى خلال تقييم هذه الوحدةModule
لإعادة تهيئةimport.meta
. شكل هذه الدالة هو(meta, module)
مع كونmeta
كائنimport.meta
فيModule
، وmodule
كائنvm.SourceTextModule
.
إنشاء كائن ES Module
جديد.
قد تسمح الخصائص المعينة لكائن import.meta
التي هي كائنات للوحدة Module
للوصول إلى المعلومات خارج context
المحدد، إذا أنشئ الكائن في سياق المستوي الأعلى. يُستخدم التابع vm.runincontext()
لإنشاء كائنات في سياق معين.
const vm = require('vm');
const contextifiedSandbox = vm.createContext({ secret: 42 });
(async () => {
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// ملاحظة: أنشئ هذا الكائن في السياق الأعلى. وعلى هذا النحو،
// يشير الكائن Object.getPrototypeOf(import.meta.prop) إلى
// Object.prototype في أعلى السياق بدلا من ذلك الذي في
// الـ sandbox.
meta.prop = {};
}
});
// وبما أن ليس للوحدة تبعيات، فلن تُستدعى دالة الرابط أبدًا.
await module.link(() => {});
module.instantiate();
await module.evaluate();
// يساوي Object.prototype.secret الآن 42.
//
// لإصلاح هذه المشكلة، يستبدل
// meta.prop = {};
// أعلاه مع،
// meta.prop = vm.runInContext('{}', contextifiedSandbox);
})();
module.dependencySpecifiers
- من النوع
<string[]>
.
محددات كافة تبعيات هذه الوحدة. وهي تُجمَّد المصفوفة المُعادة لمنع حدوث أي تغييرات عليها.
يتوافق مع الحقل [[RequestedModules]]
في سجلات وحدة نص المصدر في مواصفات ECMAScript.
module.error
- من أي نوع من الأنواع <any>.
إذا كانت الحالة module.status
بالقيمة 'errored'
، تحتوي هذه الخاصية علي الاستثناء الذي أُجريَ بواسطة الوحدة أثناء التقييم. إذا كانت الحالة غير ذلك، سيؤدي الوصول إلى هذه الخاصية إلى إجراء استثناء.
لا يمكن استخدام القيمة undefined
للحالات حيث لا يوجد إجراء استثناء بسبب الغموض المحتمل حول throw undefined;
.
يتوافق مع الحقل [[EvaluationError]]
في سجلات وحدة نص المصدر في مواصفات ECMAScript.
module.evaluate([options])
options
من النوع<Object>
.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة للتقييم قبل إنهاء التنفيذ. إذا تمت مقاطعة التنفيذ، سينطلقError
.breakOnSigint
من النوع<boolean>
، إذا كانtrue
، سينتهي التنفيذ عند تلقيSIGINT
(أو Ctrl + C). ستُعَّطل المعالجات الموجودة للحدث الذي أُرفِق بواسطةprocess.on('SIGINT')
أثناء تنفيذ السكريبت، ولكنها سوف تستمر في العمل بعد ذلك. إذا تمت مقاطعة التنفيذ، سينطلقError
.
- القيمة المُعادة: من النوع <Promise>.
يقيّم هذا التابع الوحدة. ويجب استدعاءه بعد إنشاء مثيل الوحدة؛ وإلا فانه سيُطلق خطأ. ويمكن استدعاؤه أيضًا عند تقييم الوحدة بالفعل، وفي هذه الحالة سيقوم بأحد الأمرين التاليين:
- إعادة
undefined
إذا انتهى التقييم الأولي بنجاح (module.status
تكون 'evaluated'
). - أعادة إجراء الاستثناء نفسه الذي أجراه التقييم الأولي إذا أُنهيَ التقييم الأولي مع وجود خطأ (
module.status
تكون'errored'
).
لا يمكن استدعاء هذا التابع أثناء تقييم الوحدة (module.status
تكون 'evaluating'
) لمنع لانهائية العَوْديَّة.
يتوافق مع حقل التابع Evaluate()
المتماسك من سجلات وحدة نص المصدر في مواصفات ECMAScript.
module.instantiate()
ينشئ هذا التابع مثيل للوحدة. ويجب استدعاءه بعد إنهاء الربط (linkingStatus
تكون 'linked'
)؛ وإلا فانه سيُطلق خطأ. كما يجوز إجراء استثناء إذا لم يوفر أحدُ التبعيات التصديرَ الذي تتطلبه الوحدة الأصل.
ومع ذلك، إذا نجحت هذه الدالة، ستكون الاستدعاءات اللاحقة لهذه الدالة بعد الإنشاء الأولي لمثيل هو no-ops، لتكون متناسقة مع مواصفات ECMAScript.
علي عكس التوابع الأخرى التي تعمل علي Module
، تكمل هذه الدالة بشكل متزامن ولا تقوم بإعادة أي شيء.
يتوافق مع حقل التابع Instantiate()
المتماسك من سجلات وحدة نص المصدر في مواصفات ECMAScript.
module.link (linker)
linker
من النوع<Function>
.- القيمة المُعادة: من النوع <Promise>.
يربط هذا التابع تبعيات الوحدة. يجب استدعاء هذا التابع قبل إنشاء مثيل، ويمكن استدعاؤه مره واحدة فقط لكل وحدة.
سيمرر مُعاملَين إلى الدالة linker:
specifier
وهو محدد الوحدة المطلوبة:import foo from 'foo'; // ^^^^^ محدد الوحدة
referencingModule
وهو كائنModule
الذي يُستدعَى عليه التابعlink()
.
والمتوقع من الدالة إعادة كائن Module
أو كائن Promise
الذي يُحَل إلى كائن Module
في نهاية المطاف. يجب أن يلبي الكائن Module
المُعاد الثابتين التالييَن:
- يجب أن ينتمي إلى نفس سياق كائن
Module
الأصل. - يجب ألَّا تصبح
linkingStatus
الخاصة بها'errored'
.
إذا كانت linkingStatus
المُعادة والخاصة بالكائن Module
هي 'unlinked'،
سيُستدعى هذا التابع بشكل متكرر علي Module
المُعاد مع نفس الدالة linker
المُعطاة.
يعيد link()
الكائن Promise
والذي سيُحل عندما تحل كافة مثيلات الربط إلى Module
صالح، أو يُرفض إذا أجرت دالة الربط استثناءً أو أعادت Module
غير صالح.
تتوافق دالة الربط بشكل تقريبي مع عملية HostResolveImportedModule
المُجرَدة والمعرفة من قِبَل التطبيق في مواصفة ECMAScript مع بعض الاختلافات الرئيسية:
- يُسمح لدالة الربط أن تكون غير متزامنة في حين أن
HostResolveImportedModule
متزامن. - تُنفَّذ دالة الربط أثناء الربط، وهي أحد مراحل Node.js الخاصة قبل إنشاء المثيل، في حين يُستدعى
HostResolveImportedModule
خلال إنشاء المثيل.
يُعيد تطبيقُ HostResolveImportedModule
الفعليُ المستخدم أثناء إنشاء مثيل الوحدة الوحداتِ المرتبطة أثناء الربط. لأن عند هذه النقطة، تكون جميع الوحدات قد رُبطت بالكامل بالفعل، ويكون تطبيق HostResolveImportedModule
متزامنًا تمامًا كما في المواصفات.
module.linkingStatus
- من النوع
<string>
.
حاله الربط الحالية للوحدة module
. وستكون إحدي القيم التالية:
'unlinked'
: لم يُستدعىmodule.link()
بعد.'linking'
: أُستدعيَmodule.link()
، ولكن لم تُحل كل كائناتPromise
المُعادة من قِبَل دالة الربط بعد.'linked'
: أُستدعيَmodule.link()
، ورُبطت جميع التبعيات بنجاح.'errored'
: أُستدعيَmodule.link()
، ولكن فشل أحد تبعياته علي الأقل في الارتباط، إما لان دالة رد الاتصال أعادتPromise
مرفوض، أو لأنModule
الذي أعادته دالة رد الاتصال غير صالح.
module.namespace
- من النوع
<Object>
.
كائن مجال الأسماء للوحدة. وهو متاح فقط بعد اكتمال إنشاء المثيل (module.instantiate()
).
يتوافق مع عملية GetModuleNamespace
المجردة في مواصفة ECMAScript.
module.status
- من النوع
<string>
.
الحالة الحالية للوحدة. وتكون واحدة من:
'uninstantiated'
: لم يُنشأ مثيل للوحدة. قد يكون بسبب أيٍ من الأسباب التالية:- إنشاء الوحدة للتو.
- استدعاء
module.instantiate()
علي هذه الوحدة ولكنه فشل لسبب ما.
لا تنقل هذه الحالة أيَّة معلومات تتعلق بإذا كان التابع module.link()
قد أستُدعيَ أم لا. راجع module.linkingstatus
.
'instantiating'
: يُنشأ حاليًا مثيل الوحدة من خلال استدعاءmodule.instantiate()
علي نفسه أو على وحدة أصل.'instantiated'
: نجح إنشاء مثيل للوحدة، ولكن لم يستدعى التابعmodule.evaluate()
حتى الآن.'evaluating'
: يُقيَّم مثيل الوحدة من خلال استدعاءmodule.evaluate()
علي نفسه أو على وحدة أصل.'evaluated'
: نجح تقييم الوحدة.'errored'
: قُيّمت وحدة، ولكن أُجريَ استثناء.
بخلاف 'errored'
، تتوافق سلسلة هذه الحالة مع مواصفات حقل سجل وحدة نَص المصدر [[Status]]
. ويتوافق 'errored'
مع 'evaluated'
من حيث المواصفات، لكن يُضبط مع [[EvaluationError]]
بقيمة غير undefined
.
module.url
- من النوع
<string>
.
عنوان URL الخاص بالوحدة الحالية، كما هو محدد في المُنشِئ.
الصنف: vm.Script
أضيف مع الإصدار: v0.3.1.
يحتوي مثيلات الصنف vm.Script
علي سكريبتات مترجمة مسبقا والتي يمكن تنفيذها في بيئات تجريبية معزولة (sandboxes) (أو "سياقات") معينة.
new vm.Script(code, options)
code
من النوع<string>
وهو شيفرة JavaScript المراد ترجمتها.options
filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبُعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبُعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبُعات المكدس التي ينتجها هذا السكريبت.cachedData
من النوع<Buffer>
وهو يوفرBuffer
اختياري مع بيانات مؤقتة لشيفرة V8 للمصدر المُعطى. عند توفيرها، ستُضبط قيمةcacheddatarejected
علىtrue
أوfalse
تبعًا لقبول البيانات بواسطة V8.produceCachedData
من النوع<boolean>
. عندما يكونtrue
ولا يوجدcachedData
، سيحاول V8 إنتاج بيانات مؤقتة لشيفرةcode
. عند النجاح، سينتَجBuffer
مع بيانات مؤقتة لشيفرة V8 ويُخزَّن في الخاصيةcacheddata
لمثيلvm.Script
المُعاد. ستُضبط قيمةcacheddataproduced
علىtrue
أوfalse
استنادا إلى ما إذا كانت البيانات المؤقتة للشيفرة قد أُنتِجت بنجاح أم لا. أهمِل هذا الخيار لصالح التابعscript.createCachedData()
.
إنشاء كائن vm.Script
جديد يُترجم code
ولكن لا يُنفذها. يمكن تنفيذ vm.Script
المُترجَم لاحقًا عدة مرات. لا يرتبط code
بأي كائن عام، ولكن بدلًا من ذلك، فانه يرتبط قبل كل تشغيل، فقط لهذا التشغيل.
script.createCachedData()
أضيف مع الإصدار: v10.6.0.
- القيمة المُعادة: من النوع
<Buffer>
.
إنشاء تخزين مؤقت للشيفرة البرمجية التي يمكن استخدامها مع خيار سكريبت المُنشِئ cachedData
. يُعيد مخزن مؤقت. قد يستدعى هذا التابع في أي وقت ولأي عدد من المرات.
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`);
const cacheWithoutX = script.createCachedData();
script.runInThisContext();
const cacheWithX = script.createCachedData();
script.runInContext(contextifiedSandbox[, options])
[المصدر]
سجل التغييرات
الإصدار | التغييرات |
---|---|
v6.3.0 | بداية دعم breakOnSigint .
|
v0.3.1 | أضيف مع الإصدار: v0.3.1 |
contextifiedSandbox
من النوع<Object>
، وهو الكائن الموضوع في السياق كما أُعيد من قبل التابعvm.createContext()
.options
من النوع<Object>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.- columnOffset من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.
- displayErrors من النوع
<boolean>
، عندما يكون true، إذا حدث خطأ Error أثناء ترجمة code، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس. timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.breakOnSigint
من النوع<boolean>
، إذا كانtrue
، سينتهي التنفيذ عند تلقيSIGINT
(أو Ctrl + C). ستُعَّطل المعالجات الموجودة للحدث الذي أُرفِق بواسطةprocess.on('SIGINT')
أثناء تنفيذ السكريبت، ولكنها سوف تستمر في العمل بعد ذلك. إذا انتهى التنفيذ، سينطلقError
.
تشغيل التعليمات البرمجية المترجَمة الواردة في كائن vm.Script
داخل contextifiedSandbox
المُعطَى وإعادة النتيجة. ليس لدى الشيفرة الجاري تشغيلها الحق في الوصول إلى النطاق المحلي.
يُترجم المثال التالي الشيفرة البرمجية التي تُزيد متغيرًا عامًا، ويضبط قيمة متغيرٍ عامٍ آخر، ثم يُنفذ التعليمات البرمجية عده مرات. المتغيرات العامة مضمنة في الكائن sandbox
.
const util = require('util');
const vm = require('vm');
const sandbox = {
animal: 'cat',
count: 2
};
const script = new vm.Script('count += 1; name = "kitty";');
const context = vm.createContext(sandbox);
for (let i = 0; i < 10; ++i) {
script.runInContext(context);
}
console.log(util.inspect(sandbox));
// { animal: 'cat', count: 12, name: 'kitty' }
سيؤدي استخدام الخيارات timeout
أو breakOnSigint
إلى حلقات حدث جديد وبدء الخيوط المقابلة، والتي يكون لها عبء أداء (performance overhead) غير صفري.
script.runInNewContext([sandbox[, options]])
[المصدر]
سجل التغييرات
الإصدار | التغييرات |
---|---|
v10.0.0 | بداية دعم contextCodeGeneration .
|
v0.3.1 | أضيف مع الإصدار: v0.3.1. |
sandbox
من النوع<Object>
وهو الكائن المراد وضعه في السياق. إذا كانundefined
، سينشأ كائن جديد.options
من النوع<Object>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.displayErrors
من النوع<boolean>
، عندما يكونtrue
، إذا حدث خطأError
أثناء ترجمةcode
، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.contextName
من النوع<string>
وهو اسم قابل للقراءة للسياق المُنشَأ حديثًا. القيمة الافتراضية:'VM Context i'
، حيثi
هو مؤشر تصاعدي عددي للسياق المُنشَأ.contextOrigin
من النوع<string>
وهو الأصل Origin المقابل للسياق المُنشَأ حديثًا لأغراض العرض. يجب ان يكون تنسيق الأصل مثل URL، ولكن فقط مع المخطَط، والمضيف، والمَنفذ (إذا لزم الأمر)، مثل قيمة الخاصيةurl.origin
لكائن URL. والاهم من ذلك، يجب حذف الشرطة المائلة اللاحقة، إذ إنها تشير إلى مسار. القيمة الافتراضية:''
.contextCodeGeneration
القيمة المُعادة<Object>
.strings
من النوع<boolean>
إذا ضُبط علىfalse
سيُطلِق أي استدعاء إلىeval
أو لمنشئات الدالة (Function
وGeneratorFunction
، الخ) خطأEvalError
. القيمة الافتراضية:true
.wasm
من نوع<boolean>
، إذا ضُبط علىfalse
ستُطلِق أي محاولة لترجمة وحدة WebAssembly الخطأWebAssembly.CompileError
. القيمة الافتراضية:true
.
يضع أولا كائن sandbox
المعطى في السياق، ثم ينفذ الشيفرة البرمجية المترجمة الواردة في كائن vm.Script
الوارد في كائن sandbox المُنشَأ، وإعادة النتيجة. ليس لدى الشيفرة الجاري تشغيلها الحق في الوصول إلى النطاق المحلي.
يُترجم المثال التالي الشيفرة البرمجية التي تضبط قيمة متغيرٍ عام، ثم يُنفذ التعليمات البرمجية عده مرات في عدة سياقات. تُضبط المتغيرات العامة وترِد ضمن كل sandbox
فردي.
const util = require('util');
const vm = require('vm');
const script = new vm.Script('globalVar = "set"');
const sandboxes = [{}, {}, {}];
sandboxes.forEach((sandbox) => {
script.runInNewContext(sandbox);
});
console.log(util.inspect(sandboxes));
// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
script.runInThisContext([options])
[المصدر]
أضيف مع الإصدار: v0.3.1.
options
من النوع<Object>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.displayErrors
من النوع<boolean>
، عندما يكونtrue
، إذا حدث خطأError
أثناء ترجمةcode
، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.
يُنفذ هذا التابع الشيفرة البرمجية المترجمة المضمنة في vm.Script
ضمن سياق الكائن global
الحالي. ليس لدى الشيفرة الجاري تنفيذها الحق في الوصول إلى النطاق المحلي ولكن لديها حق الوصول إلى الكائن global
الحالي.
يُترجم المثال التالي الشيفرة البرمجية التي تُزيد متغير global
، ثم يُنفذ التعليمات البرمجية عدة مرات:
const vm = require('vm');
global.globalVar = 0;
const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' });
for (let i = 0; i < 1000; ++i) {
script.runInThisContext();
}
console.log(globalVar);
// 1000
vm.compileFunction(code[, params[, options]])
[المصدر]
أُضيف مع الإصدار: v10.10.0.
code
من النوع<string>
وهو متن الدالة المراد ترجمتها.params
من النوع<string[]>
وهو مصفوفة من السلاسل النصية التي تحتوي على جميع المعاملات للدالة.options
من النوع<Object>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت. القيمة الافتراضية:''
.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت. القيمة الافترضية:0
.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت. القيمة الافتراضية:0
.cachedData
من النوع<Buffer>
وهو يوفرBuffer اختياري مع بيانات مؤقتة لشيفرة V8 للمصدر المُعطى.producecacheddata
من النوع<boolean>
، وهو يحدد ما إذا كان ستُنتَج بيانات مؤقتة جديدة. القيمة الافتراضية:false
.parsingContext
من النوع<boolean>
وهو sandbox/السياق الذي يجب ترجمة الدالة المعنية فيه.contextExtensions
من النوع<Object[]>
وهو مصفوفة تحتوي علي مجموعة من ملحقات السياق (كائنات تلتف حول النطاق الحالي) لتطبيقها أثناء الترجمة. القيمة الافتراضية:[]
.
ترجمة الشيفرة البرمجية المعطاة إلى السياق/sanbox المتوفر (إذا لم يتوفر أي سياق، يستخدم السياق الحالي)، ويقوم بإعادتها ملفوفَا داخل دالة مع params
المُعطي.
vm.createContext([sandbox[, options]])
[المصدر]
سجل التغييرات
الإصدار | التغييرات |
---|---|
v10.0.0 | لا يمكن أن يكون الخيار sandbox دالة بعد الآن.
|
v10.0.0 | بداية دعم codeGeneration .
|
v0.3.1 | أضيف مع الإصدار: v0.3.1 |
sandbox
من النوع<boolean>
.options
من النوع<boolean>
.name
من النوع<string>
وهو اسم قابل للقراءة للسياق المُنشَأ حديثًا. القيمة الافتراضية:'VM Context i'
، حيثi
هو مؤشر تصاعدي عددي للسياق المُنشَأ.origin
من النوع<string>
وهو الأصل Origin المقابل للسياق المُنشَأ حديثًا لأغراض العرض. يجب ان يكون تنسيق الأصل مثل URL، ولكن فقط مع المخطَط، والمضيف، والمَنفذ (إذا لزم الأمر)، مثل قيمة الخاصيةurl.origin
لكائنURL
. والاهم من ذلك، يجب حذف الشرطة المائلة اللاحقة، إذ إنها تشير إلى مسار. القيمة الافتراضية:''
.codeGeneration
من النوع<Object>
.strings
من النوع<boolean>
إذا ضُبط علىfalse
سيُطلِق أي استدعاء إلىeval
أو لمنشئات الدالة (Function
وGeneratorFunction
، الخ) خطأEvalError
. القيمة الافتراضية:true
.wasm
من نوع<boolean>
إذا ضُبط على false ستُطلِق أي محاولة لترجمة وحدة WebAssembly الخطأWebAssembly.CompileError
. القيمة الافتراضية:true
.
إذا أُعطي كائن sandbox
، سيُحضِـر التابع vm.createContext()
كائن sandbox هذا بحيث يمكن استخدامه في استدعاء vm.runInContext()
أو script.runInContext()
. داخل مثل هذه السكريبتات، سيكون كائن sandbox
هو الكائن العام، محتفظًا بكافة خصائصه الموجودة ولكنه يمتلك أيضا الكائنات المضمنة والدوال التي يمتلكها أي كائن عام قياسي. فيما عدا السكريبتات التي تُنفَّذ بواسطة الوحدة vm، ستبقي المتغيرات العامة دون تغيير.
const util = require('util');
const vm = require('vm');
global.globalVar = 3;
const sandbox = { globalVar: 1 };
vm.createContext(sandbox);
vm.runInContext('globalVar *= 2;', sandbox);
console.log(util.inspect(sandbox)); // { globalVar: 2 }
console.log(util.inspect(globalVar)); // 3
إذا حُذِف sandbox
(أو مُرِر بشكل صريح كـ undefined
)، سيعاد كائن sandbox جديد فارغ موضوع في السياق.
ويفيد التابع vm.createContext()
في المقام الأول في إنشاء sandbox منفرد والذي يمكن استخدامه لتشغيل عدة سكريبتات. علي سبيل المثال، في حالة محاكاة متصفح ويب، يمكن استخدام التابع لإنشاء sandbox منفرد يمثل كائن عام للإطار، ثم لتشغيل كافة وسوم <script>
معا ضمن سياق كائن sandbox هذا.
ويُجعَل name
و origin
السياق المتوفرَين مرئيـيَن من خلال مفتش API.
vm.isContext(sandbox) [المصدر]
أضيف مع الإصدار: v 0.11.7.
يُعيد true
إذا وُضِع كائن sandbox
المُعطي في السياق باستخدام vm. createcontext()
.
vm.runInContext(code, contextifiedSandbox[, options]) [المصدر]
code
من النوع<string>
وهو شيفرة JavaScript المراد ترجمتها وتنفيذها.contextifiedSandbox
من النوع<Object>
، وهو الكائن الموضوع في السياق المستخدام كمتغير global عند ترجمة code وتشغيله.options
من النوع <Object>
|<string>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.displayErrors
من النوع <number>، عندما يكونtrue
، إذا حدث خطأError
أثناء ترجمةcode
، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.
يترجم التابع vm.runInContext()
الشيفرة code، وينفذها ضمن سياق vm contextifiedSandbox
، ثم يعيد النتيجة. ليس لدى الشيفرة الجاري تنفيذها الحق في الوصول إلى النطاق المحلي. يجب أن يكون كائن contextifiedSandbox
موضوعا مسبقا في السياق باستخدام التابع vm.createcontext()
.
إذا كانت قيمة options
سلسلة نصية، ثم تحدد اسم الملف.
يُترجم المثال التالي سكريبتات مختلفة وينفذها باستخدام كائن منفرد موضوع في السياق.
const util = require('util');
const vm = require('vm');
const sandbox = { globalVar: 1 };
vm.createContext(sandbox);
for (let i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', sandbox);
}
console.log(util.inspect(sandbox));
// { globalVar: 1024 }
vm.runInNewContext(code[, sandbox[, options]])
[المصدر]
أضيف مع الإصدار: v0.3.1.
code
من النوع<string>
وهو شيفرة JavaScript المراد ترجمتها وتنفيذها.sandbox
من النوع<Object>
وهو الكائن المراد وضعه في السياق. إذا كانundefined
، سينشأ كائن جديد.options
من النوع <Object>
|<string>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.displayErrors
من النوع<boolean>
، عندما يكونtrue
، إذا حدث خطأError
أثناء ترجمةcode
، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.contextName
من النوع<string>
وهو اسم قابل للقراءة للسياق المُنشَأ حديثًا. القيمة الافتراضية:'VM Context i'
، حيثi
هو مؤشر تصاعدي عددي للسياق المُنشَأ.contextOrigin
من النوع<string>
وهو الأصل المقابل للسياق المُنشَأ حديثًا لأغراض العرض. يجب ان يكون تنسيق الأصل مثل URL، ولكن فقط مع المخطَط، والمضيف، والمنفذ (إذا لزم الأمر)، مثل قيمة الخاصيةurl.origin
لكائنURL
. والاهم من ذلك، يجب حذف الشرطة المائلة اللاحقة، إذ إنها تشير إلى مسار. القيمة الافتراضية:''
.
يضع التابع vm.runInNewContext()
أولا كائنَ sandbox
المُعطى في السياق (أو ينشئ sandbox
جديد إذا مُرر كـ undefined
)، ويترجم code
، وينفذه ضمن سياق السياق المُنشَأ، ثم يعيد النتيجة. ليس لدى الشيفرة الجاري تنفيذها الحق في الوصول إلى النطاق المحلي.
إذا كانت قيمة options
سلسلة نصية، ستُحدِد اسم الملف.
يُترجم المثال التالي شيفرة وينفذها والتي تُزيد متغيرًا عامًا وتضبط واحدًا جديدًا. المتغيرات العامة مضمنة في الكائن sandbox
.
const util = require('util');
const vm = require('vm');
const sandbox = {
animal: 'cat',
count: 2
};
vm.runInNewContext('count += 1; name = "kitty"', sandbox);
console.log(util.inspect(sandbox));
// { animal: 'cat', count: 3, name: 'kitty' }
vm.runInThisContext(code[, options])
[المصدر]
أضيف مع الإصدار: v0.3.1.
code
من النوع<string>
وهو شيفرة JavaScript المراد ترجمتها وتنفيذها.options
من النوع <Object>
|<string>
.filename
من النوع<string>
وهو يحدد اسم الملف المستخدم في تتبعات المكدس المُنتَجة بواسطة هذا السكريبت.lineOffset
من النوع <number> وهو يحدد إزاحة رقم السطر المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.columnOffset
من النوع <number> وهو يحدد إزاحة رقم العمود المعروض في تتبعات المكدس التي ينتجها هذا السكريبت.displayErrors
من النوع<boolean>
، عندما يكونtrue
، إذا حدث خطأError
أثناء ترجمةcode
، يُرفق السطر من الشيفرة البرمجية الذي يسبب الخطأ إلى تتبع المكدس.timeout
من النوع <number> وهو يحدد عدد المللي ثانية اللازمة لتنفيذcode
قبل إنهاء التنفيذ. إذا انتهى التنفيذ، سينطلقError
.
يترجم التابع vm.runInThisContext()
الشيفرة code
، وينفذها ضمن سياق global
الحالي، ثم يعيد النتيجة. ليس لدى الشيفرة الجاري تنفيذها الحق في الوصول إلى النطاق المحلي ولكن لديها حق الوصول إلى الكائن global
الحالي.
إذا كانت قيمة options
سلسلة نصية، ستُحدِد اسم الملف.
يوضح المثال التالي استخدام كل من vm.runInThisContext()
ودالة eval()
من JavaScript لتنفيذ نفس الشيفرة:
const vm = require('vm');
let localVar = 'initial value';
const vmResult = vm.runInThisContext('localVar = "vm";');
console.log('vmResult:', vmResult);
console.log('localVar:', localVar);
const evalResult = eval('localVar = "eval";');
console.log('evalResult:', evalResult);
console.log('localVar:', localVar);
// vmResult: 'vm', localVar: 'initial value'
// evalResult: 'eval', localVar: 'eval'
لا تتغير localVar
، لأنه ليس لدى vm.runInThisContext()
الحق في الوصول إلى النطاق المحلي. في المقابل، لدى eval() حق الوصول إلى النطاق المحلي، فتتغير قيمة localVar
. بهذه الطريقة يصبح التابع vm.runInThisContext()
مشابه إلى حد كبير بالاستدعاء غير المباشر للدالة eval()
، علي سبيل المثال، (0,eval)('code')
.
أمثلة: تشغيل خادم HTTP ضمن VM.
عند استخدام إما script.runInThisContext()
أو vm.runInThisContext()
، تُنفـَذ الشيفرة البرمجية داخل سياق V8 العام الحالي. سيكون للشيفرة البرمجية الممررة إلى سياق VM نطاقها المعزول الخاص.
من أجل تشغيل خادم ويب بسيط باستخدام وحدة http
، يجب أن تستدعي الشيفرةُ البرمجية التي مُرِرت إلى السياقِ التابعَ require('http')
على نفسه، أو أن يكون له مرجع إلى الوحدة http
يمرر إليه. على سبيل المثال:
'use strict';
const vm = require('vm');
const code = `
((require) => {
const http = require('http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require);
يشارك التابع require()
في الحالة السابقة الحالة مع السياق الذي مُرِر منه. وهذا قد يفرض المخاطر عند تنفيذ شيفرة برمجية غير موثوق بها، علي سبيل المثال تغيير الكائنات في السياق بطرق غير مرغوب فيها.
ماذا يعني أن يوضع كائن في السياق؟
جميع شيفرات JavaScript المنفذة داخل Node.js تعمل ضمن نطاق "السياق". فوفقًا لدليل V8 Embedder:
في V8، يكون السياق هو بيئة تنفيذ والتي تسمح لتطبيقات JavaScript المنفصلة، غير المترابطة أن تعمل في مثيل واحد من V8. يجب تحديد السياق المراد تشغيل أي شيفرات JavaScript فيه بشكل صريح.
عند استدعاء التابع vm.createContext()
، يقترن كائن sandbox
الذي مُرِر فيه (أو كائن مُنشَأ حديثًا إذا كان sandbox
قيمته undefined
) داخليا مع مثيل جديد من سياق V8. يوفر سياق V8 هذا تشغيل code
باستخدام توابع الوحدة vm
مع بيئة عامة معزولة والتي يمكن أن تعمل فيها. عملية إنشاء سياق V8 واقترانه مع الكائن sandbox
هو ما تشير إليه هذه الوثيقة بأنه "الوضع في السياق" لكائن sandbox
.