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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
|
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
#ifndef MAME_LIB_UTIL_DYNAMICCLASS_IPP
#define MAME_LIB_UTIL_DYNAMICCLASS_IPP
#pragma once
#include "dynamicclass.h"
namespace util {
namespace detail {
/// \brief Get offset to saved base virtual table pointer from type info
///
/// Gets the offset to the saved base class virtual table pointer from
/// the location the dynamic derived class virtual table entry used for
/// recovery points to.
/// \return Offset from the type info to saved base class virtual table
/// pointer in bytes.
inline std::ptrdiff_t dynamic_derived_class_base::base_vtable_offset()
{
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
return 0;
#else
return
reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_base_vtable) -
reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_type_info);
#endif
}
/// \brief Get base class virtual table pointer
///
/// Gets the base class virtual pointer for an instance of a dynamic
/// derived class.
/// \tparam Base The base class type (usually determined automatically).
/// \param [in] object Base class member of dynamic derived class
/// instance.
/// \return Pointer to first virtual member function entry in base class
/// virtual table.
template <class Base>
inline std::uintptr_t const *dynamic_derived_class_base::get_base_vptr(
Base const &object)
{
auto const vptr = *reinterpret_cast<std::uintptr_t const *>(&object);
auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
return *reinterpret_cast<std::uintptr_t const *const *>(recovery + base_vtable_offset());
}
/// \brief Restore base class virtual table pointer
///
/// Restores the base class virtual pointer in an instance of a dynamic
/// derived class. Must be called before calling the base class
/// destructor.
/// \tparam Base The base class type (usually determined automatically).
/// \param [in,out] object Base class member of dynamic derived class
/// instance.
template <class Base>
inline void dynamic_derived_class_base::restore_base_vptr(
Base &object)
{
auto &vptr = *reinterpret_cast<std::uintptr_t *>(&object);
auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
vptr = *reinterpret_cast<std::uintptr_t const *>(recovery + base_vtable_offset());
assert(reinterpret_cast<void const *>(vptr));
}
/// \brief Resolve pointer to base class member function
///
/// Given an instance and pointer to a base class member function, gets
/// the adjusted \c this pointer and conventional function pointer.
/// \tparam Base The base class type (usually determined automatically).
/// \tparam R Return type of member function (usually determined
/// automatically).
/// \tparam T Parameter types expected by the member function (usually
/// determined automatically).
/// \param [in] object Base class member of dynamic derived class
/// instance.
/// \param [in] func Pointer to member function of base class.
/// \return A \c std::pair containing the conventional function pointer
/// and adjusted \c this pointer.
/// \exception std::invalid_argument Thrown if the \p func argument is
/// not a supported member function. This includes non-virtual member
/// functions and virtual member functions that aren't supported for
/// overriding when using the MSVC C++ ABI.
template <typename Base, typename R, typename... T>
inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> dynamic_derived_class_base::resolve_base_member_function(
Base &object,
R (Base::*func)(T...))
{
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
member_function_pointer_pun_t<decltype(func)> thunk;
thunk.ptr = func;
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
return std::make_pair(
MAME_ABI_CXX_VTABLE_FNDESC
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
&object);
#else
if (thunk.equiv.is_virtual())
{
assert(!thunk.equiv.this_pointer_offset());
auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
return std::make_pair(
MAME_ABI_CXX_VTABLE_FNDESC
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
&object);
}
else
{
return std::make_pair(
reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(thunk.equiv.function_pointer()),
reinterpret_cast<std::uint8_t *>(&object) + thunk.equiv.this_pointer_offset());
}
#endif
}
template <typename Base, typename R, typename... T>
inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> dynamic_derived_class_base::resolve_base_member_function(
Base const &object,
R (Base::*func)(T...) const)
{
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
member_function_pointer_pun_t<decltype(func)> thunk;
thunk.ptr = func;
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
return std::make_pair(
MAME_ABI_CXX_VTABLE_FNDESC
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
&object);
#else
if (thunk.equiv.is_virtual())
{
assert(!thunk.equiv.this_pointer_offset());
auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
return std::make_pair(
MAME_ABI_CXX_VTABLE_FNDESC
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
&object);
}
else
{
return std::make_pair(
reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(thunk.equiv.function_pointer()),
reinterpret_cast<std::uint8_t const *>(&object) + thunk.equiv.this_pointer_offset());
}
#endif
}
/// \brief Complete object destructor for dynamic derived class
///
/// Restores the base class virtual table pointer, calls the extra data
/// and base class destructors, but does not free the memory occupied by
/// the object. Used when the base class type has a virtual destructor
/// to allow deleting instances of a dynamic derived class through
/// pointers to the base class type.
///
/// Only used for the Itanium C++ ABI.
/// \param [in] object Reference to the object to destroy.
template <class Base, typename Extra>
void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::complete_object_destructor(
value_type<Base, Extra> &object)
{
restore_base_vptr(object.base);
object.~value_type();
}
/// \brief Deleting destructor for dynamic derived class
///
/// Restores the base class virtual table pointer, calls the extra data
/// and base class destructors, and frees the memory occupied by the
/// object. Used when the base class type has a virtual destructor to
/// allow deleting instances of a dynamic derived class through pointers
/// to the base class type.
///
/// Only used for the Itanium C++ ABI.
/// \param [in] object Pointer to the object to destroy.
template <class Base, typename Extra>
void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::deleting_destructor(
value_type<Base, Extra> *object)
{
restore_base_vptr(object->base);
delete object;
}
/// \brief Destructor for dynamic derived class
///
/// Restores the base class virtual table pointer, calls the extra data
/// and base class destructors, and optionally frees the memory occupied
/// by the object. Used when the base class type has a virtual
/// destructor to allow deleting instances of a dynamic derived class
/// through pointers to the base class type.
///
/// Only used for the MSVC C++ ABI.
/// \param [in] object Pointer to the object to destroy.
/// \param [in] flags If bit 0 is set, the memory occupied by the object
/// will be freed.
/// \return The supplied object pointer.
template <class Base, typename Extra>
void *MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::scalar_deleting_destructor(
value_type<Base, Extra> *object,
unsigned int flags)
{
restore_base_vptr(object->base);
object->~value_type();
if (flags & 1)
operator delete (static_cast<void *>(object));
return object;
}
/// \brief Deleter for dynamic derived classes
///
/// Restores the base class virtual table pointer, calls the extra data
/// and base class destructors, and frees the memory occupied by the
/// object. Used to delete instances of a dynamic derived class when
/// the base class type does not have a virtual destructor.
/// \param [in] object Pointer to the object to destroy.
template <class Base, typename Extra>
void dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<!std::has_virtual_destructor_v<Base> > >::operator()(
Base *object) const
{
restore_base_vptr(*object);
delete reinterpret_cast<value_type<Base, Extra> *>(object);
}
} // namespace detail
/// \brief Create a dynamic derived class
///
/// Creates a new dynamic derived class. No base member functions are
/// overridden initially.
/// \param [in] name The unmangled name for the new dynamic derived
/// class. This will be mangled for use in the generated type info.
template <class Base, typename Extra, std::size_t VirtualCount>
dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
std::string_view name) :
detail::dynamic_derived_class_base(name)
{
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
#else
m_type_info.base_type = &typeid(Base);
m_vtable[0] = 0; // offset to top
m_vtable[1] = std::uintptr_t(&m_type_info); // type info
#endif
if constexpr (std::has_virtual_destructor_v<Base>)
{
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
{
if (MAME_ABI_CXX_VTABLE_FNDESC)
{
std::copy_n(
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor)),
MEMBER_FUNCTION_SIZE,
&m_vtable[VTABLE_PREFIX_ENTRIES]);
}
else
{
m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor);
}
}
else
{
if (MAME_ABI_CXX_VTABLE_FNDESC)
{
std::copy_n(
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor)),
MEMBER_FUNCTION_SIZE,
&m_vtable[VTABLE_PREFIX_ENTRIES]);
std::copy_n(
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor)),
MEMBER_FUNCTION_SIZE,
&m_vtable[VTABLE_PREFIX_ENTRIES + MEMBER_FUNCTION_SIZE]);
}
else
{
m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor);
m_vtable[VTABLE_PREFIX_ENTRIES + 1] = std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor);
}
}
}
std::fill(
std::next(m_vtable.begin(), VTABLE_PREFIX_ENTRIES + (FIRST_OVERRIDABLE_MEMBER_OFFSET * MEMBER_FUNCTION_SIZE)),
m_vtable.end(),
std::uintptr_t(static_cast<void *>(nullptr)));
}
/// \brief Create a dynamic derived class using a prototype
///
/// Creates a new dynamic derived class using an existing dynamic
/// derived class as a prototype. Overridden base class member
/// functions are inherited from the current state of the prototype.
/// The new dynamic derived class is not affected by any future
/// modifications to the prototype. The prototype may be destroyed
/// safely before the new dynamic derived class is destroyed (provided
/// all its instances are destroyed first).
/// \param [in] prototype The dynamic derived class to use as a
/// prototype.
/// \param [in] name The unmangled name for the new dynamic derived
/// class. This will be mangled for use in the generated type info.
template <class Base, typename Extra, std::size_t VirtualCount>
dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
dynamic_derived_class const &prototype,
std::string_view name) :
detail::dynamic_derived_class_base(name),
m_vtable(prototype.m_vtable),
m_overridden(prototype.m_overridden)
{
m_base_vtable = prototype.m_base_vtable;
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
#else
m_type_info.base_type = &typeid(Base);
m_vtable[1] = std::uintptr_t(&m_type_info); // type info
#endif
}
/// \brief Override a virtual member function
///
/// Replace the virtual table entry for the specified base member
/// function with the supplied function. This applies to existing
/// instances as well as newly created instances. Note that if you are
/// using some technique to resolve pointers to virtual member functions
/// in advance, resolved pointers may not reflect the change.
/// \tparam R Return type of member function to override (usually
/// determined automatically).
/// \tparam T Parameter types expected by the member function to
/// override (usually determined automatically).
/// \param [in] slot A pointer to the base class member function to
/// override. Must be a pointer to a virtual member function.
/// \param [in] func A pointer to the function to use to override the
/// base class member function.
/// \exception std::invalid_argument Thrown if the \p slot argument is
/// not a supported virtual member function.
/// \sa restore_base_member_function
template <class Base, typename Extra, std::size_t VirtualCount>
template <typename R, typename... T>
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
R (Base::*slot)(T...),
R MAME_ABI_CXX_MEMBER_CALL (*func)(type &, T...))
{
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
member_function_pointer_pun_t<decltype(slot)> thunk;
thunk.ptr = slot;
override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
}
template <class Base, typename Extra, std::size_t VirtualCount>
template <typename R, typename... T>
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
R (Base::*slot)(T...) const,
R MAME_ABI_CXX_MEMBER_CALL (*func)(type const &, T...))
{
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
member_function_pointer_pun_t<decltype(slot)> thunk;
thunk.ptr = slot;
override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
}
/// \brief Restore the base implementation of a member function
///
/// If the specified virtual member function of the base class has been
/// overridden, restore the base class implementation. This applies to
/// existing instances as well as newly created instances. Note that if
/// you are using some technique to resolve pointers to virtual member
/// functions in advance, resolved pointers may not reflect the change.
/// \tparam R Return type of member function to restore (usually
/// determined automatically).
/// \tparam T Parameter types expected by the member function to
/// to restore (usually determined automatically).
/// \param [in] slot A pointer to the base class member function to
/// restore. Must be a pointer to a virtual member function.
/// \exception std::invalid_argument Thrown if the \p slot argument is
/// not a supported virtual member function.
/// \sa override_member_function
template <class Base, typename Extra, std::size_t VirtualCount>
template <typename R, typename... T>
void dynamic_derived_class<Base, Extra, VirtualCount>::restore_base_member_function(
R (Base::*slot)(T...))
{
member_function_pointer_pun_t<decltype(slot)> thunk;
thunk.ptr = slot;
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(slot));
assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
if (m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] && m_base_vtable)
{
std::copy_n(
reinterpret_cast<std::uintptr_t const *>(m_base_vtable) + (index * MEMBER_FUNCTION_SIZE),
MEMBER_FUNCTION_SIZE,
&m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
}
m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = false;
}
/// \brief Create a new instance
///
/// Creates a new instance of the dynamic derived class constructed with
/// the supplied arguments.
/// \tparam T Constructor argument types (usually determined
/// automatically).
/// \param [out] object Receives an pointer to the object storing the
/// base type and extra data.
/// \param [in] args Constructor arguments for the object to be
/// instantiated. If the first argument is
/// \c std::piecewise_construct, it must be followed by two tuples
/// containing the arguments to forward to the base class and extra
/// data constructors, respectively. If the first argument is not
/// \c std::piecewise_construct, all the arguments are forwarded to
/// the base class constructor.
/// \return A unique pointer to the new instance.
template <class Base, typename Extra, std::size_t VirtualCount>
template <typename... T>
typename dynamic_derived_class<Base, Extra, VirtualCount>::pointer dynamic_derived_class<Base, Extra, VirtualCount>::instantiate(
type *&object,
T &&... args)
{
std::unique_ptr<type> result(new type(std::forward<T>(args)...));
auto &vptr = *reinterpret_cast<std::uintptr_t const **>(&result->base);
if (!m_base_vtable)
{
assert(std::uintptr_t(result.get()) == std::uintptr_t(&result->base));
m_base_vtable = vptr;
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
m_vtable[1] = vptr[-1]; // use the base class complete object locator - too hard to fake
for (std::size_t i = 0; VirtualCount > i; ++i)
{
if (!m_overridden[i])
{
std::size_t const offset = (i + FIRST_OVERRIDABLE_MEMBER_OFFSET) * MEMBER_FUNCTION_SIZE;
std::copy_n(vptr + offset, MEMBER_FUNCTION_SIZE, &m_vtable[VTABLE_PREFIX_ENTRIES + offset]);
}
}
}
vptr = &m_vtable[VTABLE_PREFIX_ENTRIES];
object = result.get();
return pointer(&result.release()->base);
}
/// \brief Replace member function in virtual table
///
/// Does the actual work involved in replacing a virtual table entry to
/// override a virtual member function of the base class, avoiding
/// duplication between overloads.
/// \param [in] slot Internal representation of pointer to a virtual
/// member function of the base class. May be modified.
/// \param [in] func A pointer to the function to use to override the
/// base class member function reinterpreted as an unsigned integer of
/// equivalent size.
/// \param [in] size Size of the member function pointer type for the
/// \p slot argument.
/// \exception std::invalid_argument Thrown if the \p slot argument is
/// not a supported virtual member function.
template <class Base, typename Extra, std::size_t VirtualCount>
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
member_function_pointer_equiv &slot,
std::uintptr_t func,
std::size_t size)
{
std::size_t const index = resolve_virtual_member_slot(slot, size);
assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = true;
if (MAME_ABI_CXX_VTABLE_FNDESC)
{
std::copy_n(
reinterpret_cast<std::uintptr_t const *>(func),
MEMBER_FUNCTION_SIZE,
&m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
}
else
{
m_vtable[VTABLE_PREFIX_ENTRIES + index] = func;
}
}
} // namespace util
#endif // MAME_LIB_UTIL_DYNAMICCLASS_IPP
|