#ifndef JOHNSON_JROOT_H
#define JOHNSON_JROOT_H
#define _JROOT_NAMESIZE 200L
#define _JROOT_ERRSIZE 500L
#define _JROOT_ROOT (void*)(1)
#define OUR_CONTEXT(js_context) \
({ \
JohnsonContext* _context; \
const VALUE _ruby_context = (VALUE)JS_GetContextPrivate(js_context); \
Data_Get_Struct(_ruby_context, JohnsonContext, _context); \
_context; \
})
#define OUR_RUNTIME(js_context) \
({ \
JohnsonRuntime* _johnson_runtime; \
JSRuntime * _js_runtime = JS_GetRuntime(js_context);\
const VALUE _ruby_runtime = (VALUE)JS_GetRuntimePrivate(_js_runtime); \
Data_Get_Struct(_ruby_runtime, JohnsonRuntime, _johnson_runtime); \
_johnson_runtime; \
})
#define _PREPARE_JROOTS(rb, context, cleancount) \
const bool _jroot_ruby = (rb); \
const int _jroot_cleans = (cleancount); \
void (*_jroot_cleanup[_jroot_cleans])(JSContext*, void*); \
void* _jroot_cleanup_data[_jroot_cleans]; \
JSContext* const _jroot_context = (context); \
int _jroot_cleanidx = 0;
#define PREPARE_JROOTS(context, cleancount) \
_PREPARE_JROOTS(false, context, cleancount)
#define PREPARE_RUBY_JROOTS(context, cleancount) \
_PREPARE_JROOTS(true, context, cleancount)
#define JCLEANUP(func, data) \
do \
{ \
assert(_jroot_cleanidx < _jroot_cleans); \
_jroot_cleanup[_jroot_cleanidx] = (func); \
_jroot_cleanup_data[_jroot_cleanidx] = (data); \
_jroot_cleanidx++; \
} while(0)
#define _JROOT(ptr, name) \
do \
{ \
static char _name[_JROOT_NAMESIZE] = ""; \
void* const _root = (ptr); \
if (*_name == '\0') \
snprintf(_name, _JROOT_NAMESIZE, "%s[%d]:%s: %s", __FILE__, __LINE__, __func__, (name)); \
JCHECK(JS_AddNamedRoot(_jroot_context, _root, _name)); \
JCLEANUP(_JROOT_ROOT, _root); \
} while(0)
#define JROOT(var) _JROOT(&(var), #var)
#define JROOT_PTR(ptr) _JROOT(ptr, #ptr)
#define JUNROOT(var) \
do \
{ \
void* const _jroot_match = &(var); \
int _jroot_i; \
for (_jroot_i = _jroot_cleanidx - 1; _jroot_i >= 0; _jroot_i--) \
if (_jroot_cleanup[_jroot_i] == _JROOT_ROOT && _jroot_cleanup_data[_jroot_i] == _jroot_match) \
{ \
JS_RemoveRoot(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
if (_jroot_i == _jroot_cleanidx - 1) _jroot_cleanidx--; \
_jroot_cleanup[_jroot_i] = NULL; \
} \
} while (0)
#define REMOVE_JROOTS \
do \
{ \
int _jroot_i; \
for (_jroot_i = _jroot_cleanidx - 1; _jroot_i >= 0; _jroot_i--) \
{ \
if (_jroot_cleanup[_jroot_i] == _JROOT_ROOT) \
JS_RemoveRoot(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
else if (_jroot_cleanup[_jroot_i]) \
(_jroot_cleanup[_jroot_i])(_jroot_context, _jroot_cleanup_data[_jroot_i]); \
} \
} while (0)
#define JCHECK_RUBY(cond) \
do \
{ \
assert(_jroot_ruby); \
if (!(cond)) \
{ \
REMOVE_JROOTS; \
raise_js_error_in_ruby(OUR_RUNTIME(_jroot_context)); \
} \
} while (0)
#define JCHECK(cond) \
do \
{ \
if (!(cond)) \
{ \
REMOVE_JROOTS; \
if (_jroot_ruby) \
raise_js_error_in_ruby(OUR_RUNTIME(_jroot_context)); \
else \
return JS_FALSE; \
} \
} while (0)
#define JPROTECT(func, data) \
({ \
int _state; \
const VALUE _old_errinfo = ruby_errinfo; \
const VALUE _result = rb_protect((func), (data), &_state); \
if (_state) \
{ \
REMOVE_JROOTS; \
if (_jroot_ruby) \
rb_jump_tag(_state); \
else \
return report_ruby_error_in_js(OUR_RUNTIME(_jroot_context), _state, _old_errinfo); \
} \
_result; \
})
#define JRETURN \
do \
{ \
assert(!_jroot_ruby); \
REMOVE_JROOTS; \
return JS_TRUE; \
} while(0)
#define JRETURN_RUBY(value) \
do \
{ \
assert(_jroot_ruby); \
typeof(value) _jroot_result = (value); \
REMOVE_JROOTS; \
return _jroot_result; \
} while(0)
#define JERROR(format, args...) \
do \
{ \
REMOVE_JROOTS; \
char _jroot_msg[_JROOT_ERRSIZE]; \
snprintf(_jroot_msg, _JROOT_ERRSIZE, (format) , ## args); \
if (_jroot_ruby) \
rb_raise(rb_eRuntimeError, _jroot_msg); \
else \
{ \
JSString* _jroot_err_str = JS_NewStringCopyZ(_jroot_context, _jroot_msg); \
if (_jroot_err_str) JS_SetPendingException(_jroot_context, STRING_TO_JSVAL(_jroot_err_str)); \
return JS_FALSE; \
} \
} while(0)
#define ARGLIST1(a) _data->a
#define ARGLIST2(a, b) _data->a, _data->b
#define ARGLIST3(a, b, c) _data->a, _data->b, _data->c
#define ARGLIST4(a, b, c, d) _data->a, _data->b, _data->c, _data->d
#define ARGLIST5(a, b, c, d, e) _data->a, _data->b, _data->c, _data->d, _data->e
#define DECLARE_RUBY_WRAPPER(name, args) \
typedef struct { args; } name ## _args; \
VALUE name ## _invoke(VALUE magic);
#define DEFINE_RUBY_WRAPPER(name, func, arglist) \
VALUE name ## _invoke(VALUE magic) \
{ \
name ## _args * _data = (name ## _args *)(FIX2LONG(magic) << 2); \
return func(arglist); \
}
#define RUBY_WRAPPER_ARG(name, args...) ({ name ## _args _x = { args }; LONG2FIX((long)(&_x) >> 2); })
#define RUBY_WRAPPER(name) name ## _invoke
#define CALL_RUBY_WRAPPER(name, args...) JPROTECT(RUBY_WRAPPER(name), RUBY_WRAPPER_ARG(name, args))
#endif