summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/delegate.cpp
blob: 33d5f8d54e80d5c7c029a361e5981c9c87bf9590 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// license:BSD-3-Clause
// copyright-holders:Aaron Giles,Vas Crabb
/***************************************************************************

    delegate.cpp

    Templates and classes to enable delegates for callbacks.

***************************************************************************/

#include "delegate.h"

#include <cstdio>
#include <sstream>


//**************************************************************************
//  MACROS
//**************************************************************************

#if defined(MAME_DELEGATE_LOG_ADJ)
	#define LOG(...) printf(__VA_ARGS__)
#else
	#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
#endif

// on some architectures, function pointers point to descriptors
// usually this is a global pointer value along with the branch target
// other platforms using this convention include:
// * AIX, Classic MacOS and WinNT on 32-bit POWER/PowerPC
// * pretty much anything on Itanium
#if (defined(__ppc64__) || (defined(__PPC64__))) && !defined(__APPLE__)
	#define MAME_DELEGATE_VT_DESCRIPTOR 1
#endif
#ifndef MAME_DELEGATE_VT_DESCRIPTOR
	#define MAME_DELEGATE_VT_DESCRIPTOR 0
#endif



//**************************************************************************
//  LATE BINDING EXCEPTION
//**************************************************************************

binding_type_exception::binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type)
	: m_target_type(&target_type)
	, m_actual_type(&actual_type)
{
	std::ostringstream os;
	os << "Error performing late bind of function expecting type " << target_type.name() << " to instance of type " << actual_type.name();
	m_what = os.str();
}


char const *binding_type_exception::what() const noexcept
{
	return m_what.c_str();
}



namespace util::detail {

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp = { { 0 } };



//**************************************************************************
//  INTERNAL DELEGATE HELPERS
//**************************************************************************

//-------------------------------------------------
//  delegate_mfp_itanium::convert_to_generic -
//  given an object pointer and member function
//  pointer, apply the displacement and get the
//  actual function pointer
//-------------------------------------------------

delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
{
	// apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
	LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta));
	object = reinterpret_cast<delegate_generic_class *>(
			reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> (MAME_DELEGATE_ITANIUM_ARM ? 1 : 0)));
	LOG("Calculated this=%p ", reinterpret_cast<void const *>(object));

	// test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant
	if (MAME_DELEGATE_ITANIUM_ARM ? !(m_this_delta & 1) : !(m_function & 1))
	{
		// conventional function pointer
		LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
		return reinterpret_cast<delegate_generic_function>(m_function);
	}
	else
	{
		// byte index into the vtable to the function
		std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1);
		delegate_generic_function result;
		if (MAME_DELEGATE_VT_DESCRIPTOR)
			result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
		else
			result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
		LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(result));
		return result;
	}
}


//-------------------------------------------------
//  delegate_mfp_msvc::adjust_this_pointer - given
//  an object pointer and member function pointer,
//  apply the displacement
//-------------------------------------------------

void delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
{
	LOG("Input this=%p ", reinterpret_cast<void const *>(object));
	if (sizeof(single_base_equiv) < m_size)
		LOG("thisdelta=%d ", m_this_delta);
	if (sizeof(unknown_base_equiv) == m_size)
		LOG("vptrdelta=%d vindex=%d ", m_vptr_offs, m_vt_index);
	std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object);
	if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
	{
		byteptr += m_vptr_offs;
		std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr);
		byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
	}
	if (sizeof(single_base_equiv) < m_size)
		byteptr += m_this_delta;
	LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr));
	object = reinterpret_cast<delegate_generic_class *>(byteptr);
}

} // namespace util::detail
an>L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); /* reset hook */ luaL_error(L, "interrupted!"); } /* ** Function to be called at a C signal. Because a C signal cannot ** just change a Lua state (as there is no proper synchronization), ** this function only sets a hook that, when called, will stop the ** interpreter. */ static void laction (int i) { signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } static void print_usage (const char *badoption) { lua_writestringerror("%s: ", progname); if (badoption[1] == 'e' || badoption[1] == 'l') lua_writestringerror("'%s' needs argument\n", badoption); else lua_writestringerror("unrecognized option '%s'\n", badoption); lua_writestringerror( "usage: %s [options] [script [args]]\n" "Available options are:\n" " -e stat execute string 'stat'\n" " -i enter interactive mode after executing 'script'\n" " -l name require library 'name'\n" " -v show version information\n" " -E ignore environment variables\n" " -- stop handling options\n" " - stop handling options and execute stdin\n" , progname); } /* ** Prints an error message, adding the program name in front of it ** (if present) */ static void l_message (const char *pname, const char *msg) { if (pname) lua_writestringerror("%s: ", pname); lua_writestringerror("%s\n", msg); } /* ** Check whether 'status' is not OK and, if so, prints the error ** message on the top of the stack. It assumes that the error object ** is a string, as it was either generated by Lua or by 'msghandler'. */ static int report (lua_State *L, int status) { if (status != LUA_OK) { const char *msg = lua_tostring(L, -1); l_message(progname, msg); lua_pop(L, 1); /* remove message */ } return status; } /* ** Message handler used to run all chunks */ static int msghandler (lua_State *L) { const char *msg = lua_tostring(L, 1); if (msg == NULL) { /* is error object not a string? */ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ return 1; /* that is the message */ else msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1)); } luaL_traceback(L, L, msg, 1); /* append a standard traceback */ return 1; /* return the traceback */ } /* ** Interface to 'lua_pcall', which sets appropriate message function ** and C-signal handler. Used to run all chunks. */ static int docall (lua_State *L, int narg, int nres) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ globalL = L; /* to be available to 'laction' */ signal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); signal(SIGINT, SIG_DFL); /* reset C-signal handler */ lua_remove(L, base); /* remove message handler from the stack */ return status; } static void print_version (void) { lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); lua_writeline(); } /* ** Create the 'arg' table, which stores all arguments from the ** command line ('argv'). It should be aligned so that, at index 0, ** it has 'argv[script]', which is the script name. The arguments ** to the script (everything after 'script') go to positive indices; ** other arguments (before the script name) go to negative indices. ** If there is no script name, assume interpreter's name as base. */ static void createargtable (lua_State *L, char **argv, int argc, int script) { int i, narg; if (script == argc) script = 0; /* no script name? */ narg = argc - (script + 1); /* number of positive indices */ lua_createtable(L, narg, script + 1); for (i = 0; i < argc; i++) { lua_pushstring(L, argv[i]); lua_rawseti(L, -2, i - script); } lua_setglobal(L, "arg"); } static int dochunk (lua_State *L, int status) { if (status == LUA_OK) status = docall(L, 0, 0); return report(L, status); } static int dofile (lua_State *L, const char *name) { return dochunk(L, luaL_loadfile(L, name)); } static int dostring (lua_State *L, const char *s, const char *name) { return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); } /* ** Calls 'require(name)' and stores the result in a global variable ** with the given name. */ static int dolibrary (lua_State *L, const char *name) { int status; lua_getglobal(L, "require"); lua_pushstring(L, name); status = docall(L, 1, 1); /* call 'require(name)' */ if (status == LUA_OK) lua_setglobal(L, name); /* global[name] = require return */ return report(L, status); } /* ** Returns the string to be used as a prompt by the interpreter. */ static const char *get_prompt (lua_State *L, int firstline) { const char *p; lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); p = lua_tostring(L, -1); if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); return p; } /* mark in error messages for incomplete statements */ #define EOFMARK "<eof>" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) /* ** Check whether 'status' signals a syntax error and the error ** message at the top of the stack ends with the above mark for ** incomplete statements. */ static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { lua_pop(L, 1); return 1; } } return 0; /* else... */ } /* ** Prompt the user, read a line, and push it into the Lua stack. */ static int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); if (readstatus == 0) return 0; /* no input (prompt will be popped by caller) */ lua_pop(L, 1); /* remove prompt */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else lua_pushlstring(L, b, l); lua_freeline(L, b); return 1; } /* ** Try to compile line on the stack as 'return <line>;'; on return, stack ** has either compiled chunk or original line (if compilation failed). */ static int addreturn (lua_State *L) { const char *line = lua_tostring(L, -1); /* original line */ const char *retline = lua_pushfstring(L, "return %s;", line); int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); if (status == LUA_OK) { lua_remove(L, -2); /* remove modified line */ if (line[0] != '\0') /* non empty? */ lua_saveline(L, line); /* keep history */ } else lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ return status; } /* ** Read multiple lines until a complete Lua statement */ static int multiline (lua_State *L) { for (;;) { /* repeat until gets a complete statement */ size_t len; const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) { lua_saveline(L, line); /* keep history */ return status; /* cannot or should not try to add continuation line */ } lua_pushliteral(L, "\n"); /* add newline... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ } } /* ** Read a line and try to load (compile) it first as an expression (by ** adding "return " in front of it) and second as a statement. Return ** the final status of load/call with the resulting function (if any) ** in the top of the stack. */ static int loadline (lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); return status; } /* ** Prints (calling the Lua 'print' function) any values on the stack */ static void l_print (lua_State *L) { int n = lua_gettop(L); if (n > 0) { /* any result to be printed? */ luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); lua_getglobal(L, "print"); lua_insert(L, 1); if (lua_pcall(L, n, 0, 0) != LUA_OK) l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", lua_tostring(L, -1))); } } /* ** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and ** print any results. */ static void doREPL (lua_State *L) { int status; const char *oldprogname = progname; progname = NULL; /* no 'progname' on errors in interactive mode */ while ((status = loadline(L)) != -1) { if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); if (status == LUA_OK) l_print(L); else report(L, status); } lua_settop(L, 0); /* clear stack */ lua_writeline(); progname = oldprogname; } /* ** Push on the stack the contents of table 'arg' from 1 to #arg */ static int pushargs (lua_State *L) { int i, n; if (lua_getglobal(L, "arg") != LUA_TTABLE) luaL_error(L, "'arg' is not a table"); n = (int)luaL_len(L, -1); luaL_checkstack(L, n + 3, "too many arguments to script"); for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); lua_remove(L, -i); /* remove table from the stack */ return n; } static int handle_script (lua_State *L, char **argv) { int status; const char *fname = argv[0]; if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) fname = NULL; /* stdin */ status = luaL_loadfile(L, fname); if (status == LUA_OK) { int n = pushargs(L); /* push arguments to script */ status = docall(L, n, LUA_MULTRET); } return report(L, status); } /* bits of various argument indicators in 'args' */ #define has_error 1 /* bad option */ #define has_i 2 /* -i */ #define has_v 4 /* -v */ #define has_e 8 /* -e */ #define has_E 16 /* -E */ /* ** Traverses all arguments from 'argv', returning a mask with those ** needed before running any Lua code (or an error code if it finds ** any invalid argument). 'first' returns the first not-handled argument ** (either the script name or a bad argument in case of error). */ static int collectargs (char **argv, int *first) { int args = 0; int i; for (i = 1; argv[i] != NULL; i++) { *first = i; if (argv[i][0] != '-') /* not an option? */ return args; /* stop handling options */ switch (argv[i][1]) { /* else check option */ case '-': /* '--' */ if (argv[i][2] != '\0') /* extra characters after '--'? */ return has_error; /* invalid option */ *first = i + 1; return args; case '\0': /* '-' */ return args; /* script "name" is '-' */ case 'E': if (argv[i][2] != '\0') /* extra characters after 1st? */ return has_error; /* invalid option */ args |= has_E; break; case 'i': args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ case 'v': if (argv[i][2] != '\0') /* extra characters after 1st? */ return has_error; /* invalid option */ args |= has_v; break; case 'e': args |= has_e; /* FALLTHROUGH */ case 'l': /* both options need an argument */ if (argv[i][2] == '\0') { /* no concatenated argument? */ i++; /* try next 'argv' */ if (argv[i] == NULL || argv[i][0] == '-') return has_error; /* no next argument or it is another option */ } break; default: /* invalid option */ return has_error; } } *first = i; /* no script name */ return args; } /* ** Processes options 'e' and 'l', which involve running Lua code. ** Returns 0 if some code raises an error. */ static int runargs (lua_State *L, char **argv, int n) { int i; for (i = 1; i < n; i++) { int option = argv[i][1]; lua_assert(argv[i][0] == '-'); /* already checked */ if (option == 'e' || option == 'l') { int status; const char *extra = argv[i] + 2; /* both options need an argument */ if (*extra == '\0') extra = argv[++i]; lua_assert(extra != NULL); status = (option == 'e') ? dostring(L, extra, "=(command line)") : dolibrary(L, extra); if (status != LUA_OK) return 0; } } return 1; } static int handle_luainit (lua_State *L) { const char *name = "=" LUA_INITVARVERSION; const char *init = getenv(name + 1); if (init == NULL) { name = "=" LUA_INIT_VAR; init = getenv(name + 1); /* try alternative name */ } if (init == NULL) return LUA_OK; else if (init[0] == '@') return dofile(L, init+1); else return dostring(L, init, name); } /* ** Main body of stand-alone interpreter (to be called in protected mode). ** Reads the options and handles them all. */ static int pmain (lua_State *L) { int argc = (int)lua_tointeger(L, 1); char **argv = (char **)lua_touserdata(L, 2); int script; int args = collectargs(argv, &script); luaL_checkversion(L); /* check that interpreter has correct version */ if (argv[0] && argv[0][0]) progname = argv[0]; if (args == has_error) { /* bad arg? */ print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; } if (args & has_v) /* option '-v'? */ print_version(); if (args & has_E) { /* option '-E'? */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } luaL_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } if (!runargs(L, argv, script)) /* execute arguments -e and -l */ return 0; /* something failed */ if (script < argc && /* execute main script (if there is one) */ handle_script(L, argv + script) != LUA_OK) return 0; if (args & has_i) /* -i option? */ doREPL(L); /* do read-eval-print loop */ else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); doREPL(L); /* do read-eval-print loop */ } else dofile(L, NULL); /* executes stdin as a file */ } lua_pushboolean(L, 1); /* signal no errors */ return 1; } int main (int argc, char **argv) { int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ status = lua_pcall(L, 2, 1, 0); /* do the call */ result = lua_toboolean(L, -1); /* get result */ report(L, status); lua_close(L); return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; }