mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
exc_server_variants: Templatize and use CompositeMachMessageServer.
The implementations for the exc and mach_exc subsystems were nearly identical, and were a good target for templatization. The existing split between exc and mach_exc was a good candidate for unification based on CompositeMachMessageServer instead of the custom unification previously done in UniversalMachMessageServer. TEST=util_test ExcServerVariants.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/766193006
This commit is contained in:
parent
302c87c6e5
commit
508a33dc7a
File diff suppressed because it is too large
Load Diff
@ -19,403 +19,14 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "util/mach/mach_message_server.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
// Routines to provide a single unified front-end to the interfaces in
|
||||
// <mach/exc.defs> and <mach/mach_exc.defs>. The two interfaces are identical,
|
||||
// except that the latter allows for 64-bit exception codes, and is requested by
|
||||
// setting the MACH_EXCEPTION_CODES behavior bit associated with an exception
|
||||
// port.
|
||||
|
||||
namespace internal {
|
||||
|
||||
//! \brief A server interface for the `exc` Mach subsystem.
|
||||
class ExcServer : public MachMessageServer::Interface {
|
||||
public:
|
||||
//! \brief An interface that the different request messages that are a part of
|
||||
//! the `exc` Mach subsystem can be dispatched to.
|
||||
class Interface {
|
||||
public:
|
||||
//! \brief Handles exceptions raised by `exception_raise()`.
|
||||
//!
|
||||
//! This behaves equivalently to a `catch_exception_raise()` function used
|
||||
//! with `exc_server()`.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
//! \param[out] destroy_request `true` if the request message is to be
|
||||
//! destroyed even when this method returns success. See
|
||||
//! MachMessageServer::Interface.
|
||||
virtual kern_return_t CatchExceptionRaise(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) = 0;
|
||||
|
||||
//! \brief Handles exceptions raised by `exception_raise_state()`.
|
||||
//!
|
||||
//! This behaves equivalently to a `catch_exception_raise_state()` function
|
||||
//! used with `exc_server()`.
|
||||
//!
|
||||
//! There is no \a destroy_request parameter because, unlike
|
||||
//! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the
|
||||
//! request message is not complex (it does not carry the \a thread or \a
|
||||
//! task port rights) and thus there is nothing to destroy.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
virtual kern_return_t CatchExceptionRaiseState(
|
||||
exception_handler_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer) = 0;
|
||||
|
||||
//! \brief Handles exceptions raised by `exception_raise_state_identity()`.
|
||||
//!
|
||||
//! This behaves equivalently to a `catch_exception_raise_state_identity()`
|
||||
//! function used with `exc_server()`.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
//! \param[out] destroy_request `true` if the request message is to be
|
||||
//! destroyed even when this method returns success. See
|
||||
//! MachMessageServer::Interface.
|
||||
virtual kern_return_t CatchExceptionRaiseStateIdentity(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) = 0;
|
||||
|
||||
protected:
|
||||
~Interface() {}
|
||||
};
|
||||
|
||||
//! \brief Constructs an object of this class.
|
||||
//!
|
||||
//! \param[in] interface The interface to dispatch requests to. Weak.
|
||||
explicit ExcServer(Interface* interface);
|
||||
|
||||
// MachMessageServer::Interface:
|
||||
|
||||
bool MachMessageServerFunction(const mach_msg_header_t* in_header,
|
||||
mach_msg_header_t* out_header,
|
||||
bool* destroy_complex_request) override;
|
||||
|
||||
std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
|
||||
|
||||
mach_msg_size_t MachMessageServerRequestSize() override;
|
||||
mach_msg_size_t MachMessageServerReplySize() override;
|
||||
|
||||
private:
|
||||
Interface* interface_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExcServer);
|
||||
};
|
||||
|
||||
//! \brief A server interface for the `mach_exc` Mach subsystem.
|
||||
class MachExcServer : public MachMessageServer::Interface {
|
||||
public:
|
||||
//! \brief An interface that the different request messages that are a part of
|
||||
//! the `mach_exc` Mach subsystem can be dispatched to.
|
||||
class Interface {
|
||||
public:
|
||||
//! \brief Handles exceptions raised by `mach_exception_raise()`.
|
||||
//!
|
||||
//! This behaves equivalently to a `catch_mach_exception_raise()` function
|
||||
//! used with `mach_exc_server()`.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
//! \param[out] destroy_request `true` if the request message is to be
|
||||
//! destroyed even when this method returns success. See
|
||||
//! MachMessageServer::Interface.
|
||||
virtual kern_return_t CatchMachExceptionRaise(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) = 0;
|
||||
|
||||
//! \brief Handles exceptions raised by `mach_exception_raise_state()`.
|
||||
//!
|
||||
//! This behaves equivalently to a `catch_mach_exception_raise_state()`
|
||||
//! function used with `mach_exc_server()`.
|
||||
//!
|
||||
//! There is no \a destroy_request parameter because, unlike
|
||||
//! CatchMachExceptionRaise() and CatchMachExceptionRaiseStateIdentity(),
|
||||
//! the request message is not complex (it does not carry the \a thread or
|
||||
//! \a task port rights) and thus there is nothing to destroy.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
virtual kern_return_t CatchMachExceptionRaiseState(
|
||||
exception_handler_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer) = 0;
|
||||
|
||||
//! \brief Handles exceptions raised by
|
||||
//! `mach_exception_raise_state_identity()`.
|
||||
//!
|
||||
//! This behaves equivalently to a
|
||||
//! `catch_mach_exception_raise_state_identity()` function used with
|
||||
//! `mach_exc_server()`.
|
||||
//!
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
//! \param[out] destroy_request `true` if the request message is to be
|
||||
//! destroyed even when this method returns success. See
|
||||
//! MachMessageServer::Interface.
|
||||
virtual kern_return_t CatchMachExceptionRaiseStateIdentity(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) = 0;
|
||||
|
||||
protected:
|
||||
~Interface() {}
|
||||
};
|
||||
|
||||
//! \brief Constructs an object of this class.
|
||||
//!
|
||||
//! \param[in] interface The interface to dispatch requests to. Weak.
|
||||
explicit MachExcServer(Interface* interface);
|
||||
|
||||
// MachMessageServer::Interface:
|
||||
|
||||
bool MachMessageServerFunction(const mach_msg_header_t* in_header,
|
||||
mach_msg_header_t* out_header,
|
||||
bool* destroy_complex_request) override;
|
||||
|
||||
std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
|
||||
|
||||
mach_msg_size_t MachMessageServerRequestSize() override;
|
||||
mach_msg_size_t MachMessageServerReplySize() override;
|
||||
|
||||
private:
|
||||
Interface* interface_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MachExcServer);
|
||||
};
|
||||
|
||||
//! \brief A server interface for the `exc` Mach subsystem, simplified to have
|
||||
//! only a single interface method needing implementation.
|
||||
class SimplifiedExcServer : public ExcServer, public ExcServer::Interface {
|
||||
public:
|
||||
//! \brief An interface that the different request messages that are a part of
|
||||
//! the `exc` Mach subsystem can be dispatched to.
|
||||
class Interface {
|
||||
public:
|
||||
//! \brief Handles exceptions raised by `exception_raise()`,
|
||||
//! `exception_raise_state()`, and `exception_raise_state_identity()`.
|
||||
//!
|
||||
//! For convenience in implementation, these different “behaviors” of
|
||||
//! exception messages are all mapped to a single interface method. The
|
||||
//! exception’s original “behavior” is specified in the \a behavior
|
||||
//! parameter. Only parameters that were supplied in the request message
|
||||
//! are populated, other parameters are set to reasonable default values.
|
||||
//!
|
||||
//! The meanings of most parameters are identical to that of
|
||||
//! ExcServer::Interface::CatchExceptionRaiseStateIdentity().
|
||||
//!
|
||||
//! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
|
||||
//! `EXCEPTION_STATE_IDENTITY`, identifying which exception request
|
||||
//! message was processed and thus which other parameters are valid.
|
||||
virtual kern_return_t CatchException(
|
||||
exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) = 0;
|
||||
|
||||
protected:
|
||||
~Interface() {}
|
||||
};
|
||||
|
||||
//! \brief Constructs an object of this class.
|
||||
//!
|
||||
//! \param[in] interface The interface to dispatch requests to. Weak.
|
||||
explicit SimplifiedExcServer(Interface* interface);
|
||||
|
||||
// ExcServer::Interface:
|
||||
|
||||
kern_return_t CatchExceptionRaise(exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) override;
|
||||
kern_return_t CatchExceptionRaiseState(
|
||||
exception_handler_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer) override;
|
||||
kern_return_t CatchExceptionRaiseStateIdentity(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) override;
|
||||
|
||||
private:
|
||||
Interface* interface_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SimplifiedExcServer);
|
||||
};
|
||||
|
||||
//! \brief A server interface for the `mach_exc` Mach subsystem, simplified to
|
||||
//! have only a single interface method needing implementation.
|
||||
class SimplifiedMachExcServer : public MachExcServer,
|
||||
public MachExcServer::Interface {
|
||||
public:
|
||||
//! \brief An interface that the different request messages that are a part of
|
||||
//! the `mach_exc` Mach subsystem can be dispatched to.
|
||||
class Interface {
|
||||
public:
|
||||
//! \brief Handles exceptions raised by `mach_exception_raise()`,
|
||||
//! `mach_exception_raise_state()`, and
|
||||
//! `mach_exception_raise_state_identity()`.
|
||||
//!
|
||||
//! For convenience in implementation, these different “behaviors” of
|
||||
//! exception messages are all mapped to a single interface method. The
|
||||
//! exception’s original “behavior” is specified in the \a behavior
|
||||
//! parameter. Only parameters that were supplied in the request message
|
||||
//! are populated, other parameters are set to reasonable default values.
|
||||
//!
|
||||
//! The meanings of most parameters are identical to that of
|
||||
//! MachExcServer::Interface::CatchMachExceptionRaiseStateIdentity().
|
||||
//!
|
||||
//! \param[in] behavior `MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT`,
|
||||
//! `MACH_EXCEPTION_CODES | EXCEPTION_STATE`, or
|
||||
//! `MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY`, identifying which
|
||||
//! exception request message was processed and thus which other
|
||||
//! parameters are valid.
|
||||
virtual kern_return_t CatchMachException(
|
||||
exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) = 0;
|
||||
|
||||
protected:
|
||||
~Interface() {}
|
||||
};
|
||||
|
||||
//! \brief Constructs an object of this class.
|
||||
//!
|
||||
//! \param[in] interface The interface to dispatch requests to. Weak.
|
||||
explicit SimplifiedMachExcServer(Interface* interface);
|
||||
|
||||
// MachExcServer::Interface:
|
||||
|
||||
kern_return_t CatchMachExceptionRaise(exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) override;
|
||||
kern_return_t CatchMachExceptionRaiseState(
|
||||
exception_handler_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer) override;
|
||||
kern_return_t CatchMachExceptionRaiseStateIdentity(
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_request) override;
|
||||
|
||||
private:
|
||||
Interface* interface_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SimplifiedMachExcServer);
|
||||
};
|
||||
|
||||
class UniversalMachExcServerImpl;
|
||||
} // namespace internal
|
||||
|
||||
//! \brief A server interface for the `exc` and `mach_exc` Mach subsystems,
|
||||
@ -423,18 +34,16 @@ class SimplifiedMachExcServer : public MachExcServer,
|
||||
//! simplified to have only a single interface method needing
|
||||
//! implementation.
|
||||
//!
|
||||
//! The `<mach/exc.defs>` and `<mach/mach_exc.defs>` interfaces are identical,
|
||||
//! except that the latter allows for 64-bit exception codes, and is requested
|
||||
//! by setting the MACH_EXCEPTION_CODES behavior bit associated with an
|
||||
//! exception port.
|
||||
//!
|
||||
//! UniversalMachExcServer operates by translating messages received in the
|
||||
//! `exc` subsystem to a variant that is compatible with the `mach_exc`
|
||||
//! subsystem. This involves changing the format of \a code, the exception code
|
||||
//! field, from `exception_data_type_t` to `mach_exception_data_type_t`.
|
||||
//! This is achieved by implementing SimplifiedExcServer::Interface and having
|
||||
//! it forward translated messages to SimplifiedMachExcServer::Interface, which
|
||||
//! is left unimplemented here so that users of this class can provide their own
|
||||
//! implementations.
|
||||
class UniversalMachExcServer
|
||||
: public MachMessageServer::Interface,
|
||||
public internal::SimplifiedExcServer::Interface,
|
||||
public internal::SimplifiedMachExcServer::Interface {
|
||||
class UniversalMachExcServer final : public MachMessageServer::Interface {
|
||||
public:
|
||||
//! \brief An interface that the different request messages that are a part of
|
||||
//! the `exc` and `mach_exc` Mach subsystems can be dispatched to.
|
||||
@ -451,13 +60,20 @@ class UniversalMachExcServer
|
||||
//! parameter. Only parameters that were supplied in the request message
|
||||
//! are populated, other parameters are set to reasonable default values.
|
||||
//!
|
||||
//! The meanings of most parameters are identical to that of
|
||||
//! MachExcServer::Interface::CatchMachExceptionRaiseStateIdentity().
|
||||
//! This behaves equivalently to a `catch_exception_raise_state_identity()`
|
||||
//! function used with `exc_server()`, or a
|
||||
//! `catch_mach_exception_raise_state_identity()` function used with
|
||||
//! `mach_exc_server()`. The meanings of most parameters are identical to
|
||||
//! their meanings to these functions.
|
||||
//!
|
||||
//! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`,
|
||||
//! or `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES`
|
||||
//! ORed in. This identifies which exception request message was
|
||||
//! processed and thus which other parameters are valid.
|
||||
//! \param[in] trailer The trailer received with the request message.
|
||||
//! \param[out] destroy_request `true` if the request message is to be
|
||||
//! destroyed even when this method returns success. See
|
||||
//! MachMessageServer::Interface.
|
||||
virtual kern_return_t CatchMachException(
|
||||
exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
@ -483,6 +99,8 @@ class UniversalMachExcServer
|
||||
//! \param[in] interface The interface to dispatch requests to. Weak.
|
||||
explicit UniversalMachExcServer(Interface* interface);
|
||||
|
||||
~UniversalMachExcServer();
|
||||
|
||||
// MachMessageServer::Interface:
|
||||
|
||||
bool MachMessageServerFunction(const mach_msg_header_t* in_header,
|
||||
@ -494,44 +112,8 @@ class UniversalMachExcServer
|
||||
mach_msg_size_t MachMessageServerRequestSize() override;
|
||||
mach_msg_size_t MachMessageServerReplySize() override;
|
||||
|
||||
// internal::SimplifiedExcServer::Interface:
|
||||
|
||||
kern_return_t CatchException(exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) override;
|
||||
|
||||
// internal::SimplifiedMachExcServer::Interface:
|
||||
|
||||
kern_return_t CatchMachException(exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) override;
|
||||
|
||||
private:
|
||||
internal::SimplifiedExcServer exc_server_;
|
||||
internal::SimplifiedMachExcServer mach_exc_server_;
|
||||
Interface* interface_; // weak
|
||||
scoped_ptr<internal::UniversalMachExcServerImpl> impl_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServer);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user