diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/private/defs.h | 18 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 2 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 51 | ||||
-rw-r--r-- | src/nvim/os/channel.c | 26 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc.c | 162 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc.h | 13 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc_helpers.c | 55 |
7 files changed, 230 insertions, 97 deletions
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index ee0fc02c4d..b049412014 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -65,8 +65,16 @@ typedef enum { kObjectTypeInteger, kObjectTypeFloat, kObjectTypeString, + kObjectTypeBuffer, + kObjectTypeWindow, + kObjectTypeTabpage, kObjectTypeArray, - kObjectTypeDictionary + kObjectTypeDictionary, + kObjectTypePosition, + kObjectTypeStringArray, + kObjectTypeBufferArray, + kObjectTypeWindowArray, + kObjectTypeTabpageArray, } ObjectType; struct object { @@ -76,8 +84,16 @@ struct object { Integer integer; Float floating; String string; + Buffer buffer; + Window window; + Tabpage tabpage; Array array; Dictionary dictionary; + Position position; + StringArray stringarray; + BufferArray bufferarray; + WindowArray windowarray; + TabpageArray tabpagearray; } data; }; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d5ebc93f7c..024f0c2405 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -426,6 +426,8 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } tv->vval.v_dict->dv_refcount++; break; + default: + abort(); } return true; diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index e1e1a35490..f1b9dc3bc8 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -14,7 +14,9 @@ err->set = true; \ } while (0) -#define BOOL_OBJ(b) ((Object) { \ +#define OBJECT_OBJ(o) o + +#define BOOLEAN_OBJ(b) ((Object) { \ .type = kObjectTypeBoolean, \ .data.boolean = b \ }) @@ -26,26 +28,59 @@ #define STRING_OBJ(s) ((Object) { \ .type = kObjectTypeString, \ - .data.string = cstr_to_string(s) \ + .data.string = s \ }) -#define STRINGL_OBJ(d, s) ((Object) { \ - .type = kObjectTypeString, \ - .data.string = (String) { \ - .size = s, \ - .data = xmemdup(d, s) \ - }}) +#define BUFFER_OBJ(s) ((Object) { \ + .type = kObjectTypeBuffer, \ + .data.buffer = s \ + }) + +#define WINDOW_OBJ(s) ((Object) { \ + .type = kObjectTypeWindow, \ + .data.window = s \ + }) + +#define TABPAGE_OBJ(s) ((Object) { \ + .type = kObjectTypeTabpage, \ + .data.tabpage = s \ + }) #define ARRAY_OBJ(a) ((Object) { \ .type = kObjectTypeArray, \ .data.array = a \ }) +#define STRINGARRAY_OBJ(a) ((Object) { \ + .type = kObjectTypeStringArray, \ + .data.stringarray = a \ + }) + +#define BUFFERARRAY_OBJ(a) ((Object) { \ + .type = kObjectTypeBufferArray, \ + .data.bufferarray = a \ + }) + +#define WINDOWARRAY_OBJ(a) ((Object) { \ + .type = kObjectTypeWindowArray, \ + .data.windowarray = a \ + }) + +#define TABPAGEARRAY_OBJ(a) ((Object) { \ + .type = kObjectTypeTabpageArray, \ + .data.tabpagearray = a \ + }) + #define DICTIONARY_OBJ(d) ((Object) { \ .type = kObjectTypeDictionary, \ .data.dictionary = d \ }) +#define POSITION_OBJ(p) ((Object) { \ + .type = kObjectTypePosition, \ + .data.position = p \ + }) + #define NIL ((Object) {.type = kObjectTypeNil}) #define PUT(dict, k, v) \ diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index b44b1d13a4..9bba247a7b 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -31,7 +31,6 @@ typedef struct { PMap(cstr_t) *subscribed_events; bool is_job, enabled; msgpack_unpacker *unpacker; - msgpack_sbuffer *sbuffer; union { Job *job; struct { @@ -168,7 +167,7 @@ bool channel_send_call(uint64_t id, "Channel %" PRIu64 " was closed due to a high stack depth " "while processing a RPC call", channel->id); - *result = STRING_OBJ(buf); + *result = STRING_OBJ(cstr_to_string(buf)); } uint64_t request_id = channel->next_request_id++; @@ -319,20 +318,12 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) goto end; } - // Each object is a new msgpack-rpc request and requires an empty response - msgpack_packer response; - msgpack_packer_init(&response, channel->sbuffer, msgpack_sbuffer_write); // Perform the call - msgpack_rpc_call(channel->id, &unpacked.data, &response); - WBuffer *buffer = wstream_new_buffer(xmemdup(channel->sbuffer->data, - channel->sbuffer->size), - channel->sbuffer->size, - free); - if (!channel_write(channel, buffer)) { + WBuffer *resp = msgpack_rpc_call(channel->id, &unpacked.data, &out_buffer); + // write the response + if (!channel_write(channel, resp)) { goto end; } - // Clear the buffer for future calls - msgpack_sbuffer_clear(channel->sbuffer); } if (result == kUnpackResultFail) { @@ -379,10 +370,9 @@ static bool channel_write(Channel *channel, WBuffer *buffer) return success; } -static void send_error(Channel *channel, uint64_t id, char *err_msg) +static void send_error(Channel *channel, uint64_t id, char *err) { - String err = {.size = strlen(err_msg), .data = err_msg}; - channel_write(channel, serialize_response(id, err, NIL, channel->sbuffer)); + channel_write(channel, serialize_response(id, err, NIL, &out_buffer)); } static void send_request(Channel *channel, @@ -449,7 +439,6 @@ static void unsubscribe(Channel *channel, char *event) static void close_channel(Channel *channel) { pmap_del(uint64_t)(channels, channel->id); - msgpack_sbuffer_free(channel->sbuffer); msgpack_unpacker_free(channel->unpacker); if (channel->is_job) { @@ -485,7 +474,6 @@ static Channel *register_channel() rv->enabled = true; rv->rpc_call_level = 0; rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - rv->sbuffer = msgpack_sbuffer_new(); rv->id = next_id++; rv->subscribed_events = pmap_new(cstr_t)(); rv->next_request_id = 1; @@ -530,7 +518,7 @@ static void call_stack_unwind(Channel *channel, char *msg, int count) while (kv_size(channel->call_stack) && count--) { ChannelCallFrame *frame = kv_pop(channel->call_stack); frame->errored = true; - frame->result = STRING_OBJ(msg); + frame->result = STRING_OBJ(cstr_to_string(msg)); } } diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 5e2ec6aa10..85569372da 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -8,77 +8,53 @@ #include "nvim/os/wstream.h" #include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/api/private/helpers.h" #include "nvim/func_attr.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/msgpack_rpc.c.generated.h" #endif +extern const uint8_t msgpack_metadata[]; +extern const unsigned int msgpack_metadata_size; + /// Validates the basic structure of the msgpack-rpc call and fills `res` /// with the basic response structure. /// -/// @param id The channel id +/// @param channel_id The channel id /// @param req The parsed request object /// @param res A packer that contains the response -void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3) +WBuffer *msgpack_rpc_call(uint64_t channel_id, + msgpack_object *req, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ARG(2) + FUNC_ATTR_NONNULL_ARG(3) { - // The initial response structure is the same no matter what happens, - // we set it up here - // Array of size 4 - msgpack_pack_array(res, 4); - // Response type is 1 - msgpack_pack_int(res, 1); - - // Validate the basic structure of the msgpack-rpc payload - if (req->type != MSGPACK_OBJECT_ARRAY) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Request is not an array", res); - return; - } - - if (req->via.array.size != 4) { - msgpack_pack_int(res, 0); // no message id yet - char error_msg[256]; - snprintf(error_msg, - sizeof(error_msg), - "Request array size is %u, it should be 4", - req->via.array.size); - msgpack_rpc_error(error_msg, res); - return; - } + uint64_t response_id; + char *err = msgpack_rpc_validate(&response_id, req); - if (req->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Id must be a positive integer", res); - return; + if (err) { + return serialize_response(response_id, err, NIL, sbuffer); } - // Set the response id, which is the same as the request - msgpack_pack_uint64(res, req->via.array.ptr[1].via.u64); + uint64_t method_id = req->via.array.ptr[2].via.u64; - if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Message type must be an integer", res); - return; + if (method_id == 0) { + return serialize_metadata(response_id, channel_id, sbuffer); } - if (req->via.array.ptr[0].via.u64 != 0) { - msgpack_rpc_error("Message type must be 0", res); - return; - } + // dispatch the call + Error error = { .set = false }; + Object rv = msgpack_rpc_dispatch(channel_id, method_id, req, &error); + // send the response + msgpack_packer response; + msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write); - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Method id must be a positive integer", res); - return; + if (error.set) { + return serialize_response(response_id, error.msg, NIL, sbuffer); } - if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { - msgpack_rpc_error("Paremeters must be an array", res); - return; - } - - // dispatch the message - msgpack_rpc_dispatch(id, req, res); + return serialize_response(response_id, NULL, rv, sbuffer); } /// Try to unpack a msgpack document from the data in the unpacker buffer. This @@ -134,19 +110,19 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) } /// Serializes a msgpack-rpc request or notification(id == 0) -WBuffer *serialize_request(uint64_t id, +WBuffer *serialize_request(uint64_t request_id, String method, Object arg, msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(4) { msgpack_packer pac; msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_pack_array(&pac, id ? 4 : 3); - msgpack_pack_int(&pac, id ? 0 : 2); + msgpack_pack_array(&pac, request_id ? 4 : 3); + msgpack_pack_int(&pac, request_id ? 0 : 2); - if (id) { - msgpack_pack_uint64(&pac, id); + if (request_id) { + msgpack_pack_uint64(&pac, request_id); } msgpack_pack_raw(&pac, method.size); @@ -161,19 +137,20 @@ WBuffer *serialize_request(uint64_t id, } /// Serializes a msgpack-rpc response -WBuffer *serialize_response(uint64_t id, - String err, +WBuffer *serialize_response(uint64_t response_id, + char *err_msg, Object arg, msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(4) { msgpack_packer pac; msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); msgpack_pack_array(&pac, 4); msgpack_pack_int(&pac, 1); - msgpack_pack_uint64(&pac, id); + msgpack_pack_uint64(&pac, response_id); - if (err.size) { + if (err_msg) { + String err = {.size = strlen(err_msg), .data = err_msg}; // error message msgpack_pack_raw(&pac, err.size); msgpack_pack_raw_body(&pac, err.data, err.size); @@ -194,3 +171,66 @@ WBuffer *serialize_response(uint64_t id, return rv; } +WBuffer *serialize_metadata(uint64_t id, + uint64_t channel_id, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, 4); + msgpack_pack_int(&pac, 1); + msgpack_pack_uint64(&pac, id); + // Nil error + msgpack_pack_nil(&pac); + // The result is the [channel_id, metadata] array + msgpack_pack_array(&pac, 2); + msgpack_pack_uint64(&pac, channel_id); + msgpack_pack_raw(&pac, msgpack_metadata_size); + msgpack_pack_raw_body(&pac, msgpack_metadata, msgpack_metadata_size); + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_sbuffer_clear(sbuffer); + return rv; +} + +static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) +{ + // response id not known yet + + *response_id = 0; + // Validate the basic structure of the msgpack-rpc payload + if (req->type != MSGPACK_OBJECT_ARRAY) { + return "Request is not an array"; + } + + if (req->via.array.size != 4) { + return "Request array size should be 4"; + } + + if (req->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Id must be a positive integer"; + } + + // Set the response id, which is the same as the request + *response_id = req->via.array.ptr[1].via.u64; + + if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Message type must be an integer"; + } + + if (req->via.array.ptr[0].via.u64 != 0) { + return "Message type must be 0"; + } + + if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Method id must be a positive integer"; + } + + if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { + return "Paremeters must be an array"; + } + + return NULL; +} diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h index cbb487b44a..b8b947c0ec 100644 --- a/src/nvim/os/msgpack_rpc.h +++ b/src/nvim/os/msgpack_rpc.h @@ -22,12 +22,15 @@ typedef enum { /// The implementation is generated at compile time with metadata extracted /// from the api/*.h headers, /// -/// @param id The channel id +/// @param channel_id The channel id +/// @param method_id The method id /// @param req The parsed request object -/// @param res A packer that contains the response -void msgpack_rpc_dispatch(uint64_t id, - msgpack_object *req, - msgpack_packer *res) +/// @param err Pointer to error structure +/// @return Some object +Object msgpack_rpc_dispatch(uint64_t channel_id, + uint64_t method_id, + msgpack_object *req, + Error *err) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index 3af6794169..e2c277abe4 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -231,12 +231,41 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) msgpack_rpc_from_array(result.data.array, res); break; + case kObjectTypePosition: + msgpack_rpc_from_position(result.data.position, res); + break; + + case kObjectTypeBuffer: + msgpack_rpc_from_buffer(result.data.buffer, res); + break; + + case kObjectTypeWindow: + msgpack_rpc_from_window(result.data.window, res); + break; + + case kObjectTypeTabpage: + msgpack_rpc_from_tabpage(result.data.tabpage, res); + break; + + case kObjectTypeStringArray: + msgpack_rpc_from_stringarray(result.data.stringarray, res); + break; + + case kObjectTypeBufferArray: + msgpack_rpc_from_bufferarray(result.data.bufferarray, res); + break; + + case kObjectTypeWindowArray: + msgpack_rpc_from_windowarray(result.data.windowarray, res); + break; + + case kObjectTypeTabpageArray: + msgpack_rpc_from_tabpagearray(result.data.tabpagearray, res); + break; + case kObjectTypeDictionary: msgpack_rpc_from_dictionary(result.data.dictionary, res); break; - - default: - abort(); } } @@ -282,6 +311,10 @@ void msgpack_rpc_free_object(Object value) case kObjectTypeBoolean: case kObjectTypeInteger: case kObjectTypeFloat: + case kObjectTypePosition: + case kObjectTypeBuffer: + case kObjectTypeWindow: + case kObjectTypeTabpage: break; case kObjectTypeString: @@ -292,6 +325,22 @@ void msgpack_rpc_free_object(Object value) msgpack_rpc_free_array(value.data.array); break; + case kObjectTypeStringArray: + msgpack_rpc_free_stringarray(value.data.stringarray); + break; + + case kObjectTypeBufferArray: + msgpack_rpc_free_bufferarray(value.data.bufferarray); + break; + + case kObjectTypeWindowArray: + msgpack_rpc_free_windowarray(value.data.windowarray); + break; + + case kObjectTypeTabpageArray: + msgpack_rpc_free_tabpagearray(value.data.tabpagearray); + break; + case kObjectTypeDictionary: msgpack_rpc_free_dictionary(value.data.dictionary); break; |