mirror of
https://github.com/zap-proto/nim.git
synced 2026-10-30 15:04:34 +00:00
Rename the capnp/ module set to zap/ and the capnpc compiler to zapc; rename all .capnp schemas and fixtures to .zap; update every import, include guard (isInZap), generated-code emission, exception type (ZapFormatError), and the macro-generated zapUnpackStructImpl/ zapPackStructImpl identifiers to the ZAP brand. Zero capnp/Cap'n Proto references remain in source or build files; upstream MIT attribution (Sandstorm, Michał Zieliński) is preserved. Raise the nimble nim floor from the fictional 0.12.0 to the locked, verified 0.15.2 (matches nimenv.cfg/build.sh; code carries fixes through Nim 0.18). Rewrite TODO.md as honest, source-anchored known-limitations (two-word intersegment pointers, nested/named-union codegen, RPC interface inheritance) instead of a bare wishlist.
225 lines
7.9 KiB
Nim
225 lines
7.9 KiB
Nim
import collections/iface, collections, collections/pprint
|
|
import caprpc/rpcschema, zap
|
|
import reactor, macros, typetraits
|
|
|
|
type
|
|
VatNetwork* = distinct Interface
|
|
|
|
VatId* = string # ...
|
|
|
|
Connection* = object of Pipe[Message]
|
|
|
|
CaprpcException* = object of system.Exception
|
|
exceptionMsg*: rpcschema.Exception
|
|
|
|
CapServerWrapper* = ref object of RootRef
|
|
cap*: CapServer
|
|
|
|
interfaceMethods VatNetwork:
|
|
connect(vatId: VatId): Connection
|
|
## Creates a Connection to a remote vat.
|
|
## If your connection is over TCP or other stream-based protocol,
|
|
## you may use wrapBytePipe to turn BytePipe into Pipe[Message].
|
|
|
|
accept(): Future[Connection]
|
|
## Waits for connection from a remote vat.
|
|
|
|
vatIdToPointer(v: VatId): AnyPointer
|
|
## Convert Vat ID into serializable form
|
|
|
|
vatIdFromPointer(v: AnyPointer): VatId
|
|
## Convert Vat ID from serializable form
|
|
|
|
interfaceMethods CapServer:
|
|
call(ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer]
|
|
## Calls a method on this CapServer.
|
|
|
|
proc toAnyPointerFuture*[T](f: Future[T]): Future[AnyPointer] =
|
|
return f.then(x => x.toAnyPointer)
|
|
|
|
proc castAs*[R, T](f: Future[R], ty: typedesc[T]): Future[T] =
|
|
return f.then(proc(x: R): Future[T] = catchError(castAs(x, T)))
|
|
|
|
proc castAs*[T](f: CapServer, ty: typedesc[T]): T =
|
|
return f.toAnyPointer.castAs(T)
|
|
|
|
proc createFromCap*(t: typedesc[CapServer], cap: CapServer): CapServer =
|
|
return cap
|
|
|
|
# GenericCapServer
|
|
|
|
type GenericCapServer*[T] = ref object of RootObj
|
|
obj: T
|
|
|
|
proc call*[T](self: GenericCapServer[T], ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
mixin capCall, getInterfaceId
|
|
if getInterfaceId(T) != ifaceId:
|
|
return now(error(AnyPointer, "calling invalid interface $1 on $2" % [$ifaceId, name(T)]))
|
|
|
|
return capCall(self.obj, methodId, args)
|
|
|
|
proc toGenericCapServer*[T](obj: T): CapServer =
|
|
return GenericCapServer[T](obj: obj).asCapServer
|
|
|
|
# capServerImpl
|
|
|
|
macro capServerImpl*(impl, ifaces): untyped =
|
|
var ifaces = ifaces
|
|
let implName = impl.repr
|
|
|
|
if ifaces.kind != nnkBracket:
|
|
ifaces = newNimNode(nnkBracket).add(ifaces)
|
|
|
|
let GenericCapServer = genSym(nskType, "GenericCapServer_" & ($impl))
|
|
let ifaceIdSym = genSym(nskParam, "ifaceId")
|
|
let selfSym = genSym(nskParam, "self")
|
|
let methodIdSym = genSym(nskParam, "methodId")
|
|
let argsSym = genSym(nskParam, "args")
|
|
|
|
var callFunc = quote do:
|
|
proc call*(`selfSym`: `GenericCapServer`, `ifaceIdSym`: uint64, `methodIdSym`: uint64, `argsSym`: AnyPointer): Future[AnyPointer] =
|
|
discard
|
|
|
|
if callFunc.kind != nnkStmtList:
|
|
callFunc = newNimNode(nnkStmtList).add(callFunc)
|
|
|
|
for iface in ifaces:
|
|
callFunc[0].body.add(quote do:
|
|
if getInterfaceId(`iface`) == `ifaceIdSym`:
|
|
return capCall(`selfSym`.obj.asInterface(`iface`), `methodIdSym`, `argsSym`))
|
|
|
|
callFunc[0].body.add(quote do:
|
|
return now(error(AnyPointer, "object $1 does not implement this interface ($2)" % [`implName`, $`ifaceIdSym`])))
|
|
|
|
let body = quote do:
|
|
type `GenericCapServer` = ref object of RootObj
|
|
obj: `impl`
|
|
|
|
proc toCapServer*(obj: `impl`): CapServer
|
|
|
|
`callFunc`
|
|
|
|
proc toCapServer(obj: `impl`): CapServer =
|
|
return `GenericCapServer`(obj: obj).asCapServer
|
|
|
|
return body
|
|
|
|
# NothingImplemented
|
|
|
|
proc inlineCap*[T, R](ty: typedesc[T], impl: R): T =
|
|
#for k, f in fieldPairs(impl[]):
|
|
# if k != "toCapServer" and f.isNil: raise newException(Exception, "not all methods implemented ($1 missing)" % k)
|
|
let implIface = impl.asInterface(T)
|
|
when ty isnot CapServer:
|
|
impl.toCapServer = (proc(): CapServer = return toGenericCapServer(implIface))
|
|
return implIface
|
|
|
|
# nothingImplemented
|
|
|
|
let nothingImplemented* = inlineCap(CapServer, CapServerInlineImpl(
|
|
call: (proc(ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
return now(error(AnyPointer, "not implemented")))
|
|
))
|
|
|
|
# null cap
|
|
|
|
type NullCapT* = ref object of RootObj
|
|
|
|
let nullCap* = NullCapT()
|
|
|
|
proc call*(self: NullCapT, ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
return now(error(AnyPointer, "null capability called"))
|
|
|
|
let nullCapInterface = NullCapT().asCapServer
|
|
|
|
converter toCapServer*(x: NullCapT): CapServer =
|
|
return nullCapInterface
|
|
|
|
proc isNullCap*(cap: CapServer): bool =
|
|
return cap.isNil or (cap.Interface.obj == nullCapInterface.Interface.obj and cap.Interface.vtable == nullCapInterface.Interface.vtable)
|
|
|
|
proc isNullCap*[T](cap: T): bool =
|
|
mixin toCapServer
|
|
return cap.isNil or cap.toCapServer.isNullCap
|
|
|
|
# injectCap
|
|
|
|
proc injectInterface*(default: CapServer, interfaces: seq[uint64], joinWith: CapServer): CapServer =
|
|
return inlineCap(CapServer,
|
|
CapServerInlineImpl(call: proc(ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
if ifaceId in interfaces:
|
|
return joinWith.call(ifaceId, methodId, args)
|
|
else:
|
|
return default.call(ifaceId, methodId, args)))
|
|
|
|
proc injectInterface*(default: CapServer, interfaceT: typedesc, joinWith: CapServer): CapServer =
|
|
mixin getInterfaceId
|
|
return injectInterface(default, @[getInterfaceId(interfaceT)], joinWith)
|
|
|
|
proc injectInterface*[T](default: T, interfaceT: typedesc, joinWith: CapServer): T =
|
|
mixin getInterfaceId, toCapServer, castAs
|
|
return injectInterface(default.toCapServer, @[getInterfaceId(interfaceT)], joinWith).castAs(T)
|
|
|
|
# restrictInterfaces
|
|
|
|
proc restrictInterfaces*(self: CapServer, interfaces: seq[uint64]): CapServer =
|
|
return inlineCap(CapServer,
|
|
CapServerInlineImpl(call: proc(ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
if ifaceId in interfaces:
|
|
return self.call(ifaceId, methodId, args)
|
|
else:
|
|
return now(error(AnyPointer, "not implemented"))))
|
|
|
|
proc restrictInterfaces*(self: CapServer, interfaceT: typedesc): CapServer =
|
|
mixin getInterfaceId
|
|
return self.restrictInterfaces(@[getInterfaceId(interfaceT)])
|
|
|
|
proc restrictInterfaces*[T, R](self: T, interfaceT: typedesc[R]): R =
|
|
mixin getInterfaceId, toCapServer
|
|
return self.toCapServer.restrictInterfaces(@[getInterfaceId(interfaceT)]).castAs(R)
|
|
|
|
# gcDestroyingWrapper
|
|
|
|
type GcDestroyingWrapper = ref object of RootRef
|
|
wrapped: CapServer
|
|
destroyCallback: proc()
|
|
|
|
proc call*(self: GcDestroyingWrapper, ifaceId: uint64, methodId: uint64, args: AnyPointer): Future[AnyPointer] =
|
|
return self.wrapped.call(ifaceId, methodId, args)
|
|
|
|
proc gcDestroyingWrapperDestroy(self: GcDestroyingWrapper) =
|
|
self.destroyCallback()
|
|
|
|
proc gcDestroyingWrapper*(wrapped: CapServer, destroyCallback: proc()): CapServer =
|
|
var r: GcDestroyingWrapper
|
|
new(r, gcDestroyingWrapperDestroy)
|
|
r.wrapped = wrapped
|
|
r.destroyCallback = destroyCallback
|
|
return r.asCapServer
|
|
|
|
proc gcDestroyingWrapper*[T](cap: T, destroyCallback: proc()): T =
|
|
mixin toCapServer
|
|
return gcDestroyingWrapper(cap.toCapServer, destroyCallback).castAs(T)
|
|
|
|
# testCopy
|
|
|
|
proc testCopy*[T](t: T) =
|
|
## Test if ``t`` serializes and unserialized correctly. Useful for debugging zap.nim.
|
|
let data = packPointerIgnoringCaps(t)
|
|
echo data.encodeHex
|
|
let unpacker = newUnpackerFlat(data)
|
|
unpacker.getCap = proc(val: RawCapValue): CapServer = return nothingImplemented
|
|
echo unpacker.unpackPointer(0, T).pprint
|
|
|
|
let packer = newPacker()
|
|
packer.buffer &= "\0\0\0\0\0\0\0\0"
|
|
copyPointer(unpacker, 0, packer, 8)
|
|
assert packer.buffer[0..<8] == "\0\0\0\0\0\0\0\0"
|
|
|
|
let copiedData = packer.buffer[8..<packer.buffer.len]
|
|
echo copiedData.encodeHex
|
|
|
|
let unpacker1 = newUnpackerFlat(copiedData)
|
|
unpacker1.getCap = proc(val: RawCapValue): CapServer = return nothingImplemented
|
|
echo unpacker1.unpackPointer(0, T).pprint
|