Optimize TypeGen

This commit is contained in:
camila314 2022-10-21 18:17:08 -05:00
parent cb9d6f212a
commit c3878b8a20
9 changed files with 313 additions and 208 deletions

View file

@ -39,14 +39,23 @@ target_sources(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/entry.cpp)
add_subdirectory(codegen)
add_custom_target(CodegenRun ALL
message(STATUS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
add_custom_command(
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings/GeometryDash.bro
${CMAKE_CURRENT_SOURCE_DIR}/bindings/Cocos2d.bro
${CMAKE_CURRENT_SOURCE_DIR}/bindings/Entry.bro
COMMAND Codegen ${GEODE_TARGET_PLATFORM} bindings ${GEODE_CODEGEN_PATH}
COMMAND echo codegen > ${GEODE_CODEGEN_PATH}/.stamp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Run Codegen"
BYPRODUCTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp
OUTPUT ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/.stamp
)
add_custom_target(CodegenRun
DEPENDS ${GEODE_CODEGEN_PATH}/.stamp
)
add_dependencies(${PROJECT_NAME} CodegenRun)
add_dependencies(CodegenRun Codegen)
# Hacky way to supress the not generated error
if (NOT EXISTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)

View file

@ -17,6 +17,7 @@ class AchievementManager : cocos2d::CCNode {
PAD = win 0x4;
}
class AchievementNotifier : cocos2d::CCNode {
void notifyAchievement(const char* title, const char* desc, const char* icon, bool quest) {
m_queue->addObject(AchievementBar::create(title, desc, icon, quest));

View file

@ -32,9 +32,9 @@ int main(int argc, char** argv) try {
}
writeFile(writeDir / "GeneratedAddress.hpp", generateAddressHeader(root));
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify")); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root)); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root)); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify"));
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root));
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root));
writeFile(writeDir / "GeneratedBinding.hpp", generateBindingHeader(root, writeDir / "binding"));
writeFile(writeDir / "GeneratedPredeclare.hpp", generatePredeclareHeader(root));
writeFile(writeDir / "GeneratedSource.cpp", generateBindingSource(root));

View file

@ -1,4 +1,5 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
#include <iostream>
#include <set>
@ -36,7 +37,7 @@ namespace geode::modifier {{
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
char const* apply_function = R"GEN(
GEODE_APPLY_MODIFY_FOR_FUNCTION({index}, {function_convention}, {class_name}, {function_name}))GEN";
GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {pure_index}, {function_convention}, {class_name}, {function_name}))GEN";
char const* modify_end = R"GEN(
}
@ -52,6 +53,9 @@ namespace geode::modifier {{
std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder) {
std::string output;
TypeBank bank;
bank.loadFrom(root);
for (auto c : root.classes) {
if (c.name == "cocos2d")
continue;
@ -104,7 +108,8 @@ std::string generateModifyHeader(Root& root, ghc::filesystem::path const& single
}
single_output += fmt::format(format_strings::apply_function,
fmt::arg("index", f.field_id),
fmt::arg("addr_index", f.field_id),
fmt::arg("pure_index", bank.getPure(*begin, c.name)),
fmt::arg("class_name", c.name),
fmt::arg("function_name", function_name),
fmt::arg("function_convention", codegen::getConvention(f))

View file

@ -175,113 +175,4 @@ namespace codegen {
if (index == std::string::npos) return s;
return s.substr(index + 2);
}
/*
// defined : we have a symbol that is usable
inline bool isFunctionDefined(Function const& f) {
// basically this is true for
// fmod for all platforms
// cocos for windows
// all funcs without stl parameter for android
// destructors for no platforms
if (f.function_type == kDestructor) return false;
if (f.function_type == kConstructor) return false;
if (f.parent_class->name.rfind("fmod::", 0) == 0) return true;
if (f.parent_class->name.rfind("cocos2d::", 0) == 0 && codegen::platform == kWindows) return true;
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return false;
if (codegen::platform == kAndroid) return true;
return false;
}
// definable : we can define it and hook it
inline bool isFunctionDefinable(Function const& f) {
// basically this is true for
// all funcs that we have the offset for
// all funcs with stl parameter for android
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return true;
if (getBind(f) != "") return true;
return false;
}
inline std::string getUnqualifiedClassName(Function const& f) {
auto index = f.parent_class->name.rfind("::");
if (index == std::string::npos) return f.parent_class->name;
return f.parent_class->name.substr(index + 2);
}
inline std::string getFunctionName(Function const& f) {
return f.name;
}
inline std::string getConst(Function const& f) {
return f.is_const ? "const" : "";
}
inline std::string getConstWhitespace(Function const& f) {
return f.is_const ? " " : "";
}
inline std::string getParameterTypes(Function const& f) { //int, float
return fmt::format("{}", fmt::join(f.args, ", "));
}
inline std::string getArguments(Function const& f) { // p0, p1
return fmt::format("{}", fmt::join(f.argnames, ", "));
}
inline std::string getParameterComma(Function const& f) { // int p0, float p1
return f.args.size() > 0 ? ", " : "";
}
inline std::string getParameterTypeComma(Function const& f) { //int, float
return f.args.size() > 0 ? ", " : "";
}
inline std::string getArgumentComma(Function const& f) { // p0, p1
return f.argnames.size() > 0 ? ", " : "";
}
// defined : we have a symbol that is usable
inline bool isFunctionDefined(Function const& f) {
// basically this is true for
// fmod for all platforms
// cocos for windows
// all funcs without stl parameter for android
// destructors for no platforms
if (f.function_type == kDestructor) return false;
if (f.function_type == kConstructor) return false;
if (f.parent_class->name.rfind("fmod::", 0) == 0) return true;
if (f.parent_class->name.rfind("cocos2d::", 0) == 0 && codegen::platform == kWindows) return true;
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return false;
if (codegen::platform == kAndroid) return true;
return false;
}
// definable : we can define it and hook it
inline bool isFunctionDefinable(Function const& f) {
// basically this is true for
// all funcs that we have the offset for
// all funcs with stl parameter for android
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return true;
if (getBind(f) != "") return true;
return false;
}
inline std::string getConvention(Function const& f) {
if (codegen::platform != kWindows) return "DefaultConv";
switch (f.function_type) {
case kConstructor: [[fallthrough]];
case kDestructor: [[fallthrough]];
case kRegularFunction:
if (isFunctionDefined(f)) return "x86::Thiscall";
return "x86::Membercall";
case kVirtualFunction:
return "x86::Thiscall";
case kStaticFunction:
if (isFunctionDefined(f)) return "x86::Cdecl";
return "x86::Optcall";
}
return "x86::Membercall";
}*/
}

View file

@ -1,4 +1,5 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
namespace { namespace format_strings {
char const* source_start = R"CAC(
@ -16,23 +17,23 @@ using namespace geode::modifier; // types
)CAC";
char const* declare_member = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(this{parameter_comma}{arguments});
}}
)GEN";
char const* declare_virtual = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto self = addresser::thunkAdjust((types::member{index})(&{class_name}::{function_name}), this);
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto self = addresser::thunkAdjust((types::member{member_index})(&{class_name}::{function_name}), this);
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(self{parameter_comma}{arguments});
}}
)GEN";
char const* declare_static = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func({arguments});
}}
)GEN";
@ -42,7 +43,7 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
// basically we destruct it once by calling the gd function,
// then lock it, so that other gd destructors dont get called
if (CCDestructor::lock(this)) return;
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
// we need to construct it back so that it uhhh ummm doesnt crash
// while going to the child destructors
@ -58,7 +59,7 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
// no crashes :pray:
CCDestructor::lock(this) = true;
{class_name}::~{unqualified_class_name}();
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
}}
)GEN";
@ -75,6 +76,9 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
std::string generateBindingSource(Root& root) {
std::string output(format_strings::source_start);
TypeBank bank;
bank.loadFrom(root);
for (auto& c : root.classes) {
for (auto& f : c.fields) {
@ -100,7 +104,6 @@ std::string generateBindingSource(Root& root) {
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("class_name", c.name),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("index", f.field_id),
fmt::arg("definition", fn->inner)
);
break;
@ -110,7 +113,6 @@ std::string generateBindingSource(Root& root) {
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("class_name", c.name),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("index", f.field_id),
fmt::arg("definition", fn->inner),
fmt::arg("return", fn->beginning.ret.name)
);
@ -140,13 +142,18 @@ std::string generateBindingSource(Root& root) {
if (fn->beginning.is_virtual && fn->beginning.type != FunctionType::Dtor)
used_declare_format = format_strings::declare_virtual;
auto ids = bank.getIDs(fn->beginning, c.name);
output += fmt::format(used_declare_format,
fmt::arg("class_name", c.name),
fmt::arg("unqualified_class_name", codegen::getUnqualifiedClassName(c.name)),
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("convention", codegen::getConvention(f)),
fmt::arg("function_name", fn->beginning.name),
fmt::arg("index", f.field_id),
fmt::arg("meta_index", ids.meta),
fmt::arg("member_index", ids.member),
fmt::arg("ret_index", ids.ret),
fmt::arg("addr_index", f.field_id),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("parameter_types", codegen::getParameterTypes(fn->beginning)),
fmt::arg("arguments", codegen::getParameterNames(fn->beginning)),

View file

@ -1,84 +1,95 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
#include <set>
namespace { namespace format_strings {
char const* declare_member_type = R"GEN(
using ret{index} = {return};
using func{index} = ret{index}(*)({const}{class_name}*{parameter_type_comma}{parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({const}{class_name}*{parameter_type_comma}{parameter_types});
using member{index} = ret{index}({class_name}::*)({parameter_types}){const};
)GEN";
char const* declare_static_type = R"GEN(
using ret{index} = {return};
using func{index} = ret{index}(*)({parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({parameter_types});
using member{index} = func{index};
)GEN";
char const* declare_structor_type = R"GEN(
using ret{index} = void;
using func{index} = ret{index}(*)({class_name}*{parameter_type_comma}{parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({const}{class_name}*{parameter_type_comma}{parameter_types});
using member{index} = func{index};
)GEN";
}}
static std::string getReturn(FunctionBegin const& fn, std::string const& parent) {
if (fn.type != FunctionType::Normal)
return "void";
if (fn.ret.name == "auto") {
std::vector<std::string> declvals;
for (auto& [t, n] : fn.args) {
declvals.push_back(fmt::format("std::declval<{}>()", t.name));
}
return fmt::format(
fn.is_static ? "decltype({}::{}({}))" : "decltype(std::declval<{}>().{}({}))",
parent,
fn.name,
fmt::join(declvals, ", ")
);
}
return fn.ret.name;
}
std::string generateTypeHeader(Root& root) {
std::string output;
for (auto& c : root.classes) {
for (auto& f : c.fields) {
if (codegen::getStatus(f) == BindStatus::Unbindable)
continue;
auto fn = f.get_fn();
TypeBank bank;
bank.loadFrom(root);
char const* used_format = format_strings::declare_member_type;
std::map<std::string, int> used_returns;
std::map<std::string, int> used_funcs;
std::map<std::string, int> used_pures;
if (fn->type != FunctionType::Normal) {
used_format = format_strings::declare_structor_type;
}
int i = 0;
for (auto& f : bank.typeList()) {
if (fn->is_static) {
used_format = format_strings::declare_static_type;
}
char const* return_fmt = "using ret{index} = {return};";
char const* func_fmt;
char const* pure_fmt = "ret{ret_index}({parameter_types});";
char const* meta_fmt;
char const* member_fmt;
output += fmt::format(used_format,
fmt::arg("parameter_types", codegen::getParameterTypes(*fn)),
fmt::arg("parameter_type_comma", str_if(", ", !fn->args.empty())),
fmt::arg("class_name", c.name),
fmt::arg("const", str_if(" const ", fn->is_const)),
fmt::arg("index", f.field_id),
fmt::arg("return", getReturn(*fn, c.name))
);
switch (f.type) {
case FuncType::Member:
func_fmt = "ret{ret_index}(*)( {const}{class_name}*{parameter_type_comma}{parameter_types});";
meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});";
member_fmt = "ret{ret_index}({class_name}::*)({parameter_types}){const};";
break;
case FuncType::Static:
func_fmt = "ret{ret_index}(*)({parameter_types});";
meta_fmt = "ret{ret_index}({parameter_types});";
member_fmt = "func{index};";
break;
case FuncType::Structor:
func_fmt = "ret{ret_index}(*)({class_name}*{parameter_type_comma}{parameter_types});";
meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});";
member_fmt = "func{index};";
break;
}
if (used_returns.count(f.return_type) == 0) {
output += fmt::format(return_fmt,
fmt::arg("index", i),
fmt::arg("return", f.return_type)
) + "\n";
used_returns[f.return_type] = i;
}
int ret_index = used_returns[f.return_type];
std::string pure_val = fmt::format(pure_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", "))
);
if (used_pures.count(pure_val) == 0) {
output += fmt::format("using pure{} = {}\n", i, pure_val);
used_pures[pure_val] = i;
}
std::string func_val = fmt::format(func_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const))
);
std::string meta_val = fmt::format(meta_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const))
);
std::string member_val = fmt::format(member_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const)),
fmt::arg("index", i)
);
if (used_funcs.count(func_val) == 0) {
output += fmt::format("using func{index} = {func}\nusing meta{index} = {meta}\nusing member{index} = {member}\n",
fmt::arg("index", i),
fmt::arg("func", func_val),
fmt::arg("meta", meta_val),
fmt::arg("member", member_val)
);
used_funcs[func_val] = i;
}
++i;
}
return output;

180
codegen/src/TypeOpt.hpp Normal file
View file

@ -0,0 +1,180 @@
#include "Shared.hpp"
#include <set>
#include <algorithm>
namespace {
enum class FuncType : int {
Structor,
Static,
Member
};
struct Func {
std::string return_type;
bool is_const;
std::string class_name;
std::vector<std::string> parameter_types;
FuncType type;
std::string toStr() const {
return fmt::format("{}{}{}{}{}",
return_type,
class_name,
fmt::join(parameter_types, ","),
is_const ? " const " : "",
static_cast<int>(type)
);
}
bool operator<(Func const& f) const {
return toStr() < f.toStr();
}
bool operator==(Func const& f) const {
return (
return_type == f.return_type &&
is_const == f.is_const &&
class_name == f.class_name &&
parameter_types == f.parameter_types &&
type == f.type
);
}
};
struct Ids {
int ret = -1;
int func = -1;
int pure = -1;
int meta = -1;
int member = -1;
};
class TypeBank {
std::vector<Func> m_stuff;
public:
static std::string getReturn(FunctionBegin const& fn, std::string const& parent) {
if (fn.type != FunctionType::Normal)
return "void";
if (fn.ret.name == "auto") {
std::vector<std::string> declvals;
for (auto& [t, n] : fn.args) {
declvals.push_back(fmt::format("std::declval<{}>()", t.name));
}
return fmt::format(
fn.is_static ? "decltype({}::{}({}))" : "decltype(std::declval<{}>().{}({}))",
parent,
fn.name,
fmt::join(declvals, ", ")
);
}
return fn.ret.name;
}
static Func makeFunc(FunctionBegin const& fn, std::string const& parent) {
Func f;
f.return_type = TypeBank::getReturn(fn, parent);
f.is_const = fn.is_const;
f.class_name = parent;
for (auto& arg : fn.args) {
f.parameter_types.push_back(arg.first.name);
}
if (fn.is_static)
f.type = FuncType::Static;
else if (fn.type != FunctionType::Normal)
f.type = FuncType::Structor;
else
f.type = FuncType::Member;
return f;
}
void loadFrom(Root& root) {
for (auto& c : root.classes) {
for (auto& f : c.fields) {
if (codegen::getStatus(f) == BindStatus::Unbindable)
continue;
m_stuff.push_back(TypeBank::makeFunc(*f.get_fn(), c.name));
}
}
std::sort(m_stuff.begin(), m_stuff.end());
m_stuff.erase(std::unique(m_stuff.begin(), m_stuff.end()), m_stuff.end());
}
std::vector<Func> const& typeList() { return m_stuff; }
Ids getIDs(FunctionBegin const& fn, std::string const& parent) {
Ids out;
Func in_f = TypeBank::makeFunc(fn, parent);
int i = 0;
for (auto f : m_stuff) {
if (out.ret == -1 && f.return_type == in_f.return_type) {
out.ret = i;
}
if (out.func == -1) {
if (in_f.type == FuncType::Member) {
if (f == in_f) out.func = i;
} else if (in_f.type == FuncType::Structor) {
if (
f.return_type == in_f.return_type &&
f.class_name == in_f.class_name &&
f.is_const == in_f.is_const &&
f.parameter_types == in_f.parameter_types
) out.func = i;
} else {
if (
f.return_type == in_f.return_type &&
f.parameter_types == in_f.parameter_types &&
f.type == in_f.type
) out.func = i;
}
}
if (out.meta == -1 || out.member == -1) {
Func assume_member = f;
assume_member.type = FuncType::Member;
if (in_f == assume_member) {
out.meta = i;
} else if (out.func != -1) {
out.meta = out.func;
}
}
if (out.pure == -1 && f.return_type == in_f.return_type && f.parameter_types == in_f.parameter_types) {
out.pure = i;
}
if (out.ret != -1 && out.func != -1 && out.pure != -1)
break;
++i;
}
out.member = out.func;
return out;
}
int getPure(FunctionBegin const& fn, std::string const& parent) {
Func in_f = TypeBank::makeFunc(fn, parent);
int i = 0;
for (auto f : m_stuff) {
if (f.return_type == in_f.return_type && f.parameter_types == in_f.parameter_types) {
return i;
}
++i;
}
return -1;
}
};
}

View file

@ -7,15 +7,16 @@
#include <Geode/loader/Mod.hpp>
#include <iostream>
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(index, convention, className, functionName) \
using base##index = wrap::functionName<Base, types::pure##index>; \
using derived##index = wrap::functionName<Derived, types::pure##index>; \
if constexpr (derived##index::uuid != nullptr && (void*)base##index::uuid != (void*)derived##index::uuid) { \
Mod::get()->addHook<derived##index::value, convention>( \
#className "::" #functionName, \
(void*)addresses::address##index() \
); \
} \
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(addr_index, pure_index, convention, className, functionName) \
if constexpr ( \
wrap::functionName<Derived, types::pure##pure_index>::uuid != nullptr && \
(void*)wrap::functionName<Base, types::pure##pure_index>::uuid != (void*)wrap::functionName<Derived, types::pure##pure_index>::uuid \
) { \
Mod::get()->addHook<wrap::functionName<Derived, types::pure##pure_index>::value, convention>( \
#className "::" #functionName, \
(void*)addresses::address##addr_index() \
); \
} \
namespace geode::modifier {