mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
Add msgpack_rpc_dispatch
/metadata generator
This adds a lua script which parses the contents of 'api.h'. After the api is parsed into a metadata table. After that, it will generate: - A msgpack blob for the metadata table. This msgpack object contains everything scripting engines need to generate their own wrappers for the remote API. - The `msgpack_rpc_dispatch` function, which takes care of validating msgpack requests, converting arguments to C types and passing control to the appropriate 'api.h' function. The result is then serialized back to msgpack and returned to the client. This approach was used because: - It automatically modifies `msgpack_rpc_dispatch` to reflect API changes. - Scripting engines that generate remote call wrappers using the msgpack metadata will also adapt automatically to API changes
This commit is contained in:
parent
6eeb006c4a
commit
35ff53c6b0
135
scripts/msgpack-gen.lua
Normal file
135
scripts/msgpack-gen.lua
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
lpeg = require('lpeg')
|
||||||
|
msgpack = require('cmsgpack')
|
||||||
|
|
||||||
|
P, R, S = lpeg.P, lpeg.R, lpeg.S
|
||||||
|
C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg
|
||||||
|
|
||||||
|
any = P(1) -- (consume one character)
|
||||||
|
letter = R('az', 'AZ') + S('_$')
|
||||||
|
alpha = letter + R('09')
|
||||||
|
nl = P('\n')
|
||||||
|
not_nl = any - nl
|
||||||
|
ws = S(' \t') + nl
|
||||||
|
fill = ws ^ 0
|
||||||
|
c_comment = P('//') * (not_nl ^ 0)
|
||||||
|
c_preproc = P('#') * (not_nl ^ 0)
|
||||||
|
c_id = letter * (alpha ^ 0)
|
||||||
|
c_void = P('void')
|
||||||
|
c_raw = P('char') * fill * P('*')
|
||||||
|
c_int = P('uint32_t')
|
||||||
|
c_array = c_raw * fill * P('*') * Cc('array')
|
||||||
|
c_param_type = (
|
||||||
|
(c_array * Cc('array') * fill) +
|
||||||
|
(c_raw * Cc('raw') * fill) +
|
||||||
|
(c_int * Cc('integer') * (ws ^ 1))
|
||||||
|
)
|
||||||
|
c_type = (c_void * Cc('none') * (ws ^ 1)) + c_param_type
|
||||||
|
c_param = Ct(c_param_type * C(c_id))
|
||||||
|
c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0)
|
||||||
|
c_params = Ct(c_void + c_param_list)
|
||||||
|
c_proto = Ct(
|
||||||
|
Cg(c_type, 'rtype') * Cg(c_id, 'fname') *
|
||||||
|
fill * P('(') * fill * Cg(c_params, 'params') * fill * P(')') *
|
||||||
|
fill * P(';')
|
||||||
|
)
|
||||||
|
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
|
||||||
|
|
||||||
|
inputf = assert(arg[1])
|
||||||
|
outputf = assert(arg[2])
|
||||||
|
|
||||||
|
input = io.open(inputf, 'rb')
|
||||||
|
api = grammar:match(input:read('*all'))
|
||||||
|
input:close()
|
||||||
|
|
||||||
|
-- assign a unique integer id for each api function
|
||||||
|
for i = 1, #api do
|
||||||
|
api[i].id = i
|
||||||
|
end
|
||||||
|
|
||||||
|
output = io.open(outputf, 'wb')
|
||||||
|
|
||||||
|
output:write([[
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <msgpack.h>
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
#include "msgpack_rpc.h"
|
||||||
|
|
||||||
|
static const uint8_t msgpack_metadata[] = {
|
||||||
|
|
||||||
|
]])
|
||||||
|
packed = msgpack.pack(api)
|
||||||
|
for i = 1, #packed do
|
||||||
|
output:write(string.byte(packed, i)..', ')
|
||||||
|
if i % 10 == 0 then
|
||||||
|
output:write('\n ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
output:write([[
|
||||||
|
};
|
||||||
|
|
||||||
|
bool msgpack_rpc_dispatch(msgpack_object *req, msgpack_packer *res)
|
||||||
|
{
|
||||||
|
uint32_t method_id = (uint32_t)req->via.u64;
|
||||||
|
|
||||||
|
switch (method_id) {
|
||||||
|
case 0:
|
||||||
|
msgpack_rpc_response(req, res);
|
||||||
|
msgpack_pack_nil(res);
|
||||||
|
// The result is the `msgpack_metadata` byte array
|
||||||
|
msgpack_pack_raw(res, sizeof(msgpack_metadata));
|
||||||
|
msgpack_pack_raw_body(res, msgpack_metadata, sizeof(msgpack_metadata));
|
||||||
|
return true;
|
||||||
|
]])
|
||||||
|
|
||||||
|
for i = 1, #api do
|
||||||
|
local fn
|
||||||
|
local args = {}
|
||||||
|
fn = api[i]
|
||||||
|
output:write('\n case '..fn.id..':')
|
||||||
|
for j = 1, #fn.params do
|
||||||
|
local expected, convert, param
|
||||||
|
local idx = tostring(j - 1)
|
||||||
|
param = fn.params[j]
|
||||||
|
ref = '(req->via.array.ptr[3].via.array.ptr + '..idx..')'
|
||||||
|
-- decide which validation/conversion to use for this argument
|
||||||
|
if param[1] == 'array' then
|
||||||
|
expected = 'MSGPACK_OBJECT_ARRAY'
|
||||||
|
convert = 'msgpack_rpc_array_argument'
|
||||||
|
elseif param[1] == 'raw' then
|
||||||
|
expected = 'MSGPACK_OBJECT_RAW'
|
||||||
|
convert = 'msgpack_rpc_raw_argument'
|
||||||
|
elseif param[1] == 'integer' then
|
||||||
|
expected = 'MSGPACK_OBJECT_POSITIVE_INTEGER'
|
||||||
|
convert = 'msgpack_rpc_integer_argument'
|
||||||
|
end
|
||||||
|
output:write('\n if ('..ref..'->type != '..expected..') {')
|
||||||
|
output:write('\n return msgpack_rpc_error(req, res, "Wrong argument types");')
|
||||||
|
output:write('\n }')
|
||||||
|
table.insert(args, convert..'('..ref..')')
|
||||||
|
end
|
||||||
|
local call_args = table.concat(args, ', ')
|
||||||
|
-- convert the result back to msgpack
|
||||||
|
if fn.rtype == 'none' then
|
||||||
|
output:write('\n '..fn.fname..'('..call_args..');')
|
||||||
|
output:write('\n return msgpack_rpc_void_result(req, res);\n')
|
||||||
|
else
|
||||||
|
if fn.rtype == 'array' then
|
||||||
|
convert = 'msgpack_rpc_array_result'
|
||||||
|
elseif fn.rtype == 'raw' then
|
||||||
|
convert = 'msgpack_rpc_raw_result'
|
||||||
|
elseif fn.rtype == 'integer' then
|
||||||
|
convert = 'msgpack_rpc_integer_result'
|
||||||
|
end
|
||||||
|
output:write('\n return '..convert..'('..fn.fname..'('..call_args..'), req, res);\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
output:write([[
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]])
|
||||||
|
output:close()
|
@ -1,5 +1,27 @@
|
|||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
|
|
||||||
|
if(DEFINED ENV{LUA_BINARY})
|
||||||
|
set(LUA_BIN $ENV{LUA_BINARY})
|
||||||
|
else()
|
||||||
|
set(LUA_BIN ${DEPS_BIN_DIR}/luajit)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/auto)
|
||||||
|
set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/msgpack-gen.lua)
|
||||||
|
set(API_HEADER ${PROJECT_SOURCE_DIR}/src/api.h)
|
||||||
|
set(MSGPACK_RPC_HEADER ${PROJECT_SOURCE_DIR}/src/msgpack_rpc.h)
|
||||||
|
set(MSGPACK_DISPATCH ${GENERATED_DIR}/msgpack_dispatch.c)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY ${GENERATED_DIR})
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${MSGPACK_DISPATCH}
|
||||||
|
COMMAND ${LUA_BIN} ${DISPATCH_GENERATOR} ${API_HEADER} ${MSGPACK_DISPATCH}
|
||||||
|
DEPENDS
|
||||||
|
${API_HEADER}
|
||||||
|
${MSGPACK_RPC_HEADER}
|
||||||
|
${DISPATCH_GENERATOR}
|
||||||
|
)
|
||||||
|
|
||||||
file( GLOB NEOVIM_SOURCES *.c )
|
file( GLOB NEOVIM_SOURCES *.c )
|
||||||
|
|
||||||
foreach(sfile ${NEOVIM_SOURCES})
|
foreach(sfile ${NEOVIM_SOURCES})
|
||||||
@ -11,6 +33,7 @@ endforeach()
|
|||||||
|
|
||||||
list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove})
|
list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove})
|
||||||
list(APPEND NEOVIM_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c")
|
list(APPEND NEOVIM_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c")
|
||||||
|
list(APPEND NEOVIM_SOURCES "${MSGPACK_DISPATCH}")
|
||||||
|
|
||||||
file( GLOB OS_SOURCES os/*.c )
|
file( GLOB OS_SOURCES os/*.c )
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user