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.
		
		
		
		
		
			
		
			
				
					1011 lines
				
				28 KiB
			
		
		
			
		
	
	
					1011 lines
				
				28 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * __        ___    ____  _   _ ___ _   _  ____ | ||
|  |  * \ \      / / \  |  _ \| \ | |_ _| \ | |/ ___| | ||
|  |  *  \ \ /\ / / _ \ | |_) |  \| || ||  \| | |  _ | ||
|  |  *   \ V  V / ___ \|  _ <| |\  || || |\  | |_| | | ||
|  |  *    \_/\_/_/   \_\_| \_\_| \_|___|_| \_|\____| | ||
|  |  * | ||
|  |  * This file is critical for vm2. It implements the bridge between the host and the sandbox. | ||
|  |  * If you do not know exactly what you are doing, you should NOT edit this file. | ||
|  |  * | ||
|  |  * The file is loaded in the host and sandbox to handle objects in both directions. | ||
|  |  * This is done to ensure that RangeErrors are from the correct context. | ||
|  |  * The boundary between the sandbox and host might throw RangeErrors from both contexts. | ||
|  |  * Therefore, thisFromOther and friends can handle objects from both domains. | ||
|  |  * | ||
|  |  * Method parameters have comments to tell from which context they came. | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | const globalsList = [ | ||
|  | 	'Number', | ||
|  | 	'String', | ||
|  | 	'Boolean', | ||
|  | 	'Date', | ||
|  | 	'RegExp', | ||
|  | 	'Map', | ||
|  | 	'WeakMap', | ||
|  | 	'Set', | ||
|  | 	'WeakSet', | ||
|  | 	'Promise', | ||
|  | 	'Function' | ||
|  | ]; | ||
|  | 
 | ||
|  | const errorsList = [ | ||
|  | 	'RangeError', | ||
|  | 	'ReferenceError', | ||
|  | 	'SyntaxError', | ||
|  | 	'TypeError', | ||
|  | 	'EvalError', | ||
|  | 	'URIError', | ||
|  | 	'Error' | ||
|  | ]; | ||
|  | 
 | ||
|  | const OPNA = 'Operation not allowed on contextified object.'; | ||
|  | 
 | ||
|  | const thisGlobalPrototypes = { | ||
|  | 	__proto__: null, | ||
|  | 	Object: Object.prototype, | ||
|  | 	Array: Array.prototype | ||
|  | }; | ||
|  | 
 | ||
|  | for (let i = 0; i < globalsList.length; i++) { | ||
|  | 	const key = globalsList[i]; | ||
|  | 	const g = global[key]; | ||
|  | 	if (g) thisGlobalPrototypes[key] = g.prototype; | ||
|  | } | ||
|  | 
 | ||
|  | for (let i = 0; i < errorsList.length; i++) { | ||
|  | 	const key = errorsList[i]; | ||
|  | 	const g = global[key]; | ||
|  | 	if (g) thisGlobalPrototypes[key] = g.prototype; | ||
|  | } | ||
|  | 
 | ||
|  | const { | ||
|  | 	getPrototypeOf: thisReflectGetPrototypeOf, | ||
|  | 	setPrototypeOf: thisReflectSetPrototypeOf, | ||
|  | 	defineProperty: thisReflectDefineProperty, | ||
|  | 	deleteProperty: thisReflectDeleteProperty, | ||
|  | 	getOwnPropertyDescriptor: thisReflectGetOwnPropertyDescriptor, | ||
|  | 	isExtensible: thisReflectIsExtensible, | ||
|  | 	preventExtensions: thisReflectPreventExtensions, | ||
|  | 	apply: thisReflectApply, | ||
|  | 	construct: thisReflectConstruct, | ||
|  | 	set: thisReflectSet, | ||
|  | 	get: thisReflectGet, | ||
|  | 	has: thisReflectHas, | ||
|  | 	ownKeys: thisReflectOwnKeys, | ||
|  | 	enumerate: thisReflectEnumerate, | ||
|  | } = Reflect; | ||
|  | 
 | ||
|  | const thisObject = Object; | ||
|  | const { | ||
|  | 	freeze: thisObjectFreeze, | ||
|  | 	prototype: thisObjectPrototype | ||
|  | } = thisObject; | ||
|  | const thisObjectHasOwnProperty = thisObjectPrototype.hasOwnProperty; | ||
|  | const ThisProxy = Proxy; | ||
|  | const ThisWeakMap = WeakMap; | ||
|  | const { | ||
|  | 	get: thisWeakMapGet, | ||
|  | 	set: thisWeakMapSet | ||
|  | } = ThisWeakMap.prototype; | ||
|  | const ThisMap = Map; | ||
|  | const thisMapGet = ThisMap.prototype.get; | ||
|  | const thisMapSet = ThisMap.prototype.set; | ||
|  | const thisFunction = Function; | ||
|  | const thisFunctionBind = thisFunction.prototype.bind; | ||
|  | const thisArrayIsArray = Array.isArray; | ||
|  | const thisErrorCaptureStackTrace = Error.captureStackTrace; | ||
|  | 
 | ||
|  | const thisSymbolToString = Symbol.prototype.toString; | ||
|  | const thisSymbolToStringTag = Symbol.toStringTag; | ||
|  | const thisSymbolIterator = Symbol.iterator; | ||
|  | const thisSymbolNodeJSUtilInspectCustom = Symbol.for('nodejs.util.inspect.custom'); | ||
|  | 
 | ||
|  | /** | ||
|  |  * VMError. | ||
|  |  * | ||
|  |  * @public | ||
|  |  * @extends {Error} | ||
|  |  */ | ||
|  | class VMError extends Error { | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Create VMError instance. | ||
|  | 	 * | ||
|  | 	 * @public | ||
|  | 	 * @param {string} message - Error message. | ||
|  | 	 * @param {string} code - Error code. | ||
|  | 	 */ | ||
|  | 	constructor(message, code) { | ||
|  | 		super(message); | ||
|  | 
 | ||
|  | 		this.name = 'VMError'; | ||
|  | 		this.code = code; | ||
|  | 
 | ||
|  | 		thisErrorCaptureStackTrace(this, this.constructor); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | thisGlobalPrototypes['VMError'] = VMError.prototype; | ||
|  | 
 | ||
|  | function thisUnexpected() { | ||
|  | 	return new VMError('Unexpected'); | ||
|  | } | ||
|  | 
 | ||
|  | if (!thisReflectSetPrototypeOf(exports, null)) throw thisUnexpected(); | ||
|  | 
 | ||
|  | function thisSafeGetOwnPropertyDescriptor(obj, key) { | ||
|  | 	const desc = thisReflectGetOwnPropertyDescriptor(obj, key); | ||
|  | 	if (!desc) return desc; | ||
|  | 	if (!thisReflectSetPrototypeOf(desc, null)) throw thisUnexpected(); | ||
|  | 	return desc; | ||
|  | } | ||
|  | 
 | ||
|  | function thisThrowCallerCalleeArgumentsAccess(key) { | ||
|  | 	'use strict'; | ||
|  | 	thisThrowCallerCalleeArgumentsAccess[key]; | ||
|  | 	return thisUnexpected(); | ||
|  | } | ||
|  | 
 | ||
|  | function thisIdMapping(factory, other) { | ||
|  | 	return other; | ||
|  | } | ||
|  | 
 | ||
|  | const thisThrowOnKeyAccessHandler = thisObjectFreeze({ | ||
|  | 	__proto__: null, | ||
|  | 	get(target, key, receiver) { | ||
|  | 		if (typeof key === 'symbol') { | ||
|  | 			key = thisReflectApply(thisSymbolToString, key, []); | ||
|  | 		} | ||
|  | 		throw new VMError(`Unexpected access to key '${key}'`); | ||
|  | 	} | ||
|  | }); | ||
|  | 
 | ||
|  | const emptyForzenObject = thisObjectFreeze({ | ||
|  | 	__proto__: null | ||
|  | }); | ||
|  | 
 | ||
|  | const thisThrowOnKeyAccess = new ThisProxy(emptyForzenObject, thisThrowOnKeyAccessHandler); | ||
|  | 
 | ||
|  | function SafeBase() {} | ||
|  | 
 | ||
|  | if (!thisReflectDefineProperty(SafeBase, 'prototype', { | ||
|  | 	__proto__: null, | ||
|  | 	value: thisThrowOnKeyAccess | ||
|  | })) throw thisUnexpected(); | ||
|  | 
 | ||
|  | function SHARED_FUNCTION() {} | ||
|  | 
 | ||
|  | const TEST_PROXY_HANDLER = thisObjectFreeze({ | ||
|  | 	__proto__: thisThrowOnKeyAccess, | ||
|  | 	construct() { | ||
|  | 		return this; | ||
|  | 	} | ||
|  | }); | ||
|  | 
 | ||
|  | function thisIsConstructor(obj) { | ||
|  | 	// Note: obj@any(unsafe)
 | ||
|  | 	const Func = new ThisProxy(obj, TEST_PROXY_HANDLER); | ||
|  | 	try { | ||
|  | 		// eslint-disable-next-line no-new
 | ||
|  | 		new Func(); | ||
|  | 		return true; | ||
|  | 	} catch (e) { | ||
|  | 		return false; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | function thisCreateTargetObject(obj, proto) { | ||
|  | 	// Note: obj@any(unsafe) proto@any(unsafe) returns@this(unsafe) throws@this(unsafe)
 | ||
|  | 	let base; | ||
|  | 	if (typeof obj === 'function') { | ||
|  | 		if (thisIsConstructor(obj)) { | ||
|  | 			// Bind the function since bound functions do not have a prototype property.
 | ||
|  | 			base = thisReflectApply(thisFunctionBind, SHARED_FUNCTION, [null]); | ||
|  | 		} else { | ||
|  | 			base = () => {}; | ||
|  | 		} | ||
|  | 	} else if (thisArrayIsArray(obj)) { | ||
|  | 		base = []; | ||
|  | 	} else { | ||
|  | 		return {__proto__: proto}; | ||
|  | 	} | ||
|  | 	if (!thisReflectSetPrototypeOf(base, proto)) throw thisUnexpected(); | ||
|  | 	return base; | ||
|  | } | ||
|  | 
 | ||
|  | function createBridge(otherInit, registerProxy) { | ||
|  | 
 | ||
|  | 	const mappingOtherToThis = new ThisWeakMap(); | ||
|  | 	const protoMappings = new ThisMap(); | ||
|  | 	const protoName = new ThisMap(); | ||
|  | 
 | ||
|  | 	function thisAddProtoMapping(proto, other, name) { | ||
|  | 		// Note: proto@this(unsafe) other@other(unsafe) name@this(unsafe) throws@this(unsafe)
 | ||
|  | 		thisReflectApply(thisMapSet, protoMappings, [proto, thisIdMapping]); | ||
|  | 		thisReflectApply(thisMapSet, protoMappings, [other, | ||
|  | 			(factory, object) => thisProxyOther(factory, object, proto)]); | ||
|  | 		if (name) thisReflectApply(thisMapSet, protoName, [proto, name]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisAddProtoMappingFactory(protoFactory, other, name) { | ||
|  | 		// Note: protoFactory@this(unsafe) other@other(unsafe) name@this(unsafe) throws@this(unsafe)
 | ||
|  | 		let proto; | ||
|  | 		thisReflectApply(thisMapSet, protoMappings, [other, | ||
|  | 			(factory, object) => { | ||
|  | 				if (!proto) { | ||
|  | 					proto = protoFactory(); | ||
|  | 					thisReflectApply(thisMapSet, protoMappings, [proto, thisIdMapping]); | ||
|  | 					if (name) thisReflectApply(thisMapSet, protoName, [proto, name]); | ||
|  | 				} | ||
|  | 				return thisProxyOther(factory, object, proto); | ||
|  | 			}]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	const result = { | ||
|  | 		__proto__: null, | ||
|  | 		globalPrototypes: thisGlobalPrototypes, | ||
|  | 		safeGetOwnPropertyDescriptor: thisSafeGetOwnPropertyDescriptor, | ||
|  | 		fromArguments: thisFromOtherArguments, | ||
|  | 		from: thisFromOther, | ||
|  | 		fromWithFactory: thisFromOtherWithFactory, | ||
|  | 		ensureThis: thisEnsureThis, | ||
|  | 		mapping: mappingOtherToThis, | ||
|  | 		connect: thisConnect, | ||
|  | 		reflectSet: thisReflectSet, | ||
|  | 		reflectGet: thisReflectGet, | ||
|  | 		reflectDefineProperty: thisReflectDefineProperty, | ||
|  | 		reflectDeleteProperty: thisReflectDeleteProperty, | ||
|  | 		reflectApply: thisReflectApply, | ||
|  | 		reflectConstruct: thisReflectConstruct, | ||
|  | 		reflectHas: thisReflectHas, | ||
|  | 		reflectOwnKeys: thisReflectOwnKeys, | ||
|  | 		reflectEnumerate: thisReflectEnumerate, | ||
|  | 		reflectGetPrototypeOf: thisReflectGetPrototypeOf, | ||
|  | 		reflectIsExtensible: thisReflectIsExtensible, | ||
|  | 		reflectPreventExtensions: thisReflectPreventExtensions, | ||
|  | 		objectHasOwnProperty: thisObjectHasOwnProperty, | ||
|  | 		weakMapSet: thisWeakMapSet, | ||
|  | 		addProtoMapping: thisAddProtoMapping, | ||
|  | 		addProtoMappingFactory: thisAddProtoMappingFactory, | ||
|  | 		defaultFactory, | ||
|  | 		protectedFactory, | ||
|  | 		readonlyFactory, | ||
|  | 		VMError | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	const isHost = typeof otherInit !== 'object'; | ||
|  | 
 | ||
|  | 	if (isHost) { | ||
|  | 		otherInit = otherInit(result, registerProxy); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	result.other = otherInit; | ||
|  | 
 | ||
|  | 	const { | ||
|  | 		globalPrototypes: otherGlobalPrototypes, | ||
|  | 		safeGetOwnPropertyDescriptor: otherSafeGetOwnPropertyDescriptor, | ||
|  | 		fromArguments: otherFromThisArguments, | ||
|  | 		from: otherFromThis, | ||
|  | 		mapping: mappingThisToOther, | ||
|  | 		reflectSet: otherReflectSet, | ||
|  | 		reflectGet: otherReflectGet, | ||
|  | 		reflectDefineProperty: otherReflectDefineProperty, | ||
|  | 		reflectDeleteProperty: otherReflectDeleteProperty, | ||
|  | 		reflectApply: otherReflectApply, | ||
|  | 		reflectConstruct: otherReflectConstruct, | ||
|  | 		reflectHas: otherReflectHas, | ||
|  | 		reflectOwnKeys: otherReflectOwnKeys, | ||
|  | 		reflectEnumerate: otherReflectEnumerate, | ||
|  | 		reflectGetPrototypeOf: otherReflectGetPrototypeOf, | ||
|  | 		reflectIsExtensible: otherReflectIsExtensible, | ||
|  | 		reflectPreventExtensions: otherReflectPreventExtensions, | ||
|  | 		objectHasOwnProperty: otherObjectHasOwnProperty, | ||
|  | 		weakMapSet: otherWeakMapSet | ||
|  | 	} = otherInit; | ||
|  | 
 | ||
|  | 	function thisOtherHasOwnProperty(object, key) { | ||
|  | 		// Note: object@other(safe) key@prim throws@this(unsafe)
 | ||
|  | 		try { | ||
|  | 			return otherReflectApply(otherObjectHasOwnProperty, object, [key]) === true; | ||
|  | 		} catch (e) { // @other(unsafe)
 | ||
|  | 			throw thisFromOtherForThrow(e); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisDefaultGet(handler, object, key, desc) { | ||
|  | 		// Note: object@other(unsafe) key@prim desc@other(safe)
 | ||
|  | 		let ret; // @other(unsafe)
 | ||
|  | 		if (desc.get || desc.set) { | ||
|  | 			const getter = desc.get; | ||
|  | 			if (!getter) return undefined; | ||
|  | 			try { | ||
|  | 				ret = otherReflectApply(getter, object, [key]); | ||
|  | 			} catch (e) { | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			ret = desc.value; | ||
|  | 		} | ||
|  | 		return handler.fromOtherWithContext(ret); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function otherFromThisIfAvailable(to, from, key) { | ||
|  | 		// Note: to@other(safe) from@this(safe) key@prim throws@this(unsafe)
 | ||
|  | 		if (!thisReflectApply(thisObjectHasOwnProperty, from, [key])) return false; | ||
|  | 		try { | ||
|  | 			to[key] = otherFromThis(from[key]); | ||
|  | 		} catch (e) { // @other(unsafe)
 | ||
|  | 			throw thisFromOtherForThrow(e); | ||
|  | 		} | ||
|  | 		return true; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	class BaseHandler extends SafeBase { | ||
|  | 
 | ||
|  | 		constructor(object) { | ||
|  | 			// Note: object@other(unsafe) throws@this(unsafe)
 | ||
|  | 			super(); | ||
|  | 			this.objectWrapper = () => object; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		getObject() { | ||
|  | 			return this.objectWrapper(); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		getFactory() { | ||
|  | 			return defaultFactory; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		fromOtherWithContext(other) { | ||
|  | 			// Note: other@other(unsafe) throws@this(unsafe)
 | ||
|  | 			return thisFromOtherWithFactory(this.getFactory(), other); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		doPreventExtensions(target, object, factory) { | ||
|  | 			// Note: target@this(unsafe) object@other(unsafe) throws@this(unsafe)
 | ||
|  | 			let keys; // @other(safe-array-of-prim)
 | ||
|  | 			try { | ||
|  | 				keys = otherReflectOwnKeys(object); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			for (let i = 0; i < keys.length; i++) { | ||
|  | 				const key = keys[i]; // @prim
 | ||
|  | 				let desc; | ||
|  | 				try { | ||
|  | 					desc = otherSafeGetOwnPropertyDescriptor(object, key); | ||
|  | 				} catch (e) { // @other(unsafe)
 | ||
|  | 					throw thisFromOtherForThrow(e); | ||
|  | 				} | ||
|  | 				if (!desc) continue; | ||
|  | 				if (!desc.configurable) { | ||
|  | 					const current = thisSafeGetOwnPropertyDescriptor(target, key); | ||
|  | 					if (current && !current.configurable) continue; | ||
|  | 					if (desc.get || desc.set) { | ||
|  | 						desc.get = this.fromOtherWithContext(desc.get); | ||
|  | 						desc.set = this.fromOtherWithContext(desc.set); | ||
|  | 					} else if (typeof object === 'function' && (key === 'caller' || key === 'callee' || key === 'arguments')) { | ||
|  | 						desc.value = null; | ||
|  | 					} else { | ||
|  | 						desc.value = this.fromOtherWithContext(desc.value); | ||
|  | 					} | ||
|  | 				} else { | ||
|  | 					if (desc.get || desc.set) { | ||
|  | 						desc = { | ||
|  | 							__proto__: null, | ||
|  | 							configurable: true, | ||
|  | 							enumerable: desc.enumerable, | ||
|  | 							writable: true, | ||
|  | 							value: null | ||
|  | 						}; | ||
|  | 					} else { | ||
|  | 						desc.value = null; | ||
|  | 					} | ||
|  | 				} | ||
|  | 				if (!thisReflectDefineProperty(target, key, desc)) throw thisUnexpected(); | ||
|  | 			} | ||
|  | 			if (!thisReflectPreventExtensions(target)) throw thisUnexpected(); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		get(target, key, receiver) { | ||
|  | 			// Note: target@this(unsafe) key@prim receiver@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			switch (key) { | ||
|  | 				case 'constructor': { | ||
|  | 					const desc = otherSafeGetOwnPropertyDescriptor(object, key); | ||
|  | 					if (desc) return thisDefaultGet(this, object, key, desc); | ||
|  | 					const proto = thisReflectGetPrototypeOf(target); | ||
|  | 					return proto === null ? undefined : proto.constructor; | ||
|  | 				} | ||
|  | 				case '__proto__': { | ||
|  | 					const desc = otherSafeGetOwnPropertyDescriptor(object, key); | ||
|  | 					if (desc) return thisDefaultGet(this, object, key, desc); | ||
|  | 					return thisReflectGetPrototypeOf(target); | ||
|  | 				} | ||
|  | 				case thisSymbolToStringTag: | ||
|  | 					if (!thisOtherHasOwnProperty(object, thisSymbolToStringTag)) { | ||
|  | 						const proto = thisReflectGetPrototypeOf(target); | ||
|  | 						const name = thisReflectApply(thisMapGet, protoName, [proto]); | ||
|  | 						if (name) return name; | ||
|  | 					} | ||
|  | 					break; | ||
|  | 				case 'arguments': | ||
|  | 				case 'caller': | ||
|  | 				case 'callee': | ||
|  | 					if (typeof object === 'function' && thisOtherHasOwnProperty(object, key)) { | ||
|  | 						throw thisThrowCallerCalleeArgumentsAccess(key); | ||
|  | 					} | ||
|  | 					break; | ||
|  | 			} | ||
|  | 			let ret; // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				ret = otherReflectGet(object, key); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			return this.fromOtherWithContext(ret); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		set(target, key, value, receiver) { | ||
|  | 			// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			if (key === '__proto__' && !thisOtherHasOwnProperty(object, key)) { | ||
|  | 				return this.setPrototypeOf(target, value); | ||
|  | 			} | ||
|  | 			try { | ||
|  | 				value = otherFromThis(value); | ||
|  | 				return otherReflectSet(object, key, value) === true; | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		getPrototypeOf(target) { | ||
|  | 			// Note: target@this(unsafe)
 | ||
|  | 			return thisReflectGetPrototypeOf(target); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		setPrototypeOf(target, value) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			throw new VMError(OPNA); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		apply(target, context, args) { | ||
|  | 			// Note: target@this(unsafe) context@this(unsafe) args@this(safe-array) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			let ret; // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				context = otherFromThis(context); | ||
|  | 				args = otherFromThisArguments(args); | ||
|  | 				ret = otherReflectApply(object, context, args); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			return thisFromOther(ret); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		construct(target, args, newTarget) { | ||
|  | 			// Note: target@this(unsafe) args@this(safe-array) newTarget@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			let ret; // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				args = otherFromThisArguments(args); | ||
|  | 				ret = otherReflectConstruct(object, args); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			return thisFromOtherWithFactory(this.getFactory(), ret, thisFromOther(object)); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		getOwnPropertyDescriptorDesc(target, prop, desc) { | ||
|  | 			// Note: target@this(unsafe) prop@prim desc@other{safe} throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			if (desc && typeof object === 'function' && (prop === 'arguments' || prop === 'caller' || prop === 'callee')) desc.value = null; | ||
|  | 			return desc; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		getOwnPropertyDescriptor(target, prop) { | ||
|  | 			// Note: target@this(unsafe) prop@prim throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			let desc; // @other(safe)
 | ||
|  | 			try { | ||
|  | 				desc = otherSafeGetOwnPropertyDescriptor(object, prop); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			desc = this.getOwnPropertyDescriptorDesc(target, prop, desc); | ||
|  | 
 | ||
|  | 			if (!desc) return undefined; | ||
|  | 
 | ||
|  | 			let thisDesc; | ||
|  | 			if (desc.get || desc.set) { | ||
|  | 				thisDesc = { | ||
|  | 					__proto__: null, | ||
|  | 					get: this.fromOtherWithContext(desc.get), | ||
|  | 					set: this.fromOtherWithContext(desc.set), | ||
|  | 					enumerable: desc.enumerable === true, | ||
|  | 					configurable: desc.configurable === true | ||
|  | 				}; | ||
|  | 			} else { | ||
|  | 				thisDesc = { | ||
|  | 					__proto__: null, | ||
|  | 					value: this.fromOtherWithContext(desc.value), | ||
|  | 					writable: desc.writable === true, | ||
|  | 					enumerable: desc.enumerable === true, | ||
|  | 					configurable: desc.configurable === true | ||
|  | 				}; | ||
|  | 			} | ||
|  | 			if (!thisDesc.configurable) { | ||
|  | 				const oldDesc = thisSafeGetOwnPropertyDescriptor(target, prop); | ||
|  | 				if (!oldDesc || oldDesc.configurable || oldDesc.writable !== thisDesc.writable) { | ||
|  | 					if (!thisReflectDefineProperty(target, prop, thisDesc)) throw thisUnexpected(); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			return thisDesc; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		definePropertyDesc(target, prop, desc) { | ||
|  | 			// Note: target@this(unsafe) prop@prim desc@this(safe) throws@this(unsafe)
 | ||
|  | 			return desc; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		defineProperty(target, prop, desc) { | ||
|  | 			// Note: target@this(unsafe) prop@prim desc@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			if (!thisReflectSetPrototypeOf(desc, null)) throw thisUnexpected(); | ||
|  | 
 | ||
|  | 			desc = this.definePropertyDesc(target, prop, desc); | ||
|  | 
 | ||
|  | 			if (!desc) return false; | ||
|  | 
 | ||
|  | 			let otherDesc = {__proto__: null}; | ||
|  | 			let hasFunc = true; | ||
|  | 			let hasValue = true; | ||
|  | 			let hasBasic = true; | ||
|  | 			hasFunc &= otherFromThisIfAvailable(otherDesc, desc, 'get'); | ||
|  | 			hasFunc &= otherFromThisIfAvailable(otherDesc, desc, 'set'); | ||
|  | 			hasValue &= otherFromThisIfAvailable(otherDesc, desc, 'value'); | ||
|  | 			hasValue &= otherFromThisIfAvailable(otherDesc, desc, 'writable'); | ||
|  | 			hasBasic &= otherFromThisIfAvailable(otherDesc, desc, 'enumerable'); | ||
|  | 			hasBasic &= otherFromThisIfAvailable(otherDesc, desc, 'configurable'); | ||
|  | 
 | ||
|  | 			try { | ||
|  | 				if (!otherReflectDefineProperty(object, prop, otherDesc)) return false; | ||
|  | 				if (otherDesc.configurable !== true && (!hasBasic || !(hasFunc || hasValue))) { | ||
|  | 					otherDesc = otherSafeGetOwnPropertyDescriptor(object, prop); | ||
|  | 				} | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (!otherDesc.configurable) { | ||
|  | 				let thisDesc; | ||
|  | 				if (otherDesc.get || otherDesc.set) { | ||
|  | 					thisDesc = { | ||
|  | 						__proto__: null, | ||
|  | 						get: this.fromOtherWithContext(otherDesc.get), | ||
|  | 						set: this.fromOtherWithContext(otherDesc.set), | ||
|  | 						enumerable: otherDesc.enumerable, | ||
|  | 						configurable: otherDesc.configurable | ||
|  | 					}; | ||
|  | 				} else { | ||
|  | 					thisDesc = { | ||
|  | 						__proto__: null, | ||
|  | 						value: this.fromOtherWithContext(otherDesc.value), | ||
|  | 						writable: otherDesc.writable, | ||
|  | 						enumerable: otherDesc.enumerable, | ||
|  | 						configurable: otherDesc.configurable | ||
|  | 					}; | ||
|  | 				} | ||
|  | 				if (!thisReflectDefineProperty(target, prop, thisDesc)) throw thisUnexpected(); | ||
|  | 			} | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		deleteProperty(target, prop) { | ||
|  | 			// Note: target@this(unsafe) prop@prim throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				return otherReflectDeleteProperty(object, prop) === true; | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		has(target, key) { | ||
|  | 			// Note: target@this(unsafe) key@prim throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				return otherReflectHas(object, key) === true; | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		isExtensible(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				if (otherReflectIsExtensible(object)) return true; | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			if (thisReflectIsExtensible(target)) { | ||
|  | 				this.doPreventExtensions(target, object, this); | ||
|  | 			} | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		ownKeys(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			let res; // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				res = otherReflectOwnKeys(object); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			return thisFromOther(res); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		preventExtensions(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				if (!otherReflectPreventExtensions(object)) return false; | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			if (thisReflectIsExtensible(target)) { | ||
|  | 				this.doPreventExtensions(target, object, this); | ||
|  | 			} | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		enumerate(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			let res; // @other(unsafe)
 | ||
|  | 			try { | ||
|  | 				res = otherReflectEnumerate(object); | ||
|  | 			} catch (e) { // @other(unsafe)
 | ||
|  | 				throw thisFromOtherForThrow(e); | ||
|  | 			} | ||
|  | 			return this.fromOtherWithContext(res); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	BaseHandler.prototype[thisSymbolNodeJSUtilInspectCustom] = undefined; | ||
|  | 	BaseHandler.prototype[thisSymbolToStringTag] = 'VM2 Wrapper'; | ||
|  | 	BaseHandler.prototype[thisSymbolIterator] = undefined; | ||
|  | 
 | ||
|  | 	function defaultFactory(object) { | ||
|  | 		// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
 | ||
|  | 		return new BaseHandler(object); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	class ProtectedHandler extends BaseHandler { | ||
|  | 
 | ||
|  | 		getFactory() { | ||
|  | 			return protectedFactory; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		set(target, key, value, receiver) { | ||
|  | 			// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
 | ||
|  | 			if (typeof value === 'function') { | ||
|  | 				return thisReflectDefineProperty(receiver, key, { | ||
|  | 					__proto__: null, | ||
|  | 					value: value, | ||
|  | 					writable: true, | ||
|  | 					enumerable: true, | ||
|  | 					configurable: true | ||
|  | 				}) === true; | ||
|  | 			} | ||
|  | 			return super.set(target, key, value, receiver); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		definePropertyDesc(target, prop, desc) { | ||
|  | 			// Note: target@this(unsafe) prop@prim desc@this(safe) throws@this(unsafe)
 | ||
|  | 			if (desc && (desc.set || desc.get || typeof desc.value === 'function')) return undefined; | ||
|  | 			return desc; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function protectedFactory(object) { | ||
|  | 		// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
 | ||
|  | 		return new ProtectedHandler(object); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	class ReadOnlyHandler extends BaseHandler { | ||
|  | 
 | ||
|  | 		getFactory() { | ||
|  | 			return readonlyFactory; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		set(target, key, value, receiver) { | ||
|  | 			// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
 | ||
|  | 			return thisReflectDefineProperty(receiver, key, { | ||
|  | 				__proto__: null, | ||
|  | 				value: value, | ||
|  | 				writable: true, | ||
|  | 				enumerable: true, | ||
|  | 				configurable: true | ||
|  | 			}); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		setPrototypeOf(target, value) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		defineProperty(target, prop, desc) { | ||
|  | 			// Note: target@this(unsafe) prop@prim desc@this(unsafe) throws@this(unsafe)
 | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		deleteProperty(target, prop) { | ||
|  | 			// Note: target@this(unsafe) prop@prim throws@this(unsafe)
 | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		isExtensible(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		preventExtensions(target) { | ||
|  | 			// Note: target@this(unsafe) throws@this(unsafe)
 | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function readonlyFactory(object) { | ||
|  | 		// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
 | ||
|  | 		return new ReadOnlyHandler(object); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	class ReadOnlyMockHandler extends ReadOnlyHandler { | ||
|  | 
 | ||
|  | 		constructor(object, mock) { | ||
|  | 			// Note: object@other(unsafe) mock:this(unsafe) throws@this(unsafe)
 | ||
|  | 			super(object); | ||
|  | 			this.mock = mock; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		get(target, key, receiver) { | ||
|  | 			// Note: target@this(unsafe) key@prim receiver@this(unsafe) throws@this(unsafe)
 | ||
|  | 			const object = this.getObject(); // @other(unsafe)
 | ||
|  | 			const mock = this.mock; | ||
|  | 			if (thisReflectApply(thisObjectHasOwnProperty, mock, key) && !thisOtherHasOwnProperty(object, key)) { | ||
|  | 				return mock[key]; | ||
|  | 			} | ||
|  | 			return super.get(target, key, receiver); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisFromOther(other) { | ||
|  | 		// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
 | ||
|  | 		return thisFromOtherWithFactory(defaultFactory, other); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisProxyOther(factory, other, proto) { | ||
|  | 		const target = thisCreateTargetObject(other, proto); | ||
|  | 		const handler = factory(other); | ||
|  | 		const proxy = new ThisProxy(target, handler); | ||
|  | 		try { | ||
|  | 			otherReflectApply(otherWeakMapSet, mappingThisToOther, [proxy, other]); | ||
|  | 			registerProxy(proxy, handler); | ||
|  | 		} catch (e) { | ||
|  | 			throw new VMError('Unexpected error'); | ||
|  | 		} | ||
|  | 		if (!isHost) { | ||
|  | 			thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, proxy]); | ||
|  | 			return proxy; | ||
|  | 		} | ||
|  | 		const proxy2 = new ThisProxy(proxy, emptyForzenObject); | ||
|  | 		try { | ||
|  | 			otherReflectApply(otherWeakMapSet, mappingThisToOther, [proxy2, other]); | ||
|  | 			registerProxy(proxy2, handler); | ||
|  | 		} catch (e) { | ||
|  | 			throw new VMError('Unexpected error'); | ||
|  | 		} | ||
|  | 		thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, proxy2]); | ||
|  | 		return proxy2; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisEnsureThis(other) { | ||
|  | 		const type = typeof other; | ||
|  | 		switch (type) { | ||
|  | 			case 'object': | ||
|  | 				if (other === null) { | ||
|  | 					return null; | ||
|  | 				} | ||
|  | 				// fallthrough
 | ||
|  | 			case 'function': | ||
|  | 				let proto = thisReflectGetPrototypeOf(other); | ||
|  | 				if (!proto) { | ||
|  | 					return other; | ||
|  | 				} | ||
|  | 				while (proto) { | ||
|  | 					const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]); | ||
|  | 					if (mapping) { | ||
|  | 						const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]); | ||
|  | 						if (mapped) return mapped; | ||
|  | 						return mapping(defaultFactory, other); | ||
|  | 					} | ||
|  | 					proto = thisReflectGetPrototypeOf(proto); | ||
|  | 				} | ||
|  | 				return other; | ||
|  | 			case 'undefined': | ||
|  | 			case 'string': | ||
|  | 			case 'number': | ||
|  | 			case 'boolean': | ||
|  | 			case 'symbol': | ||
|  | 			case 'bigint': | ||
|  | 				return other; | ||
|  | 
 | ||
|  | 			default: // new, unknown types can be dangerous
 | ||
|  | 				throw new VMError(`Unknown type '${type}'`); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisFromOtherForThrow(other) { | ||
|  | 		for (let loop = 0; loop < 10; loop++) { | ||
|  | 			const type = typeof other; | ||
|  | 			switch (type) { | ||
|  | 				case 'object': | ||
|  | 					if (other === null) { | ||
|  | 						return null; | ||
|  | 					} | ||
|  | 					// fallthrough
 | ||
|  | 				case 'function': | ||
|  | 					const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]); | ||
|  | 					if (mapped) return mapped; | ||
|  | 					let proto; | ||
|  | 					try { | ||
|  | 						proto = otherReflectGetPrototypeOf(other); | ||
|  | 					} catch (e) { // @other(unsafe)
 | ||
|  | 						other = e; | ||
|  | 						break; | ||
|  | 					} | ||
|  | 					if (!proto) { | ||
|  | 						return thisProxyOther(defaultFactory, other, null); | ||
|  | 					} | ||
|  | 					for (;;) { | ||
|  | 						const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]); | ||
|  | 						if (mapping) return mapping(defaultFactory, other); | ||
|  | 						try { | ||
|  | 							proto = otherReflectGetPrototypeOf(proto); | ||
|  | 						} catch (e) { // @other(unsafe)
 | ||
|  | 							other = e; | ||
|  | 							break; | ||
|  | 						} | ||
|  | 						if (!proto) return thisProxyOther(defaultFactory, other, thisObjectPrototype); | ||
|  | 					} | ||
|  | 					break; | ||
|  | 				case 'undefined': | ||
|  | 				case 'string': | ||
|  | 				case 'number': | ||
|  | 				case 'boolean': | ||
|  | 				case 'symbol': | ||
|  | 				case 'bigint': | ||
|  | 					return other; | ||
|  | 
 | ||
|  | 				default: // new, unknown types can be dangerous
 | ||
|  | 					throw new VMError(`Unknown type '${type}'`); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		throw new VMError('Exception recursion depth'); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisFromOtherWithFactory(factory, other, proto) { | ||
|  | 		const type = typeof other; | ||
|  | 		switch (type) { | ||
|  | 			case 'object': | ||
|  | 				if (other === null) { | ||
|  | 					return null; | ||
|  | 				} | ||
|  | 				// fallthrough
 | ||
|  | 			case 'function': | ||
|  | 				const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]); | ||
|  | 				if (mapped) return mapped; | ||
|  | 				if (proto) { | ||
|  | 					return thisProxyOther(factory, other, proto); | ||
|  | 				} | ||
|  | 				try { | ||
|  | 					proto = otherReflectGetPrototypeOf(other); | ||
|  | 				} catch (e) { // @other(unsafe)
 | ||
|  | 					throw thisFromOtherForThrow(e); | ||
|  | 				} | ||
|  | 				if (!proto) { | ||
|  | 					return thisProxyOther(factory, other, null); | ||
|  | 				} | ||
|  | 				do { | ||
|  | 					const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]); | ||
|  | 					if (mapping) return mapping(factory, other); | ||
|  | 					try { | ||
|  | 						proto = otherReflectGetPrototypeOf(proto); | ||
|  | 					} catch (e) { // @other(unsafe)
 | ||
|  | 						throw thisFromOtherForThrow(e); | ||
|  | 					} | ||
|  | 				} while (proto); | ||
|  | 				return thisProxyOther(factory, other, thisObjectPrototype); | ||
|  | 			case 'undefined': | ||
|  | 			case 'string': | ||
|  | 			case 'number': | ||
|  | 			case 'boolean': | ||
|  | 			case 'symbol': | ||
|  | 			case 'bigint': | ||
|  | 				return other; | ||
|  | 
 | ||
|  | 			default: // new, unknown types can be dangerous
 | ||
|  | 				throw new VMError(`Unknown type '${type}'`); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisFromOtherArguments(args) { | ||
|  | 		// Note: args@other(safe-array) returns@this(safe-array) throws@this(unsafe)
 | ||
|  | 		const arr = []; | ||
|  | 		for (let i = 0; i < args.length; i++) { | ||
|  | 			const value = thisFromOther(args[i]); | ||
|  | 			thisReflectDefineProperty(arr, i, { | ||
|  | 				__proto__: null, | ||
|  | 				value: value, | ||
|  | 				writable: true, | ||
|  | 				enumerable: true, | ||
|  | 				configurable: true | ||
|  | 			}); | ||
|  | 		} | ||
|  | 		return arr; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function thisConnect(obj, other) { | ||
|  | 		// Note: obj@this(unsafe) other@other(unsafe) throws@this(unsafe)
 | ||
|  | 		try { | ||
|  | 			otherReflectApply(otherWeakMapSet, mappingThisToOther, [obj, other]); | ||
|  | 		} catch (e) { | ||
|  | 			throw new VMError('Unexpected error'); | ||
|  | 		} | ||
|  | 		thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, obj]); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	thisAddProtoMapping(thisGlobalPrototypes.Object, otherGlobalPrototypes.Object); | ||
|  | 	thisAddProtoMapping(thisGlobalPrototypes.Array, otherGlobalPrototypes.Array); | ||
|  | 
 | ||
|  | 	for (let i = 0; i < globalsList.length; i++) { | ||
|  | 		const key = globalsList[i]; | ||
|  | 		const tp = thisGlobalPrototypes[key]; | ||
|  | 		const op = otherGlobalPrototypes[key]; | ||
|  | 		if (tp && op) thisAddProtoMapping(tp, op, key); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for (let i = 0; i < errorsList.length; i++) { | ||
|  | 		const key = errorsList[i]; | ||
|  | 		const tp = thisGlobalPrototypes[key]; | ||
|  | 		const op = otherGlobalPrototypes[key]; | ||
|  | 		if (tp && op) thisAddProtoMapping(tp, op, 'Error'); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	thisAddProtoMapping(thisGlobalPrototypes.VMError, otherGlobalPrototypes.VMError, 'Error'); | ||
|  | 
 | ||
|  | 	result.BaseHandler = BaseHandler; | ||
|  | 	result.ProtectedHandler = ProtectedHandler; | ||
|  | 	result.ReadOnlyHandler = ReadOnlyHandler; | ||
|  | 	result.ReadOnlyMockHandler = ReadOnlyMockHandler; | ||
|  | 
 | ||
|  | 	return result; | ||
|  | } | ||
|  | 
 | ||
|  | exports.createBridge = createBridge; | ||
|  | exports.VMError = VMError; |