]> Lady’s Gitweb - Pisces/commitdiff
Add is⸺ method to generated proxy constructors
authorLady <redacted>
Sun, 3 Dec 2023 18:24:08 +0000 (13:24 -0500)
committerLady <redacted>
Sun, 3 Dec 2023 18:24:08 +0000 (13:24 -0500)
function.js
function.test.js

index 5d59af1a6c56f99beb4a1fad81db62ada3b7394c..2f242fe206eb466deb20a768f0098c00df8f3024 100644 (file)
@@ -128,6 +128,29 @@ export const {
       };
     },
   };
+  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.
@@ -251,7 +274,11 @@ export const {
       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")) {
@@ -273,13 +300,13 @@ export const {
         );
       } 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"
@@ -293,7 +320,8 @@ export const {
                     $s,
                     target,
                   );
-                  return new proxyConstructor(O, handler);
+                  const proxy = new proxyConstructor(O, handler);
+                  return registerConstructedProxy(C, proxy);
                 }
               },
               proxyConstructor,
@@ -315,37 +343,71 @@ export const {
                 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,
+          }),
+        });
       }
     },
   };
index 09b9f7e00160eb998a82f6714dbaece118aec180..bf0e907a2df13688932447fbb084bc83940fdf82 100644 (file)
@@ -10,6 +10,7 @@
 import {
   assert,
   assertEquals,
+  assertNotStrictEquals,
   assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
@@ -469,9 +470,6 @@ describe("createIllegalConstructor", () => {
       Object.getPrototypeOf(constructor.prototype),
       Object.prototype,
     );
-    console.dir(
-      Object.getOwnPropertyDescriptors(constructor.prototype),
-    );
     assertEquals(
       constructor.prototype,
       Object.create(Object.prototype, {
@@ -712,6 +710,95 @@ describe("createProxyConstructor", () => {
     });
   });
 
+  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(
@@ -739,7 +826,7 @@ describe("createProxyConstructor", () => {
         {
           configurable: true,
           enumerable: false,
-          value: "ObjectProxy",
+          value: "Proxy",
           writable: false,
         },
       );
This page took 0.062773 seconds and 4 git commands to generate.