[yunq] Add codegen for clients and servers (untested).

This commit is contained in:
Drew Galbraith 2023-10-24 15:57:10 -07:00
parent 963cc0b4fa
commit 71e3521b87
12 changed files with 472 additions and 75 deletions

View File

@ -30,6 +30,7 @@ static const uint64_t FAILED_PRECONDITION = 0x8;
static const uint64_t INTERNAL = 0x100;
static const uint64_t UNIMPLEMENTED = 0x101;
static const uint64_t EXHAUSTED = 0x102;
static const uint64_t INVALID_RESPONSE = 0x103;
// Kernel specific error codes (relating to capabilities).
static const uint64_t CAP_NOT_FOUND = 0x1000;

46
yunq/client.cpp.jinja Normal file
View File

@ -0,0 +1,46 @@
// Generated file - DO NOT MODIFY
#include "{{file}}.client.h"
#include <glacier/buffer/byte_buffer.h>
#include <glacier/buffer/cap_buffer.h>
{% for interface in interfaces %}
{% for method in interface.methods %}
glcr::ErrorCode {{method.name}}(const {{method.request}}& request, {{method.response}}& response) {
uint64_t buffer_size = 0x1000;
// FIXME: Maybe raise this limit.
uint64_t cap_size = 100;
glcr::ByteBuffer buffer(buffer_size);
glcr::CapBuffer caps(cap_size);
const uint32_t kSentinel = 0xBEEFDEAD;
buffer.WriteAt<uint32_t>(0, kSentinel);
buffer.WriteAt<uint64_t>(8, {method_number});
uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps);
buffer.WriteAt<uint32_t>(4, 16 + length);
z_cap_t reply_port_cap;
// FIXME: We need to be able to send capabilities via endpoint call.
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr()));
// FIXME: Add a way to zero out the first buffer.
glcr::ByteBuffer recv_buffer(buffer_size);
glcr::CapBuffer recv_caps(cap_size);
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr()));
if (recv_buffer.At<uint32_t>(0) != kSentinel) {
return glcr::INVALID_RESPONSE;
}
// Check Response Code.
RET_ERR(recv_buffer.At<uint64_t>(8));
response.ParseFromBytes(recv_buffer, 16, recv_caps);
return glcr::OK;
}
{% endfor %}
{% endfor %}

22
yunq/client.h.jinja Normal file
View File

@ -0,0 +1,22 @@
// Generated file - DO NOT MODIFY
#pragma once
#include <ztypes.h>
#include "{{file}}.h"
{% for interface in interfaces -%}
class {{interface.name}}Client {
public:
{{interface.name}}Client(z_cap_t {{interface.name}}_cap) : endpoint_({{interface.name}}_cap) {}
{{interface.name}}Client(const {{interface.name}}Client&) = delete;
{{interface.name}}Client({{interface.name}}Client&&) = delete;
{% for method in interface.methods %}
[[nodiscard]] glcr::ErrorCode {{method.name}}(const {{method.request}}& request, {{method.response}}& response);
{% endfor %}
private:
z_cap_t endpoint_;
};
{%- endfor %}

View File

@ -0,0 +1,45 @@
// Generated file - DO NOT MODIFY
#include "example.yunq.client.h"
#include <glacier/buffer/byte_buffer.h>
#include <glacier/buffer/cap_buffer.h>
glcr::ErrorCode open(const OpenFileRequest& request, File& response) {
uint64_t buffer_size = 0x1000;
// FIXME: Maybe raise this limit.
uint64_t cap_size = 100;
glcr::ByteBuffer buffer(buffer_size);
glcr::CapBuffer caps(cap_size);
const uint32_t kSentinel = 0xBEEFDEAD;
buffer.WriteAt<uint32_t>(0, kSentinel);
buffer.WriteAt<uint64_t>(8, {method_number});
uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps);
buffer.WriteAt<uint32_t>(4, 16 + length);
z_cap_t reply_port_cap;
// FIXME: We need to be able to send capabilities via endpoint call.
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr()));
// FIXME: Add a way to zero out the first buffer.
glcr::ByteBuffer recv_buffer(buffer_size);
glcr::CapBuffer recv_caps(cap_size);
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr()));
if (recv_buffer.At<uint32_t>(0) != kSentinel) {
return glcr::INVALID_RESPONSE;
}
// Check Response Code.
RET_ERR(recv_buffer.At<uint64_t>(8));
response.ParseFromBytes(recv_buffer, 16, recv_caps);
return glcr::OK;
}

View File

@ -0,0 +1,18 @@
// Generated file - DO NOT MODIFY
#pragma once
#include <ztypes.h>
#include "example.yunq.h"
class VFSClient {
public:
VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {}
VFSClient(const VFSClient&) = delete;
VFSClient(VFSClient&&) = delete;
[[nodiscard]] glcr::ErrorCode open(const OpenFileRequest& request, File& response);
private:
z_cap_t endpoint_;
};

View File

@ -1,3 +1,4 @@
// Generated file -- DO NOT MODIFY.
#include "example.yunq.h"
namespace {
@ -26,86 +27,148 @@ void WriteHeader(glcr::ByteBuffer& bytes, uint64_t offset, uint32_t core_size, u
}
} // namespace
void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) {
CheckHeader(bytes);
auto {field.name}_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * {loop.index}));
// Parse path.
auto path_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * 0));
set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length));
set_options(bytes.At<uint64_t>(offset + header_size + (8 * 2)));
// Parse options.
set_options(bytes.At<uint64_t>(offset + header_size + (8 * 1)));
}
void {message.name}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) {
void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) {
CheckHeader(bytes);
auto {field.name}_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * {loop.index}));
// Parse path.
auto path_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * 0));
set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length));
set_options(bytes.At<uint64_t>(offset + header_size + (8 * 2)));
// Parse options.
set_options(bytes.At<uint64_t>(offset + header_size + (8 * 1)));
}
uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) {
uint32_t next_extension = header_size + 8 * 2;
const uint32_t core_size = next_extension;
// Write path.
ExtPointer path_ptr{
.offset = next_extension,
// FIXME: Check downcast of str length.
.length = (uint32_t)path().length(),
};
bytes.WriteStringAt(offset + next_extension, path());
next_extension += path_ptr.length;
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 0), path_ptr);
// Write options.
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 1), options());
// The next extension pointer is the length of the message.
WriteHeader(bytes, offset, core_size, next_extension);
return next_extension;
}
uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) {
uint32_t next_extension = header_size + 8 * 2;
const uint32_t core_size = next_extension;
uint64_t next_cap = 0;
// Write path.
ExtPointer path_ptr{
.offset = next_extension,
// FIXME: Check downcast of str length.
.length = (uint32_t)path().length(),
};
bytes.WriteStringAt(offset + next_extension, path());
next_extension += path_ptr.length;
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 0), path_ptr);
// Write options.
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 1), options());
// The next extension pointer is the length of the message.
WriteHeader(bytes, offset, core_size, next_extension);
return next_extension;
}
void File::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) {
CheckHeader(bytes);
auto {field.name}_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * {loop.index}));
// Parse path.
auto path_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * 0));
set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length));
set_attrs(bytes.At<uint64_t>(offset + header_size + (8 * 2)));
// Parse attrs.
set_attrs(bytes.At<uint64_t>(offset + header_size + (8 * 1)));
// Parse mem_cap.
// FIXME: Implement in-buffer capabilities for inprocess serialization.
set_mem_cap(0);
}
void {message.name}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) {
void File::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) {
CheckHeader(bytes);
auto {field.name}_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * {loop.index}));
// Parse path.
auto path_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * 0));
set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length));
set_attrs(bytes.At<uint64_t>(offset + header_size + (8 * 2)));
uint64_t mem_cap_ptr = bytes.At<uint64_t>(offset + header_size + (8 * {offset}));
// Parse attrs.
set_attrs(bytes.At<uint64_t>(offset + header_size + (8 * 1)));
// Parse mem_cap.
uint64_t mem_cap_ptr = bytes.At<uint64_t>(offset + header_size + (8 * 2));
set_mem_cap(caps.At(mem_cap_ptr));
}
uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) {
uint32_t next_extension = header_size + 8 * 3;
const uint32_t core_size = next_extension;
// Write path.
ExtPointer path_ptr{
.offset = next_extension,
// FIXME: Check downcast of str length.
.length = (uint32_t)path().length(),
};
bytes.WriteStringAt(offset + next_extension, path());
next_extension += path_ptr.length;
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 0), path_ptr);
// Write attrs.
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 1), attrs());
// Write mem_cap.
// FIXME: Implement inbuffer capabilities.
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 2), 0);
// The next extension pointer is the length of the message.
WriteHeader(bytes, offset, core_size, next_extension);
return next_extension;
}
uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) {
uint32_t next_extension = header_size + 8 * 3;
const uint32_t core_size = next_extension;
uint64_t next_cap = 0;
// Write path.
ExtPointer path_ptr{
.offset = next_extension,
// FIXME: Check downcast of str length.
.length = (uint32_t)path().length(),
};
bytes.WriteStringAt(offset + next_extension, path());
next_extension += path_ptr.length;
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 0), path_ptr);
// Write attrs.
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 1), attrs());
// Write mem_cap.
caps.WriteAt(next_cap, mem_cap());
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 2), next_cap++);
// The next extension pointer is the length of the message.
WriteHeader(bytes, offset, core_size, next_extension);
return next_extension;
}

View File

@ -5,9 +5,6 @@
#include <glacier/buffer/cap_buffer.h>
#include <glacier/string/string.h>
#include <ztypes.h>
class OpenFileRequest {
public:
OpenFileRequest() {}
@ -19,24 +16,16 @@ class OpenFileRequest {
void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&);
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset);
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&);
glcr::String path() { return path_; }
void set_path(const glcr::String& value) { path_ = value; }
uint64_t options() { return options_; }
void set_options(const uint64_t& value) { options_ = value; }
private:
glcr::String path_;
uint64_t options_;
}
};
class File {
public:
File() {}
@ -48,25 +37,16 @@ class File {
void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&);
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset);
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&);
glcr::String path() { return path_; }
void set_path(const glcr::String& value) { path_ = value; }
uint64_t attrs() { return attrs_; }
void set_attrs(const uint64_t& value) { attrs_ = value; }
z_cap_t mem_cap() { return mem_cap_; }
void set_mem_cap(const z_cap_t& value) { mem_cap_ = value; }
private:
glcr::String path_;
uint64_t attrs_;
z_cap_t mem_cap_;
}
};

View File

@ -0,0 +1,45 @@
// Generated file - DO NOT MODIFY
#include "example.yunq.client.h"
#include <glacier/buffer/byte_buffer.h>
#include <glacier/buffer/cap_buffer.h>
glcr::ErrorCode open(const OpenFileRequest& request, File& response) {
uint64_t buffer_size = 0x1000;
// FIXME: Maybe raise this limit.
uint64_t cap_size = 100;
glcr::ByteBuffer buffer(buffer_size);
glcr::CapBuffer caps(cap_size);
const uint32_t kSentinel = 0xBEEFDEAD;
buffer.WriteAt<uint32_t>(0, kSentinel);
buffer.WriteAt<uint64_t>(8, {method_number});
uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps);
buffer.WriteAt<uint32_t>(4, 16 + length);
z_cap_t reply_port_cap;
// FIXME: We need to be able to send capabilities via endpoint call.
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr()));
// FIXME: Add a way to zero out the first buffer.
glcr::ByteBuffer recv_buffer(buffer_size);
glcr::CapBuffer recv_caps(cap_size);
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr()));
if (recv_buffer.At<uint32_t>(0) != kSentinel) {
return glcr::INVALID_RESPONSE;
}
// Check Response Code.
RET_ERR(recv_buffer.At<uint64_t>(8));
response.ParseFromBytes(recv_buffer, 16, recv_caps);
return glcr::OK;
}

View File

@ -0,0 +1,32 @@
// Generated File -- DO NOT MODIFY.
#pragma once
#include <mammoth/thread.h>
#include <ztypes.h>
#include "example.yunq.h"
class VFSServerBase {
public:
VFSServerBase(z_cap_t VFS_cap) : {}
VFSServerBase(const VFSServerBase&) = delete;
VFSServerBase(VFSServerBase&&) = delete;
[[nodiscard]] Thread RunServer();
[[nodiscard]] virtual glcr::ErrorCode Handleopen(const OpenFileRequest&, File&) = 0;
private:
z_cap_t endpoint_;
friend void VFSServerBaseThreadBootstrap(void*);
void ServerThread();
[[nodiscard]] glcr::ErrorCode HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response,
glcr::CapBuffer& resp_caps);
};

93
yunq/server.cpp.jinja Normal file
View File

@ -0,0 +1,93 @@
// Generated file -- DO NOT MODIFY.
#include "{{file}}.server.h"
#include <mammoth/debug.h>
#include <zcall.h>
namespace {
const uint32_t kSentinel = 0xBEEFDEAD;
const uint32_t kHeaderSize = 0x10;
void WriteError(glcr::ByteBuffer& buffer, glcr::ErrorCode err) {
buffer.WriteAt<uint32_t>(0, kSentinel);
buffer.WriteAt<uint32_t>(4, kHeaderSize);
buffer.WriteAt<uint64_t>(8, err);
}
void WriteHeader(glcr::ByteBuffer& buffer, uint64_t message_length) {
buffer.WriteAt<uint32_t>(0, kSentinel);
buffer.WriteAt<uint32_t>(4, kHeaderSize + message_length);
buffer.WriteAt<uint64_t>(8, glcr::OK);
}
} // namespace
{% for interface in interfaces %}
void {{interface.name}}ServerBaseThreadBootstrap(void* server_base) {
({{interface.name}}ServerBase*)(server_base)->ServerThread();
}
Thread {{interface.name}}::RunServer() {
return Thread({{interface.name}}ServerBaseThreadBootstrap, this);
}
void {{interface.name}}::ServerThread() {
glcr::ByteBuffer recv_buffer(0x1000);
glcr::ByteBuffer resp_buffer(0x1000);
uint64_t resp_cap_size = 0x10;
glcr::CapBuffer resp_cap(resp_cap_size);
z_cap_t reply_port_cap;
while (true) {
uint64_t recv_buf_size = 0x1000;
glcr::ErrorCode recv_err = ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &reply_port_cap);
if (recv_err != glcr::OK) {
dbgln("Error in receive: %x", recv_err);
continue;
}
uint64_t resp_length = 0;
glcr::ErrorCode err = HandlRequest(recv_buffer, resp_buffer, resp_length, resp_cap);
if (err != glcr::OK) {
WriteError(resp_buffer, err);
ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr);
} else {
WriteHeader(resp_buffer, resp_length);
ZReplyPortSend(reply_port_cap, resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr());
}
}
}
glcr::ErrorCode {{interface.name}}::HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response, uint64_t& resp_length
glcr::CapBuffer& resp_caps) {
if (recv_buffer.At<uint32_t>(0) != kSentinel) {
return glcr::INVALID_INPUT;
}
uint64_t method_select = recv_buffer.At<uint64_t>(8);
switch(method_select) {
{%- for method in interface.methods %}
case {{loop.index0}}: {
{{method.request}} request;
{{method.response}} response;
request.ParseFromBytes(recv_buffer, kHeaderSize);
RET_ERR(Handle{{method.name}}(request, response));
resp_length = response.SerializeToBytes(resp_buffer, kHeaderSize, resp_cap);
break;
}
{%- endfor %}
default: {
return glcr::UNIMPLEMENTED;
}
}
return glcr::OK;
}
{% endfor %}

33
yunq/server.h.jinja Normal file
View File

@ -0,0 +1,33 @@
// Generated File -- DO NOT MODIFY.
#pragma once
#include <mammoth/thread.h>
#include <ztypes.h>
#include "{{file}}.h"
{% for interface in interfaces %}
class {{interface.name}}ServerBase {
public:
{{interface.name}}ServerBase(z_cap_t {{interface.name}}_cap) : {}
{{interface.name}}ServerBase(const {{interface.name}}ServerBase&) = delete;
{{interface.name}}ServerBase({{interface.name}}ServerBase&&) = delete;
[[nodiscard]] Thread RunServer();
{% for method in interface.methods %}
[[nodiscard]] virtual glcr::ErrorCode Handle{{method.name}}(const {{method.request}}&, {{method.response}}&) = 0;
{% endfor %}
private:
z_cap_t endpoint_;
friend void {{interface.name}}ServerBaseThreadBootstrap(void*);
void ServerThread();
[[nodiscard]] glcr::ErrorCode HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response,
glcr::CapBuffer& resp_caps);
};
{% endfor %}

View File

@ -38,6 +38,25 @@ def main():
message_impl = message_impl_tmpl.render(file=filename, messages=messages)
f.write(message_impl)
client_header_tmpl = jinja_env.get_template("client.h.jinja")
with open(filename + '.client.h', mode='w') as f:
client_header = client_header_tmpl.render(file=filename, interfaces=interfaces)
f.write(client_header)
client_impl_tmpl = jinja_env.get_template("client.cpp.jinja")
with open(filename + '.client.cpp', mode='w') as f:
client_impl = client_impl_tmpl.render(file=filename, interfaces=interfaces)
f.write(client_impl)
server_header_tmpl = jinja_env.get_template("server.h.jinja")
with open(filename + '.server.h', mode='w') as f:
server_header = server_header_tmpl.render(file=filename, interfaces=interfaces)
f.write(server_header)
server_impl_tmpl = jinja_env.get_template("server.cpp.jinja")
with open(filename + '.server.cpp', mode='w') as f:
server_impl = client_impl_tmpl.render(file=filename, interfaces=interfaces)
f.write(server_impl)
if __name__ == "__main__":
main()