vrclient: Introduce a new Method cursor wrapper.

CW-Bug-Id: #22729
This commit is contained in:
Rémi Bernon 2023-09-24 17:15:41 +02:00
parent 26eddba698
commit 600870e008

View file

@ -383,6 +383,40 @@ method_overrides_data = [
] ]
class Method:
def __init__(self, sdkver, abi, cursor, index, override):
self._sdkver = sdkver
self._abi = abi
self._cursor = cursor
self._index = index
self._override = override
self.result_type = cursor.result_type
self.spelling = cursor.spelling
@property
def name(self):
if self._override > 1: return f'{self.spelling}_{self._override}'
return self.spelling
def get_arguments(self):
return self._cursor.get_arguments()
def get_children(self):
return self._cursor.get_children()
class Destructor(Method):
def __init__(self, sdkver, abi, cursor, index, override):
super().__init__(sdkver, abi, cursor, index, override)
@property
def name(self):
if self._override > 1: return f'destructor_{self._override}'
return 'destructor'
class Class: class Class:
def __init__(self, sdkver, abi, cursor): def __init__(self, sdkver, abi, cursor):
self._sdkver = sdkver self._sdkver = sdkver
@ -394,6 +428,28 @@ class Class:
self.filename = SDK_CLASSES[self.spelling] self.filename = SDK_CLASSES[self.spelling]
self.version = all_versions[sdkver][self.spelling] self.version = all_versions[sdkver][self.spelling]
self._methods = None
@property
def methods(self):
if self._methods:
return self._methods
overrides = {}
is_method = lambda c: c.kind == CursorKind.CXX_METHOD and c.is_virtual_method()
in_vtable = lambda c: is_method(c) or c.kind == CursorKind.DESTRUCTOR
self._methods = []
for i, method in enumerate(filter(in_vtable, self._cursor.get_children())):
index, override = overrides.get(method.spelling, (i, 1))
overrides[method.spelling] = (index, override + 1)
if method.kind == CursorKind.DESTRUCTOR:
self._methods.append(Destructor(self._sdkver, self._abi, method, index, override))
else:
self._methods.append(Method(self._sdkver, self._abi, method, index, override))
return self._methods
def get_children(self): def get_children(self):
return self._cursor.get_children() return self._cursor.get_children()
@ -422,20 +478,6 @@ def get_path_converter(parent):
return None return None
def method_unique_name(method, existing_methods):
used_name = method.spelling
if used_name in existing_methods:
number = 2
while used_name in existing_methods:
idx = existing_methods.index(used_name)
used_name = f"{method.spelling}_{number}"
number = number + 1
existing_methods.insert(idx, used_name)
else:
existing_methods.append(used_name)
return used_name
def underlying_type(decl): def underlying_type(decl):
if type(decl) is Cursor: if type(decl) is Cursor:
decl = decl.type decl = decl.type
@ -485,16 +527,16 @@ def declspec(decl, name):
return f'{typename}{name}' return f'{typename}{name}'
def handle_method_hpp(method_name, cppname, method, cpp_h): def handle_method_hpp(method, cppname, cpp_h):
ret = f'{strip_ns(method.result_type.spelling)} ' ret = f'{strip_ns(method.result_type.spelling)} '
params = [declspec(p, "") for p in method.get_arguments()] params = [declspec(p, "") for p in method.get_arguments()]
params = ['void *'] + params params = ['void *'] + params
cpp_h.write(f'extern {ret}{cppname}_{method_name}({", ".join(params)});\n') cpp_h.write(f'extern {ret}{cppname}_{method.name}({", ".join(params)});\n')
def handle_method_cpp(method_name, classname, cppname, method, cpp): def handle_method_cpp(method, classname, cppname, cpp):
ret = f'{method.result_type.spelling} ' ret = f'{method.result_type.spelling} '
names = [p.spelling if p.spelling != "" else f'_{chr(0x61 + i)}' names = [p.spelling if p.spelling != "" else f'_{chr(0x61 + i)}'
@ -504,7 +546,7 @@ def handle_method_cpp(method_name, classname, cppname, method, cpp):
names = ['linux_side'] + names names = ['linux_side'] + names
params = ['void *linux_side'] + params params = ['void *linux_side'] + params
cpp.write(f'{ret}{cppname}_{method_name}({", ".join(params)})\n') cpp.write(f'{ret}{cppname}_{method.name}({", ".join(params)})\n')
cpp.write("{\n") cpp.write("{\n")
do_lin_to_win = None do_lin_to_win = None
@ -608,7 +650,7 @@ def handle_method_cpp(method_name, classname, cppname, method, cpp):
cpp.write("}\n\n") cpp.write("}\n\n")
def handle_method_c(used_name, classname, winclassname, cppname, method, iface_version, cfile): def handle_method_c(method, classname, winclassname, cppname, iface_version, cfile):
returns_record = method.result_type.get_canonical().kind == TypeKind.RECORD returns_record = method.result_type.get_canonical().kind == TypeKind.RECORD
if returns_record: if returns_record:
parambytes = 8 #_this + return pointer parambytes = 8 #_this + return pointer
@ -616,14 +658,14 @@ def handle_method_c(used_name, classname, winclassname, cppname, method, iface_v
parambytes = 4 #_this parambytes = 4 #_this
for param in method.get_arguments(): for param in method.get_arguments():
parambytes += param.type.get_size() parambytes += param.type.get_size()
cfile.write("DEFINE_THISCALL_WRAPPER(%s_%s, %s)\n" % (winclassname, used_name, parambytes)) cfile.write("DEFINE_THISCALL_WRAPPER(%s_%s, %s)\n" % (winclassname, method.name, parambytes))
if strip_ns(method.result_type.spelling).startswith("IVR"): if strip_ns(method.result_type.spelling).startswith("IVR"):
cfile.write("win%s " % (strip_ns(method.result_type.spelling))) cfile.write("win%s " % (strip_ns(method.result_type.spelling)))
elif returns_record: elif returns_record:
cfile.write("%s *" % strip_ns(method.result_type.spelling)) cfile.write("%s *" % strip_ns(method.result_type.spelling))
else: else:
cfile.write("%s " % strip_ns(method.result_type.spelling)) cfile.write("%s " % strip_ns(method.result_type.spelling))
cfile.write('__thiscall %s_%s(%s *_this' % (winclassname, used_name, winclassname)) cfile.write('__thiscall %s_%s(%s *_this' % (winclassname, method.name, winclassname))
if returns_record: if returns_record:
cfile.write(", %s *_r" % strip_ns(method.result_type.spelling)) cfile.write(", %s *_r" % strip_ns(method.result_type.spelling))
unnamed = 'a' unnamed = 'a'
@ -691,14 +733,14 @@ def handle_method_c(used_name, classname, winclassname, cppname, method, iface_v
is_method_overridden = False is_method_overridden = False
for classname_pattern, methodname, override_generator in method_overrides: for classname_pattern, methodname, override_generator in method_overrides:
if used_name == methodname and classname_pattern in classname: if method.name == methodname and classname_pattern in classname:
fn_name = override_generator(cppname, method) fn_name = override_generator(cppname, method)
if fn_name: if fn_name:
cfile.write("%s(%s_%s, _this->linux_side" % (fn_name, cppname, used_name)) cfile.write("%s(%s_%s, _this->linux_side" % (fn_name, cppname, method.name))
is_method_overridden = True is_method_overridden = True
break break
else: else:
cfile.write("%s_%s(_this->linux_side" % (cppname, used_name)) cfile.write("%s_%s(_this->linux_side" % (cppname, method.name))
unnamed = 'a' unnamed = 'a'
first = True first = True
@ -807,23 +849,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(vrclient);
cfile.write(" %s user_data;\n" % user_data_type) cfile.write(" %s user_data;\n" % user_data_type)
break break
cfile.write("} %s;\n\n" % winclassname) cfile.write("} %s;\n\n" % winclassname)
methods = []
method_names = [] for method in klass.methods:
for child in klass.get_children(): if type(method) is Destructor:
if child.kind == CursorKind.CXX_METHOD: continue
method_name = method_unique_name(child, method_names) handle_method_hpp(method, cppname, cpp_h)
handle_method_hpp(method_name, cppname, child, cpp_h)
handle_method_cpp(method_name, klass.spelling, cppname, child, cpp) for method in klass.methods:
handle_method_c(method_name, klass.spelling, winclassname, cppname, child, klass.version, cfile) if type(method) is Destructor:
methods.append(child) continue
handle_method_cpp(method, klass.spelling, cppname, cpp)
for method in klass.methods:
if type(method) is Destructor:
continue
else:
handle_method_c(method, klass.spelling, winclassname, cppname, klass.version, cfile)
cfile.write("extern vtable_ptr %s_vtable;\n\n" % winclassname) cfile.write("extern vtable_ptr %s_vtable;\n\n" % winclassname)
cfile.write("#ifndef __GNUC__\n") cfile.write("#ifndef __GNUC__\n")
cfile.write("void __asm_dummy_vtables(void) {\n") cfile.write("void __asm_dummy_vtables(void) {\n")
cfile.write("#endif\n") cfile.write("#endif\n")
cfile.write(" __ASM_VTABLE(%s,\n" % winclassname) cfile.write(" __ASM_VTABLE(%s,\n" % winclassname)
for method in method_names: for method in sorted(klass.methods, key=lambda x: (x._index, -x._override)):
cfile.write(" VTABLE_ADD_FUNC(%s_%s)\n" % (winclassname, method)) cfile.write(f" VTABLE_ADD_FUNC({winclassname}_{method.name})\n")
cfile.write(" );\n") cfile.write(" );\n")
cfile.write("#ifndef __GNUC__\n") cfile.write("#ifndef __GNUC__\n")
cfile.write("}\n") cfile.write("}\n")
@ -846,17 +895,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(vrclient);
# flat (FnTable) API # flat (FnTable) API
cfile.write("%s *create_%s_FnTable(void *linux_side)\n{\n" % (winclassname, winclassname)) cfile.write("%s *create_%s_FnTable(void *linux_side)\n{\n" % (winclassname, winclassname))
cfile.write(" %s *r = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(%s));\n" % (winclassname, winclassname)) cfile.write(" %s *r = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(%s));\n" % (winclassname, winclassname))
cfile.write(" struct thunk *thunks = alloc_thunks(%d);\n" % len(methods)) cfile.write(" struct thunk *thunks = alloc_thunks(%d);\n" % len(klass.methods))
cfile.write(" struct thunk **vtable = HeapAlloc(GetProcessHeap(), 0, %d * sizeof(*vtable));\n" % len(methods)) cfile.write(" struct thunk **vtable = HeapAlloc(GetProcessHeap(), 0, %d * sizeof(*vtable));\n" % len(klass.methods))
cfile.write(" int i;\n\n") cfile.write(" int i;\n\n")
cfile.write(" TRACE(\"-> %p, vtable %p, thunks %p\\n\", r, vtable, thunks);\n") cfile.write(" TRACE(\"-> %p, vtable %p, thunks %p\\n\", r, vtable, thunks);\n")
for i in range(len(methods)): for i, method in enumerate(klass.methods):
thunk_params = get_capi_thunk_params(methods[i]) thunk_params = get_capi_thunk_params(method)
arguments = list(methods[i].get_arguments()) arguments = list(method.get_arguments())
global max_c_api_param_count global max_c_api_param_count
max_c_api_param_count = max(len(arguments), max_c_api_param_count) max_c_api_param_count = max(len(arguments), max_c_api_param_count)
cfile.write(" init_thunk(&thunks[%d], r, %s_%s, %s);\n" % (i, winclassname, method_names[i], thunk_params)) cfile.write(" init_thunk(&thunks[%d], r, %s_%s, %s);\n" % (i, winclassname, method.name, thunk_params))
cfile.write(" for (i = 0; i < %d; i++)\n" % len(methods)) cfile.write(" for (i = 0; i < %d; i++)\n" % len(klass.methods))
cfile.write(" vtable[i] = &thunks[i];\n") cfile.write(" vtable[i] = &thunks[i];\n")
cfile.write(" r->linux_side = linux_side;\n") cfile.write(" r->linux_side = linux_side;\n")
cfile.write(" r->vtable = (void *)vtable;\n") cfile.write(" r->vtable = (void *)vtable;\n")
@ -887,7 +936,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(vrclient);
constructors.write(f" {{\"{klass.version}\", &create_{winclassname}, &destroy_{winclassname}}},\n") constructors.write(f" {{\"{klass.version}\", &create_{winclassname}, &destroy_{winclassname}}},\n")
constructors.write(f" {{\"FnTable:{klass.version}\", &create_{winclassname}_FnTable, &destroy_{winclassname}_FnTable}},\n") constructors.write(f" {{\"FnTable:{klass.version}\", &create_{winclassname}_FnTable, &destroy_{winclassname}_FnTable}},\n")
generate_c_api_thunk_tests(winclassname, methods, method_names) generate_c_api_thunk_tests(winclassname, klass.methods)
def canonical_typename(cursor): def canonical_typename(cursor):
@ -1232,9 +1281,9 @@ extern void call_flat_method_f(void);
f.write("#endif\n") f.write("#endif\n")
def generate_c_api_method_test(f, header, thunks_c, class_name, method_name, method): def generate_c_api_method_test(f, header, thunks_c, class_name, method):
thunk_params = get_capi_thunk_params(method) thunk_params = get_capi_thunk_params(method)
f.write("\n init_thunk(t, this_ptr_value, %s_%s, %s);\n" % (class_name, method_name, thunk_params)) f.write("\n init_thunk(t, this_ptr_value, %s_%s, %s);\n" % (class_name, method.name, thunk_params))
f.write(" ") f.write(" ")
header.write("\n") header.write("\n")
thunks_c.write("\n") thunks_c.write("\n")
@ -1249,9 +1298,9 @@ def generate_c_api_method_test(f, header, thunks_c, class_name, method_name, met
header.write("%s " % strip_ns(method.result_type.spelling)) header.write("%s " % strip_ns(method.result_type.spelling))
thunks_c.write("%s " % strip_ns(method.result_type.spelling)) thunks_c.write("%s " % strip_ns(method.result_type.spelling))
first_param = True first_param = True
f.write('(__stdcall *capi_%s_%s)(' % (class_name, method_name)) f.write('(__stdcall *capi_%s_%s)(' % (class_name, method.name))
header.write('__thiscall %s_%s(void *_this' % (class_name, method_name)) header.write('__thiscall %s_%s(void *_this' % (class_name, method.name))
thunks_c.write('__thiscall %s_%s(void *_this' % (class_name, method_name)) thunks_c.write('__thiscall %s_%s(void *_this' % (class_name, method.name))
if returns_record: if returns_record:
f.write("%s *_r" % strip_ns(method.result_type.spelling)) f.write("%s *_r" % strip_ns(method.result_type.spelling))
first_param = False first_param = False
@ -1311,11 +1360,11 @@ def generate_c_api_method_test(f, header, thunks_c, class_name, method_name, met
parameter_checks = [] parameter_checks = []
def add_parameter_check(typename, value): def add_parameter_check(typename, value):
parameter_checks.append("check_%s_parameter(\"%s_%s\", %s)" % (typename, class_name, method_name, value)) parameter_checks.append("check_%s_parameter(\"%s_%s\", %s)" % (typename, class_name, method.name, value))
add_parameter_check("ptr", "this_ptr_value") add_parameter_check("ptr", "this_ptr_value")
f.write("\n") f.write("\n")
f.write(" clear_parameters();\n") f.write(" clear_parameters();\n")
f.write(" capi_%s_%s(" % (class_name, method_name)) f.write(" capi_%s_%s(" % (class_name, method.name))
first_param = True first_param = True
if returns_record: if returns_record:
f.write("data_ptr_value") f.write("data_ptr_value")
@ -1349,7 +1398,7 @@ def generate_c_api_method_test(f, header, thunks_c, class_name, method_name, met
for c in parameter_checks: for c in parameter_checks:
f.write(" %s;\n" % c) f.write(" %s;\n" % c)
def generate_c_api_thunk_tests(winclassname, methods, method_names): def generate_c_api_thunk_tests(winclassname, methods):
class_name = re.sub(r'^win[A-Za-z]+_', '', winclassname) class_name = re.sub(r'^win[A-Za-z]+_', '', winclassname)
filename = "tests/capi_thunks_autogen.h" filename = "tests/capi_thunks_autogen.h"
@ -1388,8 +1437,8 @@ def generate_c_api_thunk_tests(winclassname, methods, method_names):
""") """)
f.write("\nvoid test_capi_thunks_%s(void)\n{\n" % class_name) f.write("\nvoid test_capi_thunks_%s(void)\n{\n" % class_name)
f.write(" struct thunk *t = alloc_thunks(1);\n"); f.write(" struct thunk *t = alloc_thunks(1);\n");
for i in range(len(methods)): for method in methods:
generate_c_api_method_test(f, header, thunks_c, class_name, method_names[i], methods[i]) generate_c_api_method_test(f, header, thunks_c, class_name, method)
f.write(" VirtualFree(t, 0, MEM_RELEASE);\n") f.write(" VirtualFree(t, 0, MEM_RELEASE);\n")
f.write("}\n") f.write("}\n")