Skip to content
Snippets Groups Projects
Commit bae1b6b7 authored by Konstantin Osipov's avatar Konstantin Osipov
Browse files

Merge branch 'reflection-v2' into 1.6

parents 5690f1e4 baa69491
No related branches found
No related tags found
No related merge requests found
......@@ -47,6 +47,7 @@ set (core_sources
exception.cc
coro.cc
object.cc
reflection.c
assoc.c
)
......
......@@ -28,11 +28,17 @@
*/
#include "error.h"
#include <stdio.h>
#include <typeinfo>
static struct method clienterror_methods[] = {
make_method(&type_ClientError, "code", &ClientError::errcode),
METHODS_SENTINEL
};
const struct type type_ClientError = make_type("ClientError", &type_Exception,
clienterror_methods);
ClientError::ClientError(const char *file, unsigned line,
uint32_t errcode, ...)
: Exception(file, line)
: Exception(&type_ClientError, file, line)
{
m_errcode = errcode;
va_list ap;
......@@ -44,7 +50,7 @@ ClientError::ClientError(const char *file, unsigned line,
ClientError::ClientError(const char *file, unsigned line, const char *msg,
uint32_t errcode)
: Exception(file, line)
: Exception(&type_ClientError, file, line)
{
m_errcode = errcode;
strncpy(m_errmsg, msg, sizeof(m_errmsg) - 1);
......@@ -61,10 +67,10 @@ ClientError::log() const
uint32_t
ClientError::get_errcode(const Exception *e)
{
const ClientError *error = dynamic_cast<const ClientError *>(e);
if (error)
return error->errcode();
if (typeid(*e) == typeid(OutOfMemory))
ClientError *client_error = type_cast(ClientError, e);
if (client_error)
return client_error->errcode();
if (type_cast(OutOfMemory, e))
return ER_MEMORY_ISSUE;
return ER_PROC_LUA;
}
......
......@@ -31,6 +31,7 @@
#include "errcode.h"
#include "exception.h"
extern const struct type type_ClientError;
class ClientError: public Exception {
public:
virtual void raise()
......
......@@ -128,22 +128,22 @@ lbox_error_last(lua_State *L)
} else {
lua_newtable(L);
lua_pushstring(L, "message");
lua_pushstring(L, e->errmsg());
lua_settable(L, -3);
lua_pushstring(L, "type");
lua_pushstring(L, e->type());
lua_settable(L, -3);
lua_pushstring(L, "code");
lua_pushinteger(L, ClientError::get_errcode(e));
lua_pushstring(L, e->type->name);
lua_settable(L, -3);
if (SystemError *se = dynamic_cast<SystemError *>(e)) {
lua_pushstring(L, "errno");
lua_pushinteger(L, se->errnum());
lua_settable(L, -3);
type_foreach_method(e->type, method) {
if (method_invokable<const char *>(method, e)) {
const char *s = method_invoke<const char *>(method, e);
lua_pushstring(L, method->name);
lua_pushstring(L, s);
lua_settable(L, -3);
} else if (method_invokable<int>(method, e)) {
int code = method_invoke<int>(method, e);
lua_pushstring(L, method->name);
lua_pushinteger(L, code);
lua_settable(L, -3);
}
}
}
return 1;
......
......@@ -782,7 +782,7 @@ MemtxEngine::waitCheckpoint()
} catch (Exception *e) {
e->log();
result = -1;
SystemError *se = dynamic_cast<SystemError *>(e);
SystemError *se = type_cast(SystemError, e);
if (se)
errno = se->errnum();
}
......
......@@ -54,9 +54,10 @@ static const log_magic_t eof_marker = mp_bswap_u32(0xd510aded); /* host byte ord
static const char inprogress_suffix[] = ".inprogress";
static const char v12[] = "0.12\n";
const struct type type_XlogError = make_type("XlogError", &type_Exception);
XlogError::XlogError(const char *file, unsigned line,
const char *format, ...)
:Exception(file, line)
:Exception(&type_XlogError, file, line)
{
va_list ap;
va_start(ap, format);
......@@ -64,10 +65,23 @@ XlogError::XlogError(const char *file, unsigned line,
va_end(ap);
}
XlogError::XlogError(const struct type *type, const char *file, unsigned line,
const char *format, ...)
:Exception(type, file, line)
{
va_list ap;
va_start(ap, format);
vsnprintf(m_errmsg, sizeof(m_errmsg), format, ap);
va_end(ap);
}
const struct type type_XlogGapError =
make_type("XlogGapError", &type_XlogError);
XlogGapError::XlogGapError(const char *file, unsigned line,
const struct vclock *from,
const struct vclock *to)
:XlogError(file, line, "")
:XlogError(&type_XlogGapError, file, line, "")
{
char *s_from = vclock_to_string(from);
char *s_to = vclock_to_string(to);
......
......@@ -43,6 +43,9 @@ struct XlogError: public Exception
{
XlogError(const char *file, unsigned line,
const char *format, ...);
protected:
XlogError(const struct type *type, const char *file, unsigned line,
const char *format, ...);
};
struct XlogGapError: public XlogError
......
......@@ -221,7 +221,7 @@ xrow_decode_error(struct xrow_header *row)
{
uint32_t code = row->type & (IPROTO_TYPE_ERROR - 1);
char error[TNT_ERRMSG_MAX] = { 0 };
char error[EXCEPTION_ERRMSG_MAX] = { 0 };
const char *pos;
uint32_t map_size;
......
......@@ -33,7 +33,6 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <typeinfo>
/** out_of_memory::size is zero-initialized by the linker. */
static OutOfMemory out_of_memory(__FILE__, __LINE__,
......@@ -45,6 +44,16 @@ diag_get()
return &fiber()->diag;
}
static const struct method exception_methods[] = {
make_method(&type_Exception, "message", &Exception::errmsg),
make_method(&type_Exception, "file", &Exception::file),
make_method(&type_Exception, "line", &Exception::line),
make_method(&type_Exception, "log", &Exception::log),
METHODS_SENTINEL
};
const struct type type_Exception = make_type("Exception", NULL,
exception_methods);
void *
Exception::operator new(size_t size)
{
......@@ -68,9 +77,17 @@ Exception::~Exception()
}
}
Exception::Exception(const char *file, unsigned line)
: m_ref(0), m_file(file), m_line(line){
m_errmsg[0] = 0;
Exception::Exception(const struct type *type_arg, const char *file,
unsigned line)
:type(type_arg), m_ref(0) {
if (m_file != NULL) {
snprintf(m_file, sizeof(m_file), "%s", file);
m_line = line;
} else {
m_file[0] = '\0';
m_line = 0;
}
m_errmsg[0] = '\0';
if (this == &out_of_memory) {
/* A special workaround for out_of_memory static init */
out_of_memory.m_ref = 1;
......@@ -78,33 +95,31 @@ Exception::Exception(const char *file, unsigned line)
}
}
const char *
Exception::type() const
{
const char *name = typeid(*this).name();
/** A quick & dirty version of name demangle for class names */
char *res = NULL;
(void) strtol(name, &res, 10);
return res && strlen(res) ? res : name;
}
void
Exception::log() const
{
_say(S_ERROR, m_file, m_line, m_errmsg, "%s", type());
_say(S_ERROR, m_file, m_line, m_errmsg, "%s", type->name);
}
static const struct method systemerror_methods[] = {
make_method(&type_SystemError, "errnum", &SystemError::errnum),
METHODS_SENTINEL
};
const struct type type_SystemError = make_type("SystemError", &type_Exception,
systemerror_methods);
SystemError::SystemError(const char *file, unsigned line)
: Exception(file, line),
m_errno(errno)
SystemError::SystemError(const struct type *type,
const char *file, unsigned line)
:Exception(type, file, line),
m_errno(errno)
{
/* nothing */
}
SystemError::SystemError(const char *file, unsigned line,
const char *format, ...)
:Exception(file, line),
: Exception(&type_SystemError, file, line),
m_errno(errno)
{
va_list ap;
......@@ -135,10 +150,13 @@ SystemError::log() const
m_errmsg);
}
const struct type type_OutOfMemory =
make_type("OutOfMemory", &type_SystemError);
OutOfMemory::OutOfMemory(const char *file, unsigned line,
size_t amount, const char *allocator,
const char *object)
:SystemError(file, line)
: SystemError(&type_OutOfMemory, file, line)
{
m_errno = ENOMEM;
snprintf(m_errmsg, sizeof(m_errmsg),
......
......@@ -31,15 +31,20 @@
#include "object.h"
#include <stdarg.h>
#include <assert.h>
#include "reflection.h"
#include "say.h"
enum { TNT_ERRMSG_MAX = 512 };
enum {
EXCEPTION_ERRMSG_MAX = 512,
EXCEPTION_FILE_MAX = 256
};
extern const struct type type_Exception;
class Exception: public Object {
protected:
/** Allocated size. */
size_t size;
public:
const struct type *type; /* reflection */
void *operator new(size_t size);
void operator delete(void*);
virtual void raise()
......@@ -48,14 +53,20 @@ class Exception: public Object {
throw this;
}
const char *type() const;
const char *
errmsg() const
{
return m_errmsg;
}
const char *file() const {
return m_file;
}
int line() const {
return m_line;
}
virtual void log() const;
virtual ~Exception();
......@@ -71,19 +82,20 @@ class Exception: public Object {
}
protected:
Exception(const char *file, unsigned line);
Exception(const struct type *type, const char *file, unsigned line);
/* Ref counter */
size_t m_ref;
/* file name */
const char *m_file;
/* line number */
unsigned m_line;
/* file name */
char m_file[EXCEPTION_FILE_MAX];
/* error description */
char m_errmsg[TNT_ERRMSG_MAX];
char m_errmsg[EXCEPTION_ERRMSG_MAX];
};
extern const struct type type_SystemError;
class SystemError: public Exception {
public:
......@@ -103,7 +115,7 @@ class SystemError: public Exception {
SystemError(const char *file, unsigned line,
const char *format, ...);
protected:
SystemError(const char *file, unsigned line);
SystemError(const struct type *type, const char *file, unsigned line);
void
init(const char *format, ...);
......@@ -116,6 +128,7 @@ class SystemError: public Exception {
int m_errno;
};
extern const struct type type_OutOfMemory;
class OutOfMemory: public SystemError {
public:
OutOfMemory(const char *file, unsigned line,
......
......@@ -36,12 +36,14 @@
#include "assoc.h"
#include "memory.h"
#include "trigger.h"
#include <typeinfo>
static struct cord main_cord;
__thread struct cord *cord_ptr = NULL;
pthread_t main_thread_id;
const struct type type_FiberCancelException =
make_type("FiberCancelException", &type_Exception);
static void
update_last_stack_frame(struct fiber *fiber)
{
......@@ -223,7 +225,8 @@ fiber_join(struct fiber *fiber)
/* Move exception to the caller */
diag_move(&fiber->diag, &fiber()->diag);
Exception *e = diag_last_error(&fiber()->diag);
if (e != NULL && typeid(*e) != typeid(FiberCancelException))
/** Raise exception again, unless it's FiberCancelException */
if (e != NULL && type_cast(FiberCancelException, e) == NULL)
e->raise();
fiber_testcancel();
}
......@@ -705,9 +708,8 @@ cord_costart_thread_func(void *arg)
}
diag_move(&f->diag, &fiber()->diag);
Exception *e = diag_last_error(&fiber()->diag);
if (e != NULL && typeid(*e) != typeid(FiberCancelException)) {
if (e != NULL && type_cast(FiberCancelException, e) == NULL)
e->raise();
}
return NULL;
}
......
......@@ -85,10 +85,11 @@ enum {
* cancelled.
*/
#if defined(__cplusplus)
extern const struct type type_FiberCancelException;
class FiberCancelException: public Exception {
public:
FiberCancelException(const char *file, unsigned line)
: Exception(file, line) {
: Exception(&type_FiberCancelException, file, line) {
/* Nothing */
}
......
......@@ -725,9 +725,10 @@ tarantool_lua_utils_init(struct lua_State *L)
return 0;
}
const struct type type_LuajitError = make_type("LuajitError", &type_Exception);
LuajitError::LuajitError(const char *file, unsigned line,
struct lua_State *L)
:Exception(file, line)
: Exception(&type_LuajitError, file, line)
{
const char *msg = lua_tostring(L, -1);
snprintf(m_errmsg, sizeof(m_errmsg), "%s", msg ? msg : "");
......
......@@ -445,6 +445,7 @@ tarantool_lua_utils_init(struct lua_State *L);
} /* extern "C" */
#include "exception.h"
extern const struct type type_LuajitError;
class LuajitError: public Exception {
public:
LuajitError(const char *file, unsigned line,
......
/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "reflection.h"
/* TODO: sorry, unimplemented: non-trivial designated initializers */
const struct method METHODS_SENTINEL = {
.owner = NULL,
.name = NULL,
.rtype = CTYPE_VOID,
.atype = {},
.nargs = 0,
.isconst = false,
._spacer = {}
};
extern inline bool
type_assignable(const struct type *type, const struct type *object);
extern inline const struct method *
type_method_by_name(const struct type *type, const char *name);
#ifndef TARANTOOL_REFLECTION_H_INCLUDED
#define TARANTOOL_REFLECTION_H_INCLUDED
/*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stddef.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h> /* strcmp */
#if defined(__cplusplus)
extern "C" {
#endif /* defined(__cplusplus) */
struct type;
struct method;
/**
* Primitive C types
*/
enum ctype {
CTYPE_VOID = 0,
CTYPE_INT,
CTYPE_CONST_CHAR_PTR
};
struct type {
const char *name;
const struct type *parent;
const struct method *methods;
};
inline bool
type_assignable(const struct type *type, const struct type *object)
{
assert(object != NULL);
do {
if (object == type)
return true;
assert(object->parent != object);
object = object->parent;
} while (object != NULL);
return false;
}
/**
* Determine if the specified object is assignment-compatible with
* the object represented by type.
*/
#define type_cast(T, obj) ({ \
T *r = NULL; \
if (type_assignable(&type_ ## T, (obj->type))) \
r = (T *) obj; \
(r); \
})
#if defined(__cplusplus)
/* Pointer to arbitrary C++ member function */
typedef void (type::*method_thiscall_f)(void);
#endif
enum { METHOD_ARG_MAX = 8 };
struct method {
const struct type *owner;
const char *name;
enum ctype rtype;
enum ctype atype[METHOD_ARG_MAX];
int nargs;
bool isconst;
union {
/* Add extra space to get proper struct size in C */
void *_spacer[2];
#if defined(__cplusplus)
method_thiscall_f thiscall;
static_assert(sizeof(thiscall) <= sizeof(_spacer),
"sizeof(thiscall)");
#endif /* defined(__cplusplus) */
};
};
#define type_foreach_method(m, method) \
for(const struct type *_m = (m); _m != NULL; _m = _m->parent) \
for (const struct method *(method) = _m->methods; \
(method)->name != NULL; (method)++)
inline const struct method *
type_method_by_name(const struct type *type, const char *name)
{
type_foreach_method(type, method) {
if (strcmp(method->name, name) == 0)
return method;
}
return NULL;
}
extern const struct method METHODS_SENTINEL;
#if defined(__cplusplus)
} /* extern "C" */
/*
* Begin of C++ syntax sugar
*/
/*
* Initializer for struct type without methods
*/
inline type
make_type(const char *name, const type *parent)
{
/* TODO: sorry, unimplemented: non-trivial designated initializers */
type t;
t.name = name;
t.parent = parent;
t.methods = &METHODS_SENTINEL;
return t;
}
/*
* Initializer for struct type with methods
*/
inline struct type
make_type(const char *name, const type *parent, const method *methods)
{
/* TODO: sorry, unimplemented: non-trivial designated initializers */
type t;
t.name = name;
t.parent = parent;
t.methods = methods;
return t;
}
template<typename T> inline enum ctype ctypeof();
template<> inline enum ctype ctypeof<void>() { return CTYPE_VOID; }
template<> inline enum ctype ctypeof<int>() { return CTYPE_INT; }
template<> inline enum ctype ctypeof<const char *>() { return CTYPE_CONST_CHAR_PTR; }
/**
* \cond false
*/
/** A helper for recursive templates */
template <int N, typename A, typename... Args>
struct method_helper {
static bool
invokable(const method *method)
{
if (method->atype[N] != ctypeof<A>())
return false;
return method_helper<N + 1, Args... >::invokable(method);
}
static void
init(struct method *method)
{
method->atype[N] = ctypeof<A>();
return method_helper<N + 1, Args... >::init(method);
}
};
template <int N, typename R>
struct method_helper<N, R> {
static bool
invokable(const method *method)
{
if (method->nargs != N)
return false;
if (method->rtype != ctypeof<R>())
return false;
return true;
}
static void
init(struct method *method)
{
method->rtype = ctypeof<R>();
method->nargs = N;
}
};
/**
* \endcond false
*/
/**
* Initializer for R (T::*)(void) C++ member methods
*/
template<typename R, typename... Args, typename T> inline method
make_method(const struct type *owner, const char *name,
R (T::*method_arg)(Args...))
{
struct method m;
m.owner = owner;
m.name = name;
m.thiscall = (method_thiscall_f) method_arg;
m.isconst = false;
method_helper<0, Args..., R>::init(&m);
return m;
}
template<typename R, typename... Args, typename T> inline method
make_method(const struct type *owner, const char *name,
R (T::*method_arg)(Args...) const)
{
struct method m = make_method(owner, name, (R (T::*)(Args...)) method_arg);
m.isconst = true;
return m;
}
/**
* Check if method is invokable with provided argument types
*/
template<typename R, typename... Args, typename T> inline bool
method_invokable(const struct method *method, T *object)
{
if (!type_assignable(method->owner, object->type))
return false;
return method_helper<0, Args..., R>::invokable(method);
}
/**
* Invoke method with object and provided arguments.
*/
template<typename R, typename... Args, typename T > inline R
method_invoke(const struct method *method, T *object, Args... args)
{
assert((method_invokable<R, Args...>(method, object)));
typedef R (T::*MemberFunction)(Args...);
return (object->*(MemberFunction) method->thiscall)(args...);
}
template<typename R, typename... Args, typename T > inline R
method_invoke(const struct method *method, const T *object, Args... args)
{
assert(method->isconst);
return method_invoke<R, Args...>(object, args...);
}
#endif /* defined(__cplusplus) */
#endif /* TARANTOOL_REFLECTION_H_INCLUDED */
......@@ -46,13 +46,15 @@
#include "say.h"
#include "trivia/util.h"
const struct type type_SocketError = make_type("SocketError",
&type_SystemError);
SocketError::SocketError(const char *file, unsigned line, int fd,
const char *format, ...)
: SystemError(file, line)
: SystemError(&type_SocketError, file, line)
{
int save_errno = errno;
char buf[TNT_ERRMSG_MAX];
char buf[EXCEPTION_ERRMSG_MAX];
va_list ap;
va_start(ap, format);
......
......@@ -43,6 +43,7 @@
enum { SERVICE_NAME_MAXLEN = 32 };
extern const struct type type_SocketError;
class SocketError: public SystemError {
public:
SocketError(const char *file, unsigned line, int fd,
......
......@@ -94,9 +94,11 @@ box.error.raise()
...
box.error.last()
---
- type: ClientError
message: Illegal parameters, bla bla
- line: -1
code: 1
type: ClientError
message: Illegal parameters, bla bla
file: '[C]'
...
box.error.clear()
---
......
......@@ -103,3 +103,8 @@ target_link_libraries(guava.test salad)
add_executable(find_path.test find_path.c
${CMAKE_SOURCE_DIR}/src/find_path.c
)
add_executable(reflection_c.test reflection_c.c unit.c
${CMAKE_SOURCE_DIR}/src/reflection.c)
add_executable(reflection_cxx.test reflection_cxx.cc unit.c
${CMAKE_SOURCE_DIR}/src/reflection.c)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment