node中vm模块的使用

704 阅读2分钟

vm 虚拟机,vm 模块使能够在 V8 虚拟机上下文中编译和运行代码。

JavaScript 代码可以被立即地编译并且运行,或者编译、保存并且稍后运行。

一个例子

const vm = require('vm');

const code = 'console.log("1");';
const script = new vm.Script(code);

script.runInThisContext();

复用字节码

JavaScript 代码编译成字节码,通过二进制存储起来,稍后运行。

编译成字节码:

const vm = require('vm');
const code = `console.log("1");`;
const script = new vm.Script(code, {
  produceCachedData: true // 需要指定 produceCachedData 为 true
});

// script.cachedData 为字节码 buffer
const bytecodeBuffer = script.cachedData;

// 可以将 bytecodeBuffer 保存成一个二进制文件

使用字节码:

// 先获取 bytecodeBuffer


const anotherScript = new vm.Script(' '.repeat(code.length), {
  cachedData: bytecodeBuffer // 指定字节码
});

anotherScript.runInThisContext();

注意,这里 vm.Script 第一个参数可以传空字符串,但长度必须和之前的代码长度一致。

创建 vm.Script 实例时,V8 会检查字节码(cachedData)是否与源代码(第一个参数传入的代码)匹配,如果匹配就跳过编译过程,所以第一个参数不能省略。其次,这个检查非常简单,它只会对比代码长度是否一致,所以只要使用与源代码长度相同的空格,就可以“欺骗”这个检查。

runInContext vs runInNewContext vs runInThisContext

首先它们的作用是一样的,执行 vm.Script 对象里被编译的代码并返回其结果,被执行的代码都无法获取本地作用域。

const vm = require('vm');

const code = 'console.log(a);';
const script = new vm.Script(code);
const a = 1; // 本地作用域变量
script.runInThisContext(); // 报错
script.runInContext(); // 报错
script.runInNewContext(); // 报错

runInThisContext

能获取 global 对象。

const vm = require('vm');

const code = 'console.log(a);';
const script = new vm.Script(code);
global.a = 1; // golbal对象变量
script.runInThisContext();

runInContext

无法获取本地作用域,也不能获取 global 对象,但是可以传入一个对象作为上下文 context。

const vm = require('vm');

const code = 'console.log(a);';
const script = new vm.Script(code);


const context = {
  a: 1, console
};

vm.createContext(context); // conext需要传给vm.createContext处理
script.runInContext(context);

code中,对全局变量进行修改,会体现在 context 对象中:

const vm = require('vm');

const code = 'a++';
const script = new vm.Script(code);


const context = {
  a: 1,
};

vm.createContext(context); // conext需要传给vm.createContext处理
script.runInContext(context);

console.log(context.a); // 2

runInNewContext

runInNewContext的源代码如下,它不过是对runInContext调用的简化。

runInNewContext(contextObject, options) {
  const context = createContext(contextObject, getContextOptions(options));
  return this.runInContext(context, options);
}

例:

const vm = require('vm');

const code = 'console.log(a);';
const script = new vm.Script(code);

const context = {
  a: 1, console
};

script.runInNewContext(context); // 因为不能获取 global 对象,所以 console 也需要传入。