You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					540 lines
				
				14 KiB
			
		
		
			
		
	
	
					540 lines
				
				14 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * This callback will be called to transform a script to JavaScript. | ||
|  |  * | ||
|  |  * @callback compileCallback | ||
|  |  * @param {string} code - Script code to transform to JavaScript. | ||
|  |  * @param {string} filename - Filename of this script. | ||
|  |  * @return {string} JavaScript code that represents the script code. | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * This callback will be called to resolve a module if it couldn't be found. | ||
|  |  * | ||
|  |  * @callback resolveCallback | ||
|  |  * @param {string} moduleName - Name of the modulusedRequiree to resolve. | ||
|  |  * @param {string} dirname - Name of the current directory. | ||
|  |  * @return {(string|undefined)} The file or directory to use to load the requested module. | ||
|  |  */ | ||
|  | 
 | ||
|  | const fs = require('fs'); | ||
|  | const pa = require('path'); | ||
|  | const { | ||
|  | 	Script, | ||
|  | 	createContext | ||
|  | } = require('vm'); | ||
|  | const { | ||
|  | 	EventEmitter | ||
|  | } = require('events'); | ||
|  | const { | ||
|  | 	INSPECT_MAX_BYTES | ||
|  | } = require('buffer'); | ||
|  | const { | ||
|  | 	createBridge, | ||
|  | 	VMError | ||
|  | } = require('./bridge'); | ||
|  | const { | ||
|  | 	transformer, | ||
|  | 	INTERNAL_STATE_NAME | ||
|  | } = require('./transformer'); | ||
|  | const { | ||
|  | 	lookupCompiler | ||
|  | } = require('./compiler'); | ||
|  | const { | ||
|  | 	VMScript | ||
|  | } = require('./script'); | ||
|  | 
 | ||
|  | const objectDefineProperties = Object.defineProperties; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Host objects | ||
|  |  * | ||
|  |  * @private | ||
|  |  */ | ||
|  | const HOST = Object.freeze({ | ||
|  | 	Buffer, | ||
|  | 	Function, | ||
|  | 	Object, | ||
|  | 	transformAndCheck, | ||
|  | 	INSPECT_MAX_BYTES, | ||
|  | 	INTERNAL_STATE_NAME | ||
|  | }); | ||
|  | 
 | ||
|  | /** | ||
|  |  * Compile a script. | ||
|  |  * | ||
|  |  * @private | ||
|  |  * @param {string} filename - Filename of the script. | ||
|  |  * @param {string} script - Script. | ||
|  |  * @return {vm.Script} The compiled script. | ||
|  |  */ | ||
|  | function compileScript(filename, script) { | ||
|  | 	return new Script(script, { | ||
|  | 		__proto__: null, | ||
|  | 		filename, | ||
|  | 		displayErrors: false | ||
|  | 	}); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Default run options for vm.Script.runInContext | ||
|  |  * | ||
|  |  * @private | ||
|  |  */ | ||
|  | const DEFAULT_RUN_OPTIONS = Object.freeze({__proto__: null, displayErrors: false}); | ||
|  | 
 | ||
|  | function checkAsync(allow) { | ||
|  | 	if (!allow) throw new VMError('Async not available'); | ||
|  | } | ||
|  | 
 | ||
|  | function transformAndCheck(args, code, isAsync, isGenerator, allowAsync) { | ||
|  | 	const ret = transformer(args, code, isAsync, isGenerator, undefined); | ||
|  | 	checkAsync(allowAsync || !ret.hasAsync); | ||
|  | 	return ret.code; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * | ||
|  |  * This callback will be called and has a specific time to finish.<br> | ||
|  |  * No parameters will be supplied.<br> | ||
|  |  * If parameters are required, use a closure. | ||
|  |  * | ||
|  |  * @private | ||
|  |  * @callback runWithTimeout | ||
|  |  * @return {*} | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | let cacheTimeoutContext = null; | ||
|  | let cacheTimeoutScript = null; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Run a function with a specific timeout. | ||
|  |  * | ||
|  |  * @private | ||
|  |  * @param {runWithTimeout} fn - Function to run with the specific timeout. | ||
|  |  * @param {number} timeout - The amount of time to give the function to finish. | ||
|  |  * @return {*} The value returned by the function. | ||
|  |  * @throws {Error} If the function took to long. | ||
|  |  */ | ||
|  | function doWithTimeout(fn, timeout) { | ||
|  | 	if (!cacheTimeoutContext) { | ||
|  | 		cacheTimeoutContext = createContext(); | ||
|  | 		cacheTimeoutScript = new Script('fn()', { | ||
|  | 			__proto__: null, | ||
|  | 			filename: 'timeout_bridge.js', | ||
|  | 			displayErrors: false | ||
|  | 		}); | ||
|  | 	} | ||
|  | 	cacheTimeoutContext.fn = fn; | ||
|  | 	try { | ||
|  | 		return cacheTimeoutScript.runInContext(cacheTimeoutContext, { | ||
|  | 			__proto__: null, | ||
|  | 			displayErrors: false, | ||
|  | 			timeout | ||
|  | 		}); | ||
|  | 	} finally { | ||
|  | 		cacheTimeoutContext.fn = null; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | const bridgeScript = compileScript(`${__dirname}/bridge.js`, | ||
|  | 	`(function(global) {"use strict"; const exports = {};${fs.readFileSync(`${__dirname}/bridge.js`, 'utf8')}\nreturn exports;})`); | ||
|  | const setupSandboxScript = compileScript(`${__dirname}/setup-sandbox.js`, | ||
|  | 	`(function(global, host, bridge, data, context) { ${fs.readFileSync(`${__dirname}/setup-sandbox.js`, 'utf8')}\n})`); | ||
|  | const getGlobalScript = compileScript('get_global.js', 'this'); | ||
|  | 
 | ||
|  | let getGeneratorFunctionScript = null; | ||
|  | let getAsyncFunctionScript = null; | ||
|  | let getAsyncGeneratorFunctionScript = null; | ||
|  | try { | ||
|  | 	getGeneratorFunctionScript = compileScript('get_generator_function.js', '(function*(){}).constructor'); | ||
|  | } catch (ex) {} | ||
|  | try { | ||
|  | 	getAsyncFunctionScript = compileScript('get_async_function.js', '(async function(){}).constructor'); | ||
|  | } catch (ex) {} | ||
|  | try { | ||
|  | 	getAsyncGeneratorFunctionScript = compileScript('get_async_generator_function.js', '(async function*(){}).constructor'); | ||
|  | } catch (ex) {} | ||
|  | 
 | ||
|  | /** | ||
|  |  * Class VM. | ||
|  |  * | ||
|  |  * @public | ||
|  |  */ | ||
|  | class VM extends EventEmitter { | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * The timeout for {@link VM#run} calls. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @member {number} timeout | ||
|  | 	 * @memberOf VM# | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Get the global sandbox object. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @readonly | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @member {Object} sandbox | ||
|  | 	 * @memberOf VM# | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * The compiler to use to get the JavaScript code. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @readonly | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @member {(string|compileCallback)} compiler | ||
|  | 	 * @memberOf VM# | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * The resolved compiler to use to get the JavaScript code. | ||
|  | 	 * | ||
|  | 	 * @private | ||
|  | 	 * @readonly | ||
|  | 	 * @member {compileCallback} _compiler | ||
|  | 	 * @memberOf VM# | ||
|  | 	 */ | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Create a new VM instance. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {Object} [options] - VM options. | ||
|  | 	 * @param {number} [options.timeout] - The amount of time until a call to {@link VM#run} will timeout. | ||
|  | 	 * @param {Object} [options.sandbox] - Objects that will be copied into the global object of the sandbox. | ||
|  | 	 * @param {(string|compileCallback)} [options.compiler="javascript"] - The compiler to use. | ||
|  | 	 * @param {boolean} [options.eval=true] - Allow the dynamic evaluation of code via eval(code) or Function(code)().<br> | ||
|  | 	 * Only available for node v10+. | ||
|  | 	 * @param {boolean} [options.wasm=true] - Allow to run wasm code.<br> | ||
|  | 	 * Only available for node v10+. | ||
|  | 	 * @param {boolean} [options.allowAsync=true] - Allows for async functions. | ||
|  | 	 * @throws {VMError} If the compiler is unknown. | ||
|  | 	 */ | ||
|  | 	constructor(options = {}) { | ||
|  | 		super(); | ||
|  | 
 | ||
|  | 		// Read all options
 | ||
|  | 		const { | ||
|  | 			timeout, | ||
|  | 			sandbox, | ||
|  | 			compiler = 'javascript', | ||
|  | 			allowAsync: optAllowAsync = true | ||
|  | 		} = options; | ||
|  | 		const allowEval = options.eval !== false; | ||
|  | 		const allowWasm = options.wasm !== false; | ||
|  | 		const allowAsync = optAllowAsync && !options.fixAsync; | ||
|  | 
 | ||
|  | 		// Early error if sandbox is not an object.
 | ||
|  | 		if (sandbox && 'object' !== typeof sandbox) { | ||
|  | 			throw new VMError('Sandbox must be object.'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Early error if compiler can't be found.
 | ||
|  | 		const resolvedCompiler = lookupCompiler(compiler); | ||
|  | 
 | ||
|  | 		// Create a new context for this vm.
 | ||
|  | 		const _context = createContext(undefined, { | ||
|  | 			__proto__: null, | ||
|  | 			codeGeneration: { | ||
|  | 				__proto__: null, | ||
|  | 				strings: allowEval, | ||
|  | 				wasm: allowWasm | ||
|  | 			} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		const sandboxGlobal = getGlobalScript.runInContext(_context, DEFAULT_RUN_OPTIONS); | ||
|  | 
 | ||
|  | 		// Initialize the sandbox bridge
 | ||
|  | 		const { | ||
|  | 			createBridge: sandboxCreateBridge | ||
|  | 		} = bridgeScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal); | ||
|  | 
 | ||
|  | 		// Initialize the bridge
 | ||
|  | 		const bridge = createBridge(sandboxCreateBridge, () => {}); | ||
|  | 
 | ||
|  | 		const data = { | ||
|  | 			__proto__: null, | ||
|  | 			allowAsync | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		if (getGeneratorFunctionScript) { | ||
|  | 			data.GeneratorFunction = getGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS); | ||
|  | 		} | ||
|  | 		if (getAsyncFunctionScript) { | ||
|  | 			data.AsyncFunction = getAsyncFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS); | ||
|  | 		} | ||
|  | 		if (getAsyncGeneratorFunctionScript) { | ||
|  | 			data.AsyncGeneratorFunction = getAsyncGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Create the bridge between the host and the sandbox.
 | ||
|  | 		const internal = setupSandboxScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal, HOST, bridge.other, data, _context); | ||
|  | 
 | ||
|  | 		const runScript = (script) => { | ||
|  | 			// This closure is intentional to hide _context and bridge since the allow to access the sandbox directly which is unsafe.
 | ||
|  | 			let ret; | ||
|  | 			try { | ||
|  | 				ret = script.runInContext(_context, DEFAULT_RUN_OPTIONS); | ||
|  | 			} catch (e) { | ||
|  | 				throw bridge.from(e); | ||
|  | 			} | ||
|  | 			return bridge.from(ret); | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		const makeReadonly = (value, mock) => { | ||
|  | 			try { | ||
|  | 				internal.readonly(value, mock); | ||
|  | 			} catch (e) { | ||
|  | 				throw bridge.from(e); | ||
|  | 			} | ||
|  | 			return value; | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		const makeProtected = (value) => { | ||
|  | 			const sandboxBridge = bridge.other; | ||
|  | 			try { | ||
|  | 				sandboxBridge.fromWithFactory(sandboxBridge.protectedFactory, value); | ||
|  | 			} catch (e) { | ||
|  | 				throw bridge.from(e); | ||
|  | 			} | ||
|  | 			return value; | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		const addProtoMapping = (hostProto, sandboxProto) => { | ||
|  | 			const sandboxBridge = bridge.other; | ||
|  | 			let otherProto; | ||
|  | 			try { | ||
|  | 				otherProto = sandboxBridge.from(sandboxProto); | ||
|  | 				sandboxBridge.addProtoMapping(otherProto, hostProto); | ||
|  | 			} catch (e) { | ||
|  | 				throw bridge.from(e); | ||
|  | 			} | ||
|  | 			bridge.addProtoMapping(hostProto, otherProto); | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		const addProtoMappingFactory = (hostProto, sandboxProtoFactory) => { | ||
|  | 			const sandboxBridge = bridge.other; | ||
|  | 			const factory = () => { | ||
|  | 				const proto = sandboxProtoFactory(this); | ||
|  | 				bridge.addProtoMapping(hostProto, proto); | ||
|  | 				return proto; | ||
|  | 			}; | ||
|  | 			try { | ||
|  | 				const otherProtoFactory = sandboxBridge.from(factory); | ||
|  | 				sandboxBridge.addProtoMappingFactory(otherProtoFactory, hostProto); | ||
|  | 			} catch (e) { | ||
|  | 				throw bridge.from(e); | ||
|  | 			} | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		// Define the properties of this object.
 | ||
|  | 		// Use Object.defineProperties here to be able to
 | ||
|  | 		// hide and set properties read-only.
 | ||
|  | 		objectDefineProperties(this, { | ||
|  | 			__proto__: null, | ||
|  | 			timeout: { | ||
|  | 				__proto__: null, | ||
|  | 				value: timeout, | ||
|  | 				writable: true, | ||
|  | 				enumerable: true | ||
|  | 			}, | ||
|  | 			compiler: { | ||
|  | 				__proto__: null, | ||
|  | 				value: compiler, | ||
|  | 				enumerable: true | ||
|  | 			}, | ||
|  | 			sandbox: { | ||
|  | 				__proto__: null, | ||
|  | 				value: bridge.from(sandboxGlobal), | ||
|  | 				enumerable: true | ||
|  | 			}, | ||
|  | 			_runScript: {__proto__: null, value: runScript}, | ||
|  | 			_makeReadonly: {__proto__: null, value: makeReadonly}, | ||
|  | 			_makeProtected: {__proto__: null, value: makeProtected}, | ||
|  | 			_addProtoMapping: {__proto__: null, value: addProtoMapping}, | ||
|  | 			_addProtoMappingFactory: {__proto__: null, value: addProtoMappingFactory}, | ||
|  | 			_compiler: {__proto__: null, value: resolvedCompiler}, | ||
|  | 			_allowAsync: {__proto__: null, value: allowAsync} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		// prepare global sandbox
 | ||
|  | 		if (sandbox) { | ||
|  | 			this.setGlobals(sandbox); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Adds all the values to the globals. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @param {Object} values - All values that will be added to the globals. | ||
|  | 	 * @return {this} This for chaining. | ||
|  | 	 * @throws {*} If the setter of a global throws an exception it is propagated. And the remaining globals will not be written. | ||
|  | 	 */ | ||
|  | 	setGlobals(values) { | ||
|  | 		for (const name in values) { | ||
|  | 			if (Object.prototype.hasOwnProperty.call(values, name)) { | ||
|  | 				this.sandbox[name] = values[name]; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set a global value. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @param {string} name - The name of the global. | ||
|  | 	 * @param {*} value - The value of the global. | ||
|  | 	 * @return {this} This for chaining. | ||
|  | 	 * @throws {*} If the setter of the global throws an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	setGlobal(name, value) { | ||
|  | 		this.sandbox[name] = value; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Get a global value. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @param {string} name - The name of the global. | ||
|  | 	 * @return {*} The value of the global. | ||
|  | 	 * @throws {*} If the getter of the global throws an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	getGlobal(name) { | ||
|  | 		return this.sandbox[name]; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Freezes the object inside VM making it read-only. Not available for primitive values. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {*} value - Object to freeze. | ||
|  | 	 * @param {string} [globalName] - Whether to add the object to global. | ||
|  | 	 * @return {*} Object to freeze. | ||
|  | 	 * @throws {*} If the setter of the global throws an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	freeze(value, globalName) { | ||
|  | 		this.readonly(value); | ||
|  | 		if (globalName) this.sandbox[globalName] = value; | ||
|  | 		return value; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Freezes the object inside VM making it read-only. Not available for primitive values. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {*} value - Object to freeze. | ||
|  | 	 * @param {*} [mock] - When the object does not have a property the mock is used before prototype lookup. | ||
|  | 	 * @return {*} Object to freeze. | ||
|  | 	 */ | ||
|  | 	readonly(value, mock) { | ||
|  | 		return this._makeReadonly(value, mock); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {*} value - Object to protect. | ||
|  | 	 * @param {string} [globalName] - Whether to add the object to global. | ||
|  | 	 * @return {*} Object to protect. | ||
|  | 	 * @throws {*} If the setter of the global throws an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	protect(value, globalName) { | ||
|  | 		this._makeProtected(value); | ||
|  | 		if (globalName) this.sandbox[globalName] = value; | ||
|  | 		return value; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Run the code in VM. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {(string|VMScript)} code - Code to run. | ||
|  | 	 * @param {(string|Object)} [options] - Options map or filename. | ||
|  | 	 * @param {string} [options.filename="vm.js"] - Filename that shows up in any stack traces produced from this script.<br> | ||
|  | 	 * This is only used if code is a String. | ||
|  | 	 * @return {*} Result of executed code. | ||
|  | 	 * @throws {SyntaxError} If there is a syntax error in the script. | ||
|  | 	 * @throws {Error} An error is thrown when the script took to long and there is a timeout. | ||
|  | 	 * @throws {*} If the script execution terminated with an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	run(code, options) { | ||
|  | 		let script; | ||
|  | 		let filename; | ||
|  | 
 | ||
|  | 		if (typeof options === 'object') { | ||
|  | 			filename = options.filename; | ||
|  | 		} else { | ||
|  | 			filename = options; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (code instanceof VMScript) { | ||
|  | 			script = code._compileVM(); | ||
|  | 			checkAsync(this._allowAsync || !code._hasAsync); | ||
|  | 		} else { | ||
|  | 			const useFileName = filename || 'vm.js'; | ||
|  | 			let scriptCode = this._compiler(code, useFileName); | ||
|  | 			const ret = transformer(null, scriptCode, false, false, useFileName); | ||
|  | 			scriptCode = ret.code; | ||
|  | 			checkAsync(this._allowAsync || !ret.hasAsync); | ||
|  | 			// Compile the script here so that we don't need to create a instance of VMScript.
 | ||
|  | 			script = new Script(scriptCode, { | ||
|  | 				__proto__: null, | ||
|  | 				filename: useFileName, | ||
|  | 				displayErrors: false | ||
|  | 			}); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (!this.timeout) { | ||
|  | 			return this._runScript(script); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return doWithTimeout(() => { | ||
|  | 			return this._runScript(script); | ||
|  | 		}, this.timeout); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Run the code in VM. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @since v3.9.0 | ||
|  | 	 * @param {string} filename - Filename of file to load and execute in a NodeVM. | ||
|  | 	 * @return {*} Result of executed code. | ||
|  | 	 * @throws {Error} If filename is not a valid filename. | ||
|  | 	 * @throws {SyntaxError} If there is a syntax error in the script. | ||
|  | 	 * @throws {Error} An error is thrown when the script took to long and there is a timeout. | ||
|  | 	 * @throws {*} If the script execution terminated with an exception it is propagated. | ||
|  | 	 */ | ||
|  | 	runFile(filename) { | ||
|  | 		const resolvedFilename = pa.resolve(filename); | ||
|  | 
 | ||
|  | 		if (!fs.existsSync(resolvedFilename)) { | ||
|  | 			throw new VMError(`Script '${filename}' not found.`); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (fs.statSync(resolvedFilename).isDirectory()) { | ||
|  | 			throw new VMError('Script must be file, got directory.'); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return this.run(fs.readFileSync(resolvedFilename, 'utf8'), resolvedFilename); | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | exports.VM = VM; |