[project] Generate syscalls from new interface DSL

This change adds a new interface DSL for specifying objects (with
methods) and interfaces (that expose objects, and optionally have their
own methods).

Significant changes:

- Add the new scripts/definitions Python module to parse the DSL
- Add the new definitions directory containing DSL definition files
- Use cog to generate syscall-related code in kernel and libj6
- Unify ordering of pointer + length pairs in interfaces
This commit is contained in:
Justin C. Miller
2021-08-30 01:05:32 -07:00
parent 80f815c020
commit 186724e751
52 changed files with 4025 additions and 206 deletions

View File

@@ -0,0 +1,23 @@
def _indent(x):
from textwrap import indent
return indent(str(x), ' ')
class Description(str): pass
class Import(str): pass
class Options(set):
def __str__(self):
if not self: return ""
return "[{}]".format(" ".join(self))
class UID(int):
def __str__(self):
return f"{self:016x}"
from .object import Object
from .interface import Interface, Expose
from .function import Function, Method, Param
from .type import Type
from .primitive import get_primitive
from .objref import ObjectRef

View File

@@ -0,0 +1,49 @@
from . import _indent
from . import Options
def _hasopt(opt):
def test(self):
return opt in self.options
return test
class Function:
typename = "function"
def __init__(self, name, opts=Options(), desc="", children=tuple()):
self.name = name
self.options = opts
self.desc = desc
self.params = [c for c in children if isinstance(c, Param)]
self.id = -1
def __str__(self):
parts = ["{} {}".format(self.typename, self.name)]
if self.desc:
parts.append(_indent(self.desc))
if self.options:
parts.append(f" Options: {self.options}")
parts.extend(map(_indent, self.params))
return "\n".join(parts)
static = property(lambda x: True)
constructor = property(lambda x: False)
class Method(Function):
typename = "method"
static = property(_hasopt("static"))
constructor = property(_hasopt("constructor"))
class Param:
def __init__(self, name, typename, opts=Options(), desc=""):
self.name = name
self.type = typename
self.options = opts
self.desc = desc
def __str__(self):
return "param {} {} {} {}".format(
self.name, repr(self.type), self.options, self.desc or "")

View File

@@ -0,0 +1,43 @@
from . import _indent
from . import Options
class Expose(object):
def __init__(self, type):
self.type = type
def __repr__(self):
return f'expose {repr(self.type)}'
class Interface:
def __init__(self, name, uid, opts=Options(), desc="", children=tuple()):
from .function import Function
self.name = name
self.uid = uid
self.options = opts
self.desc = desc
self.functions = [c for c in children if isinstance(c, Function)]
self.exposes = [e.type for e in children if isinstance(e, Expose)]
def __str__(self):
parts = [f"interface {self.name}: {self.uid}"]
if self.desc:
parts.append(_indent(self.desc))
if self.options:
parts.append(f" Options: {self.options}")
parts.extend(map(_indent, self.exposes))
parts.extend(map(_indent, self.functions))
return "\n".join(parts)
def __methods(self):
mm = [(i, None, self.functions[i]) for i in range(len(self.functions))]
base = len(mm)
for o in [e.object for e in self.exposes]:
mm.extend([(base + i, o, o.methods[i]) for i in range(len(o.methods))])
base += len(o.methods)
return mm
methods = property(__methods)

View File

@@ -0,0 +1,25 @@
from . import _indent
from . import Options
class Object:
def __init__(self, name, uid, typename=None, opts=Options(), desc="", children=tuple()):
self.name = name
self.uid = uid
self.options = opts
self.desc = desc
self.super = typename
self.methods = children
from . import ObjectRef
self.__ref = ObjectRef(name)
def __str__(self):
parts = [f"object {self.name}: {self.uid}"]
if self.desc:
parts.append(_indent(self.desc))
if self.options:
parts.append(f" Options: {self.options}")
parts.extend(map(_indent, self.methods))
return "\n".join(parts)
reftype = property(lambda self: self.__ref)

View File

@@ -0,0 +1,44 @@
from .type import Type
class ObjectRef(Type):
all_refs = {}
def __init__(self, name, filename=None):
super().__init__(name)
self.__c_type = "j6_handle_t"
self.__object = None
ObjectRef.all_refs[self] = filename
def __repr__(self):
return f'ObjectRef({self.name})'
object = property(lambda self: self.__object)
def c_names(self, options):
one = self.__c_type
out = bool({"out", "inout"}.intersection(options))
if "list" in options:
two = "size_t"
if out:
one = f"const {one} *"
two += " *"
else:
one = f"{one} *"
return ((one, ""), (two, "_count"))
else:
if out:
one += " *"
return ((one, ""),)
def cxx_names(self, options):
return self.c_names(options)
@classmethod
def connect(cls, objects):
for ref, filename in cls.all_refs.items():
ref.__object = objects.get(ref.name)
if ref.__object is None:
from ..errors import UnknownTypeError
raise UnknownTypeError(ref.name, filename)

View File

@@ -0,0 +1,70 @@
from .type import Type
class Primitive(Type):
def __init__(self, name, c_type):
super().__init__(name)
self.c_type = c_type
def __repr__(self):
return f'Primitive({self.name})'
def c_names(self, options=None):
one = self.c_type
if options and "out" in options or "inout" in options:
one += " *"
return ((one, ""),)
def cxx_names(self, options):
return self.c_names(options)
class PrimitiveRef(Primitive):
def __init__(self, name, c_type, counted=False):
super().__init__(name, c_type)
self.__counted = counted
def c_names(self, options=None):
one = f"{self.c_type} *"
two = "size_t"
if options and "out" in options or "inout" in options:
two += " *"
else:
one = "const " + one
if self.__counted:
return ((one, ""), (two, "_len"))
else:
return ((one, ""),)
def cxx_names(self, options):
return self.c_names(options)
_primitives = {
"string": PrimitiveRef("string", "char"),
"buffer": PrimitiveRef("buffer", "void", counted=True),
"int": Primitive("int", "int"),
"uint": Primitive("uint", "unsigned"),
"size": Primitive("size", "size_t"),
"address": Primitive("address", "uintptr_t"),
"int8": Primitive("int8", "int8_t"),
"uint8": Primitive("uint8", "uint8_t"),
"int16": Primitive("int16", "int16_t"),
"uint16": Primitive("uint16", "uint16_t"),
"int32": Primitive("int32", "int32_t"),
"uint32": Primitive("uint32", "uint32_t"),
"int64": Primitive("int64", "int64_t"),
"uint64": Primitive("uint64", "uint64_t"),
}
def get_primitive(name):
p = _primitives.get(name)
if not p:
from ..errors import InvalidType
raise InvalidType(name)
return p

View File

@@ -0,0 +1,12 @@
class Type:
def __init__(self, name):
self.__name = name
name = property(lambda self: self.__name)
def c_names(self, options):
raise NotImplemented("Call to base Type.c_names")
def cxx_names(self, options):
raise NotImplemented("Call to base Type.c_names")