resolve awful meta-class hacking (with thanks to jsbueno on stackexchange)
[ieee754fpu.git] / src / ieee754 / pipeline.py
index b1e3b0ba111b08eca8e0d5b17a172b0a8350041c..2e57245920126fdd75c97b2c6fe4792f25e8ca43 100644 (file)
@@ -1,7 +1,11 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 # See Notices.txt for copyright information
 
+from abc import ABCMeta
+from nmigen import Elaboratable
+
 from nmutil.singlepipe import SimpleHandshake
+import threading
 
 
 class PipelineSpec:
@@ -27,18 +31,55 @@ class PipelineSpec:
         self.id_wid = id_width
         self.op_wid = op_wid
         self.opkls = opkls
-        self.pipekls = pipekls or SimpleHandshake
+        self.pipekls = pipekls or SimpleHandshakeRedir
         self.core_config = None
         self.fpformat = None
         self.n_comb_stages = None
 
+# with many thanks to jsbueno on stackexchange for this one
+# https://stackoverflow.com/questions/57273070/
+
+class Meta(ABCMeta):
+    registry = {}
+    recursing = threading.local()
+    recursing.check = False
+    mlock = threading.Lock()
+
+    def __call__(cls, *args, **kw):
+        mcls = cls.__class__
+        if mcls.recursing.check:
+            return super().__call__(*args, **kw)
+        spec = args[0]
+        base = spec.pipekls
+
+        if (cls, base) not in mcls.registry:
+            print ("__call__", args, kw, cls, base, base.__bases__, cls.__bases__)
+            mcls.registry[cls, base] = type(
+                cls.__name__,
+                (cls, base) + cls.__bases__[1:],
+                {}
+            )
+        real_cls = mcls.registry[cls, base]
+
+        with mcls.mlock:
+            mcls.recursing.check = True
+            instance = real_cls.__class__.__call__(real_cls, *args, **kw)
+            mcls.recursing.check = False
+        return instance
+
+
+class DynamicPipe(metaclass=Meta):
+    def __init__(self, *args):
+        print ("DynamicPipe init", super(), args)
+        super().__init__(self, *args)
+
 
-def DynamicPipeCreate(pspec, *args, **kwargs):
-    superclass = pspec.pipekls
-    class DynamicPipe(superclass):
-        def __init__(self, *args, **kwargs):
-            print(superclass)
-            superclass.__init__(self, *args, **kwargs)
-        pass
-    return DynamicPipe(*args, **kwargs)
+# bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
+# for the dynamically-derived class.  luckily, SimpleHandshake only needs
+# "self" as the 1st argument (it is its own "Stage").  anything else
+# could hypothetically be passed through the pspec.
+class SimpleHandshakeRedir(SimpleHandshake):
+    def __init__(self, pspec, *args):
+        print ("redir", pspec, args)
+        SimpleHandshake.__init__(self, self)