'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; /** * 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.object = object; } 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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @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.object; // @other(unsafe) let res; // @other(unsafe) try { res = otherReflectEnumerate(object); } catch (e) { // @other(unsafe) throw thisFromOtherForThrow(e); } return this.fromOtherWithContext(res); } } 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.object; // @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;