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.
		
		
		
		
		
			
		
			
				
					
					
						
							470 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							470 lines
						
					
					
						
							11 KiB
						
					
					
				/* global host, data, VMError */
 | 
						|
 | 
						|
'use strict';
 | 
						|
 | 
						|
const LocalError = Error;
 | 
						|
const LocalTypeError = TypeError;
 | 
						|
const LocalWeakMap = WeakMap;
 | 
						|
 | 
						|
const {
 | 
						|
	apply: localReflectApply,
 | 
						|
	defineProperty: localReflectDefineProperty
 | 
						|
} = Reflect;
 | 
						|
 | 
						|
const {
 | 
						|
	set: localWeakMapSet,
 | 
						|
	get: localWeakMapGet
 | 
						|
} = LocalWeakMap.prototype;
 | 
						|
 | 
						|
const {
 | 
						|
	isArray: localArrayIsArray
 | 
						|
} = Array;
 | 
						|
 | 
						|
function uncurryThis(func) {
 | 
						|
	return (thiz, ...args) => localReflectApply(func, thiz, args);
 | 
						|
}
 | 
						|
 | 
						|
const localArrayPrototypeSlice = uncurryThis(Array.prototype.slice);
 | 
						|
const localArrayPrototypeIncludes = uncurryThis(Array.prototype.includes);
 | 
						|
const localArrayPrototypePush = uncurryThis(Array.prototype.push);
 | 
						|
const localArrayPrototypeIndexOf = uncurryThis(Array.prototype.indexOf);
 | 
						|
const localArrayPrototypeSplice = uncurryThis(Array.prototype.splice);
 | 
						|
const localStringPrototypeStartsWith = uncurryThis(String.prototype.startsWith);
 | 
						|
const localStringPrototypeSlice = uncurryThis(String.prototype.slice);
 | 
						|
const localStringPrototypeIndexOf = uncurryThis(String.prototype.indexOf);
 | 
						|
 | 
						|
const {
 | 
						|
	argv: optionArgv,
 | 
						|
	env: optionEnv,
 | 
						|
	console: optionConsole,
 | 
						|
	vm,
 | 
						|
	resolver,
 | 
						|
	extensions
 | 
						|
} = data;
 | 
						|
 | 
						|
function ensureSandboxArray(a) {
 | 
						|
	return localArrayPrototypeSlice(a);
 | 
						|
}
 | 
						|
 | 
						|
const globalPaths = ensureSandboxArray(resolver.globalPaths);
 | 
						|
 | 
						|
class Module {
 | 
						|
 | 
						|
	constructor(id, path, parent) {
 | 
						|
		this.id = id;
 | 
						|
		this.filename = id;
 | 
						|
		this.path = path;
 | 
						|
		this.parent = parent;
 | 
						|
		this.loaded = false;
 | 
						|
		this.paths = path ? ensureSandboxArray(resolver.genLookupPaths(path)) : [];
 | 
						|
		this.children = [];
 | 
						|
		this.exports = {};
 | 
						|
	}
 | 
						|
 | 
						|
	_updateChildren(child, isNew) {
 | 
						|
		const children = this.children;
 | 
						|
		if (children && (isNew || !localArrayPrototypeIncludes(children, child))) {
 | 
						|
			localArrayPrototypePush(children, child);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	require(id) {
 | 
						|
		return requireImpl(this, id, false);
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
const originalRequire = Module.prototype.require;
 | 
						|
const cacheBuiltins = {__proto__: null};
 | 
						|
 | 
						|
function requireImpl(mod, id, direct) {
 | 
						|
	if (direct && mod.require !== originalRequire) {
 | 
						|
		return mod.require(id);
 | 
						|
	}
 | 
						|
	const filename = resolver.resolve(mod, id, undefined, Module._extensions, direct);
 | 
						|
	if (localStringPrototypeStartsWith(filename, 'node:')) {
 | 
						|
		id = localStringPrototypeSlice(filename, 5);
 | 
						|
		let nmod = cacheBuiltins[id];
 | 
						|
		if (!nmod) {
 | 
						|
			nmod = resolver.loadBuiltinModule(vm, id);
 | 
						|
			if (!nmod) throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');
 | 
						|
			cacheBuiltins[id] = nmod;
 | 
						|
		}
 | 
						|
		return nmod;
 | 
						|
	}
 | 
						|
 | 
						|
	const cachedModule = Module._cache[filename];
 | 
						|
	if (cachedModule !== undefined) {
 | 
						|
		mod._updateChildren(cachedModule, false);
 | 
						|
		return cachedModule.exports;
 | 
						|
	}
 | 
						|
 | 
						|
	let nmod = cacheBuiltins[id];
 | 
						|
	if (nmod) return nmod;
 | 
						|
	nmod = resolver.loadBuiltinModule(vm, id);
 | 
						|
	if (nmod) {
 | 
						|
		cacheBuiltins[id] = nmod;
 | 
						|
		return nmod;
 | 
						|
	}
 | 
						|
 | 
						|
	const path = resolver.fs.dirname(filename);
 | 
						|
	const module = new Module(filename, path, mod);
 | 
						|
	resolver.registerModule(module, filename, path, mod, direct);
 | 
						|
	mod._updateChildren(module, true);
 | 
						|
	try {
 | 
						|
		Module._cache[filename] = module;
 | 
						|
		const handler = findBestExtensionHandler(filename);
 | 
						|
		handler(module, filename);
 | 
						|
		module.loaded = true;
 | 
						|
	} catch (e) {
 | 
						|
		delete Module._cache[filename];
 | 
						|
		const children = mod.children;
 | 
						|
		if (localArrayIsArray(children)) {
 | 
						|
			const index = localArrayPrototypeIndexOf(children, module);
 | 
						|
			if (index !== -1) {
 | 
						|
				localArrayPrototypeSplice(children, index, 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		throw e;
 | 
						|
	}
 | 
						|
 | 
						|
	return module.exports;
 | 
						|
}
 | 
						|
 | 
						|
Module.builtinModules = ensureSandboxArray(resolver.getBuiltinModulesList());
 | 
						|
Module.globalPaths = globalPaths;
 | 
						|
Module._extensions = {__proto__: null};
 | 
						|
Module._cache = {__proto__: null};
 | 
						|
 | 
						|
{
 | 
						|
	const keys = Object.getOwnPropertyNames(extensions);
 | 
						|
	for (let i = 0; i < keys.length; i++) {
 | 
						|
		const key = keys[i];
 | 
						|
		const handler = extensions[key];
 | 
						|
		Module._extensions[key] = (mod, filename) => handler(mod, filename);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
function findBestExtensionHandler(filename) {
 | 
						|
	const name = resolver.fs.basename(filename);
 | 
						|
	for (let i = 0; (i = localStringPrototypeIndexOf(name, '.', i + 1)) !== -1;) {
 | 
						|
		const ext = localStringPrototypeSlice(name, i);
 | 
						|
		const handler = Module._extensions[ext];
 | 
						|
		if (handler) return handler;
 | 
						|
	}
 | 
						|
	const js = Module._extensions['.js'];
 | 
						|
	if (js) return js;
 | 
						|
	const keys = Object.getOwnPropertyNames(Module._extensions);
 | 
						|
	if (keys.length === 0) throw new VMError(`Failed to load '${filename}': Unknown type.`, 'ELOADFAIL');
 | 
						|
	return Module._extensions[keys[0]];
 | 
						|
}
 | 
						|
 | 
						|
function createRequireForModule(mod) {
 | 
						|
	// eslint-disable-next-line no-shadow
 | 
						|
	function require(id) {
 | 
						|
		return requireImpl(mod, id, true);
 | 
						|
	}
 | 
						|
	function resolve(id, options) {
 | 
						|
		return resolver.resolve(mod, id, options, Module._extensions, true);
 | 
						|
	}
 | 
						|
	require.resolve = resolve;
 | 
						|
	function paths(id) {
 | 
						|
		return ensureSandboxArray(resolver.lookupPaths(mod, id));
 | 
						|
	}
 | 
						|
	resolve.paths = paths;
 | 
						|
 | 
						|
	require.extensions = Module._extensions;
 | 
						|
 | 
						|
	require.cache = Module._cache;
 | 
						|
 | 
						|
	return require;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Prepare sandbox.
 | 
						|
 */
 | 
						|
 | 
						|
const TIMERS = new LocalWeakMap();
 | 
						|
 | 
						|
class Timeout {
 | 
						|
}
 | 
						|
 | 
						|
class Interval {
 | 
						|
}
 | 
						|
 | 
						|
class Immediate {
 | 
						|
}
 | 
						|
 | 
						|
function clearTimer(timer) {
 | 
						|
	const obj = localReflectApply(localWeakMapGet, TIMERS, [timer]);
 | 
						|
	if (obj) {
 | 
						|
		obj.clear(obj.value);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// This is a function and not an arrow function, since the original is also a function
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.setTimeout = function setTimeout(callback, delay, ...args) {
 | 
						|
	if (typeof callback !== 'function') throw new LocalTypeError('"callback" argument must be a function');
 | 
						|
	const obj = new Timeout(callback, args);
 | 
						|
	const cb = () => {
 | 
						|
		localReflectApply(callback, null, args);
 | 
						|
	};
 | 
						|
	const tmr = host.setTimeout(cb, delay);
 | 
						|
 | 
						|
	const ref = {
 | 
						|
		__proto__: null,
 | 
						|
		clear: host.clearTimeout,
 | 
						|
		value: tmr
 | 
						|
	};
 | 
						|
 | 
						|
	localReflectApply(localWeakMapSet, TIMERS, [obj, ref]);
 | 
						|
	return obj;
 | 
						|
};
 | 
						|
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.setInterval = function setInterval(callback, interval, ...args) {
 | 
						|
	if (typeof callback !== 'function') throw new LocalTypeError('"callback" argument must be a function');
 | 
						|
	const obj = new Interval();
 | 
						|
	const cb = () => {
 | 
						|
		localReflectApply(callback, null, args);
 | 
						|
	};
 | 
						|
	const tmr = host.setInterval(cb, interval);
 | 
						|
 | 
						|
	const ref = {
 | 
						|
		__proto__: null,
 | 
						|
		clear: host.clearInterval,
 | 
						|
		value: tmr
 | 
						|
	};
 | 
						|
 | 
						|
	localReflectApply(localWeakMapSet, TIMERS, [obj, ref]);
 | 
						|
	return obj;
 | 
						|
};
 | 
						|
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.setImmediate = function setImmediate(callback, ...args) {
 | 
						|
	if (typeof callback !== 'function') throw new LocalTypeError('"callback" argument must be a function');
 | 
						|
	const obj = new Immediate();
 | 
						|
	const cb = () => {
 | 
						|
		localReflectApply(callback, null, args);
 | 
						|
	};
 | 
						|
	const tmr = host.setImmediate(cb);
 | 
						|
 | 
						|
	const ref = {
 | 
						|
		__proto__: null,
 | 
						|
		clear: host.clearImmediate,
 | 
						|
		value: tmr
 | 
						|
	};
 | 
						|
 | 
						|
	localReflectApply(localWeakMapSet, TIMERS, [obj, ref]);
 | 
						|
	return obj;
 | 
						|
};
 | 
						|
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.clearTimeout = function clearTimeout(timeout) {
 | 
						|
	clearTimer(timeout);
 | 
						|
};
 | 
						|
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.clearInterval = function clearInterval(interval) {
 | 
						|
	clearTimer(interval);
 | 
						|
};
 | 
						|
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
global.clearImmediate = function clearImmediate(immediate) {
 | 
						|
	clearTimer(immediate);
 | 
						|
};
 | 
						|
 | 
						|
const localProcess = host.process;
 | 
						|
 | 
						|
function vmEmitArgs(event, args) {
 | 
						|
	const allargs = [event];
 | 
						|
	for (let i = 0; i < args.length; i++) {
 | 
						|
		if (!localReflectDefineProperty(allargs, i + 1, {
 | 
						|
			__proto__: null,
 | 
						|
			value: args[i],
 | 
						|
			writable: true,
 | 
						|
			enumerable: true,
 | 
						|
			configurable: true
 | 
						|
		})) throw new LocalError('Unexpected');
 | 
						|
	}
 | 
						|
	return localReflectApply(vm.emit, vm, allargs);
 | 
						|
}
 | 
						|
 | 
						|
const LISTENERS = new LocalWeakMap();
 | 
						|
const LISTENER_HANDLER = new LocalWeakMap();
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * @param {*} name
 | 
						|
 * @param {*} handler
 | 
						|
 * @this process
 | 
						|
 * @return {this}
 | 
						|
 */
 | 
						|
function addListener(name, handler) {
 | 
						|
	if (name !== 'beforeExit' && name !== 'exit') {
 | 
						|
		throw new LocalError(`Access denied to listen for '${name}' event.`);
 | 
						|
	}
 | 
						|
 | 
						|
	let cb = localReflectApply(localWeakMapGet, LISTENERS, [handler]);
 | 
						|
	if (!cb) {
 | 
						|
		cb = () => {
 | 
						|
			handler();
 | 
						|
		};
 | 
						|
		localReflectApply(localWeakMapSet, LISTENER_HANDLER, [cb, handler]);
 | 
						|
		localReflectApply(localWeakMapSet, LISTENERS, [handler, cb]);
 | 
						|
	}
 | 
						|
 | 
						|
	localProcess.on(name, cb);
 | 
						|
 | 
						|
	return this;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * @this process
 | 
						|
 * @return {this}
 | 
						|
 */
 | 
						|
// eslint-disable-next-line no-shadow
 | 
						|
function process() {
 | 
						|
	return this;
 | 
						|
}
 | 
						|
 | 
						|
const baseUptime = localProcess.uptime();
 | 
						|
 | 
						|
// FIXME wrong class structure
 | 
						|
global.process = {
 | 
						|
	__proto__: process.prototype,
 | 
						|
	argv: optionArgv !== undefined ? optionArgv : [],
 | 
						|
	title: localProcess.title,
 | 
						|
	version: localProcess.version,
 | 
						|
	versions: localProcess.versions,
 | 
						|
	arch: localProcess.arch,
 | 
						|
	platform: localProcess.platform,
 | 
						|
	env: optionEnv !== undefined ? optionEnv : {},
 | 
						|
	pid: localProcess.pid,
 | 
						|
	features: localProcess.features,
 | 
						|
	nextTick: function nextTick(callback, ...args) {
 | 
						|
		if (typeof callback !== 'function') {
 | 
						|
			throw new LocalError('Callback must be a function.');
 | 
						|
		}
 | 
						|
 | 
						|
		localProcess.nextTick(()=>{
 | 
						|
			localReflectApply(callback, null, args);
 | 
						|
		});
 | 
						|
	},
 | 
						|
	hrtime: function hrtime(time) {
 | 
						|
		return localProcess.hrtime(time);
 | 
						|
	},
 | 
						|
	uptime: function uptime() {
 | 
						|
		return localProcess.uptime() - baseUptime;
 | 
						|
	},
 | 
						|
	cwd: function cwd() {
 | 
						|
		return localProcess.cwd();
 | 
						|
	},
 | 
						|
	addListener,
 | 
						|
	on: addListener,
 | 
						|
 | 
						|
	once: function once(name, handler) {
 | 
						|
		if (name !== 'beforeExit' && name !== 'exit') {
 | 
						|
			throw new LocalError(`Access denied to listen for '${name}' event.`);
 | 
						|
		}
 | 
						|
 | 
						|
		let triggered = false;
 | 
						|
		const cb = () => {
 | 
						|
			if (triggered) return;
 | 
						|
			triggered = true;
 | 
						|
			localProcess.removeListener(name, cb);
 | 
						|
			handler();
 | 
						|
		};
 | 
						|
		localReflectApply(localWeakMapSet, LISTENER_HANDLER, [cb, handler]);
 | 
						|
 | 
						|
		localProcess.on(name, cb);
 | 
						|
 | 
						|
		return this;
 | 
						|
	},
 | 
						|
 | 
						|
	listeners: function listeners(name) {
 | 
						|
		if (name !== 'beforeExit' && name !== 'exit') {
 | 
						|
			// Maybe add ({__proto__:null})[name] to throw when name fails in https://tc39.es/ecma262/#sec-topropertykey.
 | 
						|
			return [];
 | 
						|
		}
 | 
						|
 | 
						|
		// Filter out listeners, which were not created in this sandbox
 | 
						|
		const all = localProcess.listeners(name);
 | 
						|
		const filtered = [];
 | 
						|
		let j = 0;
 | 
						|
		for (let i = 0; i < all.length; i++) {
 | 
						|
			const h = localReflectApply(localWeakMapGet, LISTENER_HANDLER, [all[i]]);
 | 
						|
			if (h) {
 | 
						|
				if (!localReflectDefineProperty(filtered, j, {
 | 
						|
					__proto__: null,
 | 
						|
					value: h,
 | 
						|
					writable: true,
 | 
						|
					enumerable: true,
 | 
						|
					configurable: true
 | 
						|
				})) throw new LocalError('Unexpected');
 | 
						|
				j++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return filtered;
 | 
						|
	},
 | 
						|
 | 
						|
	removeListener: function removeListener(name, handler) {
 | 
						|
		if (name !== 'beforeExit' && name !== 'exit') {
 | 
						|
			return this;
 | 
						|
		}
 | 
						|
 | 
						|
		const cb = localReflectApply(localWeakMapGet, LISTENERS, [handler]);
 | 
						|
		if (cb) localProcess.removeListener(name, cb);
 | 
						|
 | 
						|
		return this;
 | 
						|
	},
 | 
						|
 | 
						|
	umask: function umask() {
 | 
						|
		if (arguments.length) {
 | 
						|
			throw new LocalError('Access denied to set umask.');
 | 
						|
		}
 | 
						|
 | 
						|
		return localProcess.umask();
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
if (optionConsole === 'inherit') {
 | 
						|
	global.console = host.console;
 | 
						|
} else if (optionConsole === 'redirect') {
 | 
						|
	global.console = {
 | 
						|
		debug(...args) {
 | 
						|
			vmEmitArgs('console.debug', args);
 | 
						|
		},
 | 
						|
		log(...args) {
 | 
						|
			vmEmitArgs('console.log', args);
 | 
						|
		},
 | 
						|
		info(...args) {
 | 
						|
			vmEmitArgs('console.info', args);
 | 
						|
		},
 | 
						|
		warn(...args) {
 | 
						|
			vmEmitArgs('console.warn', args);
 | 
						|
		},
 | 
						|
		error(...args) {
 | 
						|
			vmEmitArgs('console.error', args);
 | 
						|
		},
 | 
						|
		dir(...args) {
 | 
						|
			vmEmitArgs('console.dir', args);
 | 
						|
		},
 | 
						|
		time() {},
 | 
						|
		timeEnd() {},
 | 
						|
		trace(...args) {
 | 
						|
			vmEmitArgs('console.trace', args);
 | 
						|
		}
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
return {
 | 
						|
	__proto__: null,
 | 
						|
	Module,
 | 
						|
	jsonParse: JSON.parse,
 | 
						|
	createRequireForModule,
 | 
						|
	requireImpl
 | 
						|
};
 |