};
},
};
+ const { get: wmGet, set: wmSet } = WeakMap.prototype;
+ const wsConstructor = WeakSet;
+ const { add: wsAdd, has: wsHas } = WeakSet.prototype;
+ const proxyConstructorValuesMap = new WeakMap();
+ const registerConstructedProxy = (constructor, proxy) => {
+ const values = (() => {
+ const existing = reflectApply(wmGet, proxyConstructorValuesMap, [
+ constructor,
+ ]);
+ if (existing) {
+ return existing;
+ } else {
+ const result = new wsConstructor();
+ reflectApply(wmSet, proxyConstructorValuesMap, [
+ constructor,
+ result,
+ ]);
+ return result;
+ }
+ })();
+ reflectApply(wsAdd, values, [proxy]);
+ return proxy;
+ };
const applyBaseFunction = ($, base, lengthDelta = 0) => {
if (base === UNDEFINED) {
// No base function was provided to apply.
newTarget = UNDEFINED,
propertyOverride = UNDEFINED,
) => {
- const constructor = $ === UNDEFINED ? objectConstructor : $;
+ const constructor = $ === UNDEFINED
+ ? function ($) {
+ return new objectConstructor($);
+ }
+ : $;
const target = newTarget === UNDEFINED ? constructor : newTarget;
const len = toLength(constructor.length);
if (!(type(handler) === "object")) {
);
} else {
// The arguments are acceptable.
- return applyProperties(
+ const C = applyProperties(
defineOwnProperties(
setPrototype(
- function C(...$s) {
+ function (...$s) {
if (new.target === UNDEFINED) {
- // The constructor was not called with new; this is
- // an error.
+ // The constructor was not called with new; this is an
+ // error.
throw new TypeError(
`Piscēs: ${
C.name ?? "Proxy"
$s,
target,
);
- return new proxyConstructor(O, handler);
+ const proxy = new proxyConstructor(O, handler);
+ return registerConstructedProxy(C, proxy);
}
},
proxyConstructor,
value: UNDEFINED,
writable: false,
}),
- revocable: setPropertyValues(objectCreate(null), {
- configurable: true,
- enumerable: false,
- value: defineOwnProperties(
- (...$s) => {
- const O = reflectConstruct(
- constructor,
- $s,
- target,
- );
- return revocable(O, handler);
- },
- {
- length: defineOwnDataProperty(
- objectCreate(null),
- "value",
- len,
- ),
- name: defineOwnDataProperty(
- objectCreate(null),
- "value",
- "revocable",
- ),
- },
- ),
- writable: true,
- }),
},
),
propertyOverride,
);
+ const { name } = C;
+ return defineOwnProperties(C, {
+ revocable: setPropertyValues(objectCreate(null), {
+ configurable: true,
+ enumerable: false,
+ value: defineOwnProperties(
+ (...$s) => {
+ const O = reflectConstruct(
+ constructor,
+ $s,
+ target,
+ );
+ const proxy = revocable(O, handler);
+ return registerConstructedProxy(C, proxy);
+ },
+ {
+ length: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ len,
+ ),
+ name: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ "revocable",
+ ),
+ },
+ ),
+ writable: true,
+ }),
+ [`is${name}`]: setPropertyValues(objectCreate(null), {
+ configurable: true,
+ enumerable: false,
+ value: defineOwnProperty(
+ ($) => {
+ const values = reflectApply(
+ wmGet,
+ proxyConstructorValuesMap,
+ [C],
+ );
+ if (values === UNDEFINED) {
+ // No values have been registered for the current
+ // constructor.
+ return false;
+ } else {
+ // One or more values has been registered for the
+ // current constructor; return whether the provided
+ // argument is one.
+ return reflectApply(wsHas, values, [$]);
+ }
+ },
+ "name",
+ defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ `is${name}`,
+ ),
+ ),
+ writable: true,
+ }),
+ });
}
},
};
import {
assert,
assertEquals,
+ assertNotStrictEquals,
assertSpyCall,
assertSpyCalls,
assertStrictEquals,
Object.getPrototypeOf(constructor.prototype),
Object.prototype,
);
- console.dir(
- Object.getOwnPropertyDescriptors(constructor.prototype),
- );
assertEquals(
constructor.prototype,
Object.create(Object.prototype, {
});
});
+ describe("~is[[.name]]", () => {
+ it("[[GetOwnProperty]] defines the appropriate method", () => {
+ assertNotStrictEquals(
+ Object.getOwnPropertyDescriptor(
+ createProxyConstructor({}),
+ "isProxy",
+ ),
+ undefined,
+ );
+ assertNotStrictEquals(
+ Object.getOwnPropertyDescriptor(
+ createProxyConstructor({}, function Base() {}),
+ "isBaseProxy",
+ ),
+ undefined,
+ );
+ assertNotStrictEquals(
+ Object.getOwnPropertyDescriptor(
+ createProxyConstructor({}, function Bad() {}, undefined, {
+ name: "⸺",
+ }),
+ "is⸺",
+ ),
+ undefined,
+ );
+ });
+
+ it("[[GetOwnProperty]] has the correct descriptor", () => {
+ const proxyConstructor = createProxyConstructor({});
+ assertEquals(
+ Object.getOwnPropertyDescriptor(
+ proxyConstructor,
+ "isProxy",
+ ),
+ {
+ configurable: true,
+ enumerable: false,
+ value: proxyConstructor.isProxy,
+ writable: true,
+ },
+ );
+ });
+
+ it("[[Call]] returns true for created proxies", () => {
+ const proxyConstructor = createProxyConstructor({});
+ const proxy = new proxyConstructor();
+ assertStrictEquals(
+ proxyConstructor.isProxy(proxy),
+ true,
+ );
+ });
+
+ it("[[Call]] returns false for nonproxies", () => {
+ const constructor = function Base() {};
+ const proxyConstructor = createProxyConstructor({}, constructor);
+ assertStrictEquals(
+ proxyConstructor.isBaseProxy(new constructor()),
+ false,
+ );
+ });
+
+ it("[[Construct]] throws an error", () => {
+ const proxyConstructor = createProxyConstructor({});
+ assertThrows(() => new proxyConstructor.isProxy({}));
+ });
+
+ describe(".length", () => {
+ it("[[Get]] returns the correct length", () => {
+ const proxyConstructor = createProxyConstructor({});
+ assertStrictEquals(proxyConstructor.isProxy.length, 1);
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ const proxyConstructor = createProxyConstructor({});
+ assertStrictEquals(proxyConstructor.isProxy.name, "isProxy");
+ const otherProxyConstructor = createProxyConstructor(
+ {},
+ function Base() {},
+ );
+ assertStrictEquals(
+ otherProxyConstructor.isBaseProxy.name,
+ "isBaseProxy",
+ );
+ });
+ });
+ });
+
describe("~length", () => {
it("[[GetOwnProperty]] has the correct descriptor", () => {
assertEquals(
{
configurable: true,
enumerable: false,
- value: "ObjectProxy",
+ value: "Proxy",
writable: false,
},
);