make_virtuals.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. proto = """
  2. #define GDVIRTUAL$VER($RET m_name $ARG) \\
  3. StringName _gdvirtual_##m_name##_sn = #m_name;\\
  4. mutable bool _gdvirtual_##m_name##_initialized = false;\\
  5. mutable GDExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\
  6. template<bool required>\\
  7. _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
  8. ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
  9. if (_script_instance) {\\
  10. Callable::CallError ce; \\
  11. $CALLSIARGS\\
  12. $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
  13. if (ce.error == Callable::CallError::CALL_OK) {\\
  14. $CALLSIRET\\
  15. return true;\\
  16. } \\
  17. }\\
  18. if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
  19. /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
  20. _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
  21. _gdvirtual_##m_name##_initialized = true;\\
  22. }\\
  23. if (_gdvirtual_##m_name) {\\
  24. $CALLPTRARGS\\
  25. $CALLPTRRETDEF\\
  26. _gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
  27. $CALLPTRRET\\
  28. return true;\\
  29. }\\
  30. \\
  31. if (required) {\\
  32. ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
  33. $RVOID\\
  34. }\\
  35. \\
  36. return false;\\
  37. }\\
  38. _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
  39. ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
  40. if (_script_instance) {\\
  41. return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
  42. }\\
  43. if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
  44. /* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
  45. _gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
  46. _gdvirtual_##m_name##_initialized = true;\\
  47. }\\
  48. if (_gdvirtual_##m_name) {\\
  49. return true;\\
  50. }\\
  51. return false;\\
  52. }\\
  53. \\
  54. _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
  55. MethodInfo method_info;\\
  56. method_info.name = #m_name;\\
  57. method_info.flags = METHOD_FLAG_VIRTUAL;\\
  58. $FILL_METHOD_INFO\\
  59. return method_info;\\
  60. }
  61. """
  62. def generate_version(argcount, const=False, returns=False):
  63. s = proto
  64. sproto = str(argcount)
  65. method_info = ""
  66. if returns:
  67. sproto += "R"
  68. s = s.replace("$RET", "m_ret, ")
  69. s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors
  70. s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;")
  71. method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
  72. method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n"
  73. else:
  74. s = s.replace("$RET", "")
  75. s = s.replace("$RVOID", "")
  76. s = s.replace("$CALLPTRRETDEF", "")
  77. if const:
  78. sproto += "C"
  79. s = s.replace("$CONST", "const")
  80. method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n"
  81. else:
  82. s = s.replace("$CONST", "")
  83. s = s.replace("$VER", sproto)
  84. argtext = ""
  85. callargtext = ""
  86. callsiargs = ""
  87. callsiargptrs = ""
  88. callptrargsptr = ""
  89. if argcount > 0:
  90. argtext += ", "
  91. callsiargs = "Variant vargs[" + str(argcount) + "]={"
  92. callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={"
  93. callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={"
  94. callptrargs = ""
  95. for i in range(argcount):
  96. if i > 0:
  97. argtext += ", "
  98. callargtext += ", "
  99. callsiargs += ", "
  100. callsiargptrs += ", "
  101. callptrargs += "\t\t"
  102. callptrargsptr += ", "
  103. argtext += "m_type" + str(i + 1)
  104. callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1)
  105. callsiargs += "Variant(arg" + str(i + 1) + ")"
  106. callsiargptrs += "&vargs[" + str(i) + "]"
  107. callptrargs += (
  108. "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n"
  109. )
  110. callptrargsptr += "&argval" + str(i + 1)
  111. method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n"
  112. method_info += (
  113. "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n"
  114. )
  115. if argcount:
  116. callsiargs += "};\\\n"
  117. callsiargptrs += "};\\\n"
  118. s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs)
  119. s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount))
  120. callptrargsptr += "};\\\n"
  121. s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr)
  122. s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)")
  123. else:
  124. s = s.replace("$CALLSIARGS", "")
  125. s = s.replace("$CALLSIARGPASS", "nullptr, 0")
  126. s = s.replace("$CALLPTRARGS", "")
  127. s = s.replace("$CALLPTRARGPASS", "nullptr")
  128. if returns:
  129. if argcount > 0:
  130. callargtext += ","
  131. callargtext += " m_ret& r_ret"
  132. s = s.replace("$CALLSIBEGIN", "Variant ret = ")
  133. s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);")
  134. s = s.replace("$CALLPTRRETPASS", "&ret")
  135. s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;")
  136. else:
  137. s = s.replace("$CALLSIBEGIN", "")
  138. s = s.replace("$CALLSIRET", "")
  139. s = s.replace("$CALLPTRRETPASS", "nullptr")
  140. s = s.replace("$CALLPTRRET", "")
  141. s = s.replace("$ARG", argtext)
  142. s = s.replace("$CALLARGS", callargtext)
  143. s = s.replace("$FILL_METHOD_INFO", method_info)
  144. return s
  145. def run(target, source, env):
  146. max_versions = 12
  147. txt = """
  148. #ifndef GDVIRTUAL_GEN_H
  149. #define GDVIRTUAL_GEN_H
  150. """
  151. for i in range(max_versions + 1):
  152. txt += "/* " + str(i) + " Arguments */\n\n"
  153. txt += generate_version(i, False, False)
  154. txt += generate_version(i, False, True)
  155. txt += generate_version(i, True, False)
  156. txt += generate_version(i, True, True)
  157. txt += "#endif"
  158. with open(target[0], "w") as f:
  159. f.write(txt)
  160. if __name__ == "__main__":
  161. from platform_methods import subprocess_main
  162. subprocess_main(globals())