Files
extism/scripts/header.py
zach dc3d54e260 feat: Add C API for host functions + support for C++, Python, Go, Node, OCaml (#195)
- New types:
  - `ExtismValType` - Enum of WebAssembly types
  - `ExtismValUnion` - A union of the possible WebAssembly types
  - `ExtismVal` - A struct with `ExtismValType` and `ExtismValUnion`
  - `ExtismFunction` - The host function wrapper type
  - `ExtismFunctionType` - The type of the host function callback
- `ExtismCurrentPlugin` - Provides access to the currently running
plugin from inside a host function

- New functions:
  - `extism_function_new` - Create a new `ExtismFunction`
  - `extism_function_free` - Free an `ExtismFunction`
- `extism_current_plugin_memory`, `extism_current_plugin_memory_alloc`,
`extism_current_plugin_memory_free`,
`extism_current_plugin_memory_length` - Manage plugin memory from inside
a host functions

- Updated functions
- `extism_plugin_new` and `extsim_plugin_update` - now accept two extra
parameters for `ExtismFunction*` array and length of that array

## Notes

- Host functions take a user-data argument, which is owned by the
resulting `ExtismFunction` and will be cleaned up when
`extism_function_free` is called (if a cleanup function was passed in
with the user data)
- Host functions in every SDK require working with `ExtismVal` arguments
directly, this is pretty low-level for what is kind of a high-level
feature. We could work on adding some types to the SDKs that make
working with pointers to plugin data more accessible, maybe something
similar to how the Rust PDK handes input/output data.
- In each language the host functions more-or-less share a signature:
`(CurrentPlugin plugin, Val inputs[], Val outputs[], userData)`
- C, C++, OCaml and Go take a single userData argument but Python and
Node take a "rest" argument which allows passing any number of user-data
values
- Go requires the host function to be exported:
f9eb5ed839/go/main.go (L13-L26)
- Zig and Ruby should be relatively simple to add host functions to next
but I haven't really looked into Elixir, .NET or Java yet.
- Also closes #20
2023-01-10 12:04:40 -08:00

99 lines
2.4 KiB
Python

from pycparser import c_ast, parse_file
class Function:
def __init__(self, name, return_type, args):
self.name = name
self.return_type = return_type
self.args = args
typemap = {
"_Bool": "bool",
"ExtismPlugin": "int32_t",
"ExtismSize": "uint64_t",
}
class Type:
def __init__(self, name, const=False, pointer=False):
self.name = typemap.get(name) or name
self.const = const
self.pointer = pointer
class Arg:
def __init__(self, name, type):
self.name = name
self.type = type
class Visitor(c_ast.NodeVisitor):
def __init__(self, header):
self.header = header
def args(self, args):
dest = []
for arg in args:
name = arg.name
if isinstance(arg.type, c_ast.PtrDecl):
t = arg.type.type
is_ptr = True
else:
t = arg.type
is_ptr = False
if hasattr(t.type, 'name'):
type_name = t.type.name
else:
try:
type_name = t.type.names[0]
except:
continue
const = hasattr(t.type, 'quals') and 'const' in t.type.quals
t = Type(type_name, const=const, pointer=is_ptr)
dest.append(Arg(name, t))
return dest
def visit_FuncDecl(self, node: c_ast.FuncDecl):
if isinstance(node.type, c_ast.PtrDecl):
t = node.type.type
is_ptr = True
else:
t = node.type
is_ptr = False
name = t.declname
args = self.args(node.args)
if hasattr(t.type, 'name'):
return_type_name = t.type.name
else:
return_type_name = t.type.names[0]
const = hasattr(t.type, 'quals') and 'const' in t.type.quals
return_type = Type(return_type_name, const=const, pointer=is_ptr)
self.header.functions.append(Function(name, return_type, args))
class Header:
def __init__(self, filename='runtime/extism.h'):
self.functions = []
self.header = parse_file(filename, use_cpp=True, cpp_args='-w')
self.visitor = Visitor(self)
self.visitor.visit(self.header)
def __iter__(self):
return self.functions.__iter__()
def __getitem__(self, func):
for f in self.functions:
if f.name == func:
return f
return None