Skip to content

Commit

Permalink
chore: update proxyMaskFunction
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalton committed Feb 16, 2023
1 parent 83131b2 commit 9235da9
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 9 deletions.
21 changes: 14 additions & 7 deletions packages/near-membrane-shared/src/Function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ import type { ProxyTarget, ProxyTrapInvokers } from './types';
// references to the enum from the generated JavaScript.
import { ProxyHandlerTraps } from './types';

export function noop() {
// No operation performed.
}

export function isProxyMaskedFunction(value: any): boolean {
// To extract the flag value of a blue near-membrane proxy we must perform
// a two step handshake. First, we trigger the "has" trap for the
Expand All @@ -38,21 +34,32 @@ export function isProxyMaskedFunction(value: any): boolean {
);
}

export function noop() {
// No operation performed.
}

export function proxyMaskFunction<T extends Function>(
func: Function,
maskFunc: T,
trapInvokers?: ProxyTrapInvokers
): T {
let applyTrapInvoker = ReflectApply;
let constructTrapInvoker = ReflectConstruct;
let definePropertyTrapInvoker = ReflectDefineProperty;
let getTrapInvoker = (ReflectGet as ProxyTrapInvokers['get'])!;
let getOwnPropertyDescriptorTrapInvoker = ReflectGetOwnPropertyDescriptor;
let hasTrapInvoker = ReflectHas;
let setTrapInvoker = ReflectSet;
if (trapInvokers) {
({
apply: applyTrapInvoker = ReflectApply,
construct: constructTrapInvoker = ReflectConstruct,
defineProperty: definePropertyTrapInvoker = ReflectDefineProperty,
get: getTrapInvoker = (ReflectGet as ProxyTrapInvokers['get'])!,
getOwnPropertyDescriptor:
getOwnPropertyDescriptorTrapInvoker = ReflectGetOwnPropertyDescriptor,
has: hasTrapInvoker = ReflectHas,
set: setTrapInvoker = ReflectSet,
} = trapInvokers);
}
let handshakeFlag = false;
Expand All @@ -79,7 +86,7 @@ export function proxyMaskFunction<T extends Function>(
if (key === LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL) {
throw new TypeErrorCtor(ERR_ILLEGAL_PROPERTY_ACCESS);
}
return ReflectDefineProperty(target, key, desc);
return definePropertyTrapInvoker(target, key, desc);
},
deleteProperty(target: ProxyTarget, key: PropertyKey) {
lastProxyTrapCalled = ProxyHandlerTraps.GetOwnPropertyDescriptor;
Expand Down Expand Up @@ -111,7 +118,7 @@ export function proxyMaskFunction<T extends Function>(
},
getOwnPropertyDescriptor(target: ProxyTarget, key: PropertyKey) {
lastProxyTrapCalled = ProxyHandlerTraps.GetOwnPropertyDescriptor;
const result = ReflectGetOwnPropertyDescriptor(target, key);
const result = getOwnPropertyDescriptorTrapInvoker(target, key);
// Getting forged descriptors of handshake properties is not allowed.
if (result && key === LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL) {
throw new TypeErrorCtor(ERR_ILLEGAL_PROPERTY_ACCESS);
Expand Down Expand Up @@ -158,7 +165,7 @@ export function proxyMaskFunction<T extends Function>(
if (key === LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL) {
throw new TypeErrorCtor(ERR_ILLEGAL_PROPERTY_ACCESS);
}
return ReflectSet(target, key, value, receiver);
return setTrapInvoker(target, key, value, receiver);
},
setPrototypeOf(target: ProxyTarget, proto: object | null) {
lastProxyTrapCalled = ProxyHandlerTraps.SetPrototypeOf;
Expand Down
66 changes: 64 additions & 2 deletions packages/near-membrane-shared/src/__tests__/Function.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,63 @@ describe('Function', () => {
function mask() {
return 'A';
}
mask.x = 1;
mask[Symbol('y')] = 2;
Reflect.defineProperty(mask, 'z', {
configurable: true,
enumerable: false,
value: 3,
writable: true,
});
const proxyMasked = proxyMaskFunction(() => 'a', mask);
expect(proxyMasked.name).toBe(mask.name);
expect(proxyMasked()).toBe('a');
expect(Reflect.ownKeys(proxyMasked)).toEqual(Reflect.ownKeys(mask));
expect(Reflect.getOwnPropertyDescriptor(proxyMasked, 'z')).toEqual(
Reflect.getOwnPropertyDescriptor(mask, 'z')
);
});

it('should mask extensibility', () => {
function mask() {
return 'A';
}
function frozenMask() {
return 'frozen';
}
Object.freeze(frozenMask);
function sealedMask() {
return 'sealed';
}
Object.seal(sealedMask);

const maskProto = Reflect.getPrototypeOf(mask);
const proxyMasked = proxyMaskFunction(() => 'a', mask);
const proxyMaskedFrozen = proxyMaskFunction(() => 'a', frozenMask);
const proxyMaskedSealed = proxyMaskFunction(() => 'a', sealedMask);

expect(Object.isFrozen(proxyMaskedFrozen)).toBe(true);
expect(Object.isExtensible(proxyMaskedFrozen)).toBe(false);
expect(Object.isFrozen(proxyMaskedSealed)).toBe(false);
expect(Object.isExtensible(proxyMaskedSealed)).toBe(false);
expect(Object.isSealed(proxyMaskedSealed)).toBe(true);

proxyMasked.x = 1;
expect(proxyMasked.x).toBe(1);
expect(mask.x).toBe(1);
expect(Reflect.deleteProperty(proxyMasked, 'x')).toBe(true);
expect('x' in proxyMasked).toBe(false);
expect('x' in mask).toBe(false);

expect(Reflect.getPrototypeOf(proxyMasked)).toBe(maskProto);
expect(Reflect.setPrototypeOf(proxyMasked, null)).toBe(true);
expect(Reflect.getPrototypeOf(proxyMasked)).toBe(null);
expect(Reflect.getPrototypeOf(mask)).toBe(null);
Reflect.setPrototypeOf(mask, maskProto);

expect(Reflect.preventExtensions(proxyMasked)).toBe(true);
expect(Object.isExtensible(proxyMasked)).toBe(false);
expect(Object.isExtensible(mask)).toBe(false);
});

it('should convert `this` of `mask` to `func`', () => {
Expand Down Expand Up @@ -135,7 +189,6 @@ describe('Function', () => {
return Reflect.get(target, key, receiver);
},
});

expect(proxyMasked.test).toBe('testResult');
});

Expand All @@ -157,7 +210,6 @@ describe('Function', () => {
return Reflect.has(target, key);
},
});

expect('test' in proxyMasked).toBe(true);
});

Expand All @@ -167,6 +219,15 @@ describe('Function', () => {
expect(() => {
proxyMasked[LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL] = true;
}).toThrowError(ERR_ILLEGAL_PROPERTY_ACCESS);
expect(() => {
Reflect.defineProperty(proxyMasked, LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL, {
configurable: true,
enumerable: true,
value: true,
writable: true,
});
}).toThrowError(ERR_ILLEGAL_PROPERTY_ACCESS);
delete proxyMasked[LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL];
bogusMask[LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL] = true;
expect(() => LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL in proxyMasked).toThrowError(
ERR_ILLEGAL_PROPERTY_ACCESS
Expand All @@ -180,6 +241,7 @@ describe('Function', () => {
LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL
)
).toThrowError(ERR_ILLEGAL_PROPERTY_ACCESS);
delete bogusMask[LOCKER_NEAR_MEMBRANE_PROXY_MASKED_SYMBOL];
});
});
});
3 changes: 3 additions & 0 deletions packages/near-membrane-shared/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ export interface ProxyTrapInvokers {
// We can add more trap invokers as needed.
apply?: typeof Reflect.apply;
construct?: typeof Reflect.construct;
defineProperty?: typeof Reflect.defineProperty;
get?: <T extends object, P extends PropertyKey>(
target: T,
propertyKey: P,
receiver?: unknown,
handshake?: boolean
) => P extends keyof T ? T[P] : any;
getOwnPropertyDescriptor?: typeof Reflect.getOwnPropertyDescriptor;
has?: typeof Reflect.has;
set?: typeof Reflect.set;
}
export type Setter = (value: any) => void;
// eslint-disable-next-line no-shadow
Expand Down

0 comments on commit 9235da9

Please sign in to comment.