Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transient and opaque instances #79

Merged
merged 8 commits into from
Aug 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions include/phoenix/script.hh
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,17 @@ namespace phoenix {
/// \brief A pointer which may be used by the user of this API
void* user_ptr = nullptr;

protected:
PHOENIX_INTERNAL virtual std::uint8_t* data() {
return reinterpret_cast<std::uint8_t*>(this);
}

PHOENIX_INTERNAL virtual const std::uint8_t* data() const {
return reinterpret_cast<const std::uint8_t*>(this);
}

private:
friend class transient_instance;
friend class symbol;
friend class script;
friend class vm;
Expand All @@ -226,6 +236,54 @@ namespace phoenix {
const std::type_info* _m_type {nullptr};
};

/// \brief Represents an object associated with an instance in the script.
///
/// Instances allocated with init_opaque will be backed up by this class with plain memory storage
class opaque_instance final : public instance {
public:
PHOENIX_INTERNAL opaque_instance(symbol const& sym, std::vector<symbol*> const& members);
PHOENIX_INTERNAL ~opaque_instance();

protected:
friend class symbol;

PHOENIX_INTERNAL std::uint8_t* data() override {
return _m_storage.get();
}

PHOENIX_INTERNAL const std::uint8_t* data() const override {
return _m_storage.get();
}

private:
template <typename T, typename... Args>
PHOENIX_INTERNAL T* construct_at(size_t offset, Args&&... args);

std::unique_ptr<std::uint8_t[]> _m_storage;
std::vector<std::string*> _m_strings;
};

/// \brief Represents object instance in the script with no defined backing to memory.
///
/// Expected to be used for DMA mods or to emulate variable-like access to engine-functions.
class transient_instance : public instance {
public:
transient_instance();
~transient_instance();

protected:
friend class symbol;

virtual void set_int(symbol const& sym, uint16_t index, std::int32_t value) = 0;
virtual std::int32_t get_int(symbol const& sym, uint16_t index) = 0;

virtual void set_float(symbol const& sym, uint16_t index, float value) = 0;
virtual float get_float(symbol const& sym, uint16_t index) = 0;

virtual void set_string(symbol const& sym, uint16_t index, std::string_view value) = 0;
virtual const std::string& get_string(symbol const& sym, uint16_t index) = 0;
};

/// \brief The base class for all exceptions thrown by interacting with a script.
struct script_error : public error {
using error::error;
Expand Down Expand Up @@ -573,8 +631,9 @@ namespace phoenix {
if (*_m_registered_to != *context->_m_type)
throw illegal_context_type {this, *context->_m_type};

auto data_ptr = context->data();
std::uint32_t target_offset = offset_as_member() + index * sizeof(T);
return reinterpret_cast<const T*>(reinterpret_cast<const char*>(context.get()) + target_offset);
return reinterpret_cast<const T*>(data_ptr + target_offset);
}

template <typename T>
Expand All @@ -584,8 +643,9 @@ namespace phoenix {
if (*_m_registered_to != *context->_m_type)
throw illegal_context_type {this, *context->_m_type};

auto data_ptr = context->data();
std::uint32_t target_offset = offset_as_member() + index * sizeof(T);
return reinterpret_cast<T*>(reinterpret_cast<char*>(context.get()) + target_offset);
return reinterpret_cast<T*>(data_ptr + target_offset);
}

private:
Expand Down Expand Up @@ -792,6 +852,14 @@ namespace phoenix {
return find_symbol_by_index(inst->_m_symbol_index);
}

[[nodiscard]] PHOENIX_API std::vector<symbol*> find_class_members(symbol const& cls);

inline void register_as_opaque(std::string_view class_name) {
return register_as_opaque(find_symbol_by_name(class_name));
}

void register_as_opaque(symbol* sym);

protected:
PHOENIX_INTERNAL script() = default;

Expand Down
16 changes: 16 additions & 0 deletions include/phoenix/vm.hh
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ namespace phoenix {
return inst;
}

std::shared_ptr<instance> init_opaque_instance(symbol* sym);

/// \brief Initializes an instance with the given type into \p instance
/// \tparam _instance_t The type of the instance to initialize (ie. C_NPC).
/// \param instance The instance to initialize.
Expand Down Expand Up @@ -962,6 +964,20 @@ namespace phoenix {
}
}

[[nodiscard]] PHOENIX_API std::int32_t
get_int(std::shared_ptr<instance>& context,
std::variant<int32_t, float, symbol*, std::shared_ptr<instance>>& value,
uint16_t index);
[[nodiscard]] PHOENIX_API float
get_float(std::shared_ptr<instance>& context,
std::variant<int32_t, float, symbol*, std::shared_ptr<instance>>& value,
uint16_t index);

PHOENIX_API void set_int(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, std::int32_t value);
PHOENIX_API void set_float(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, float value);
PHOENIX_API void
set_string(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, std::string_view value);

private:
std::array<daedalus_stack_frame, stack_size> _m_stack;
uint16_t _m_stack_ptr {0};
Expand Down
151 changes: 151 additions & 0 deletions source/script.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,58 @@ namespace phoenix {
return syms;
}

std::vector<symbol*> script::find_class_members(const symbol& cls) {
std::vector<symbol*> members {};

for (auto& sym : _m_symbols) {
if (!sym.is_member() || sym.parent() != cls.index())
continue;
members.push_back(&sym);
}

return members;
}

void script::register_as_opaque(symbol* sym) {
auto members = find_class_members(*sym);

auto registered_to = &typeid(opaque_instance);
size_t class_size = 0;

for (auto* member : members) {
member->_m_registered_to = registered_to;

switch (member->type()) {
case datatype::void_:
case datatype::float_:
case datatype::integer:
case datatype::class_:
case datatype::function:
case datatype::prototype:
case datatype::instance:
member->_m_member_offset = class_size;
class_size += 4 * member->count();
break;
case datatype::string: {
auto align = alignof(std::string);
auto offset = class_size;

auto remain = offset % align;
auto offset_remain = remain == 0 ? 0 : align - remain;

class_size += offset_remain;
member->_m_member_offset = class_size;

class_size += sizeof(std::string) * member->count();
break;
}
}
}

sym->_m_registered_to = registered_to;
sym->_m_class_size = class_size;
}

symbol* script::add_temporary_strings_symbol() {
symbol sym {};
sym._m_name = "$PHOENIX_FAKE_STRINGS";
Expand Down Expand Up @@ -345,6 +397,11 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
return reinterpret_cast<transient_instance&>(*context).get_string(*this, index);
}

return *get_member_ptr<std::string>(index, context);
} else {
return std::get<std::unique_ptr<std::string[]>>(_m_value)[index];
Expand All @@ -363,6 +420,11 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
return reinterpret_cast<transient_instance&>(*context).get_float(*this, index);
}

return *get_member_ptr<float>(index, context);
} else {
return std::get<std::unique_ptr<float[]>>(_m_value)[index];
Expand All @@ -381,6 +443,11 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
return reinterpret_cast<transient_instance&>(*context).get_int(*this, index);
}

return *get_member_ptr<std::int32_t>(index, context);
} else {
return std::get<std::unique_ptr<std::int32_t[]>>(_m_value)[index];
Expand All @@ -399,6 +466,12 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
reinterpret_cast<transient_instance&>(*context).set_string(*this, index, value);
return;
}

*get_member_ptr<std::string>(index, context) = value;
} else {
std::get<std::unique_ptr<std::string[]>>(_m_value).get()[index] = value;
Expand All @@ -417,6 +490,12 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
reinterpret_cast<transient_instance&>(*context).set_float(*this, index, value);
return;
}

*get_member_ptr<float>(index, context) = value;
} else {
std::get<std::unique_ptr<float[]>>(_m_value)[index] = value;
Expand All @@ -435,6 +514,12 @@ namespace phoenix {
if (context == nullptr) {
throw no_context(this);
}

if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) {
reinterpret_cast<transient_instance&>(*context).set_int(*this, index, value);
return;
}

*get_member_ptr<std::int32_t>(index, context) = value;
} else {
std::get<std::unique_ptr<std::int32_t[]>>(_m_value)[index] = value;
Expand Down Expand Up @@ -463,4 +548,70 @@ namespace phoenix {
else
_m_flags &= ~symbol_flag::access_trap;
}

opaque_instance::opaque_instance(const symbol& sym, const std::vector<symbol*>& members) {
size_t str_count = 0;
for (auto* member : members) {
if (member->type() != datatype::string)
continue;
str_count += member->count();
}

_m_storage.reset(new uint8_t[sym.class_size()]());
_m_strings.resize(str_count, nullptr);

str_count = 0;
for (auto* member : members) {
unsigned offset = member->offset_as_member();

for (auto i = 0U; i < member->count(); ++i) {
switch (member->type()) {
case datatype::float_:
this->construct_at<float>(offset, 0);
offset += 4;
break;
case datatype::integer:
this->construct_at<int>(offset, 0);
offset += 4;
break;
case datatype::string:
_m_strings[str_count] = this->construct_at<std::string>(offset, "");
str_count++;
offset += sizeof(std::string);
break;
case datatype::function:
this->construct_at<int>(offset, 0);
offset += 4;
break;
case datatype::class_:
case datatype::prototype:
case datatype::instance:
case datatype::void_:
this->construct_at<int>(offset);
offset += 4;
break;
}
}
}
}

opaque_instance::~opaque_instance() {
for (auto& i : _m_strings)
i->std::string::~string();
}

template <typename T, typename... Args>
T* opaque_instance::construct_at(size_t offset, Args&&... args) {
auto align = alignof(T);
auto remain = offset % align;
auto real_offset = remain == 0 ? offset : offset + (align - remain);
return new (static_cast<void*>(&_m_storage[real_offset])) T(std::forward<Args>(args)...);
}

transient_instance::transient_instance() {
_m_type = &typeid(transient_instance);
}

transient_instance::~transient_instance() {}

} // namespace phoenix
Loading