From 600870e0087ff57cd16c51ba6a048c9854dce9bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 24 Sep 2023 17:15:41 +0200 Subject: [PATCH] vrclient: Introduce a new Method cursor wrapper. CW-Bug-Id: #22729 --- vrclient_x64/gen_wrapper.py | 155 ++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/vrclient_x64/gen_wrapper.py b/vrclient_x64/gen_wrapper.py index ffed68dd..3f3c317a 100755 --- a/vrclient_x64/gen_wrapper.py +++ b/vrclient_x64/gen_wrapper.py @@ -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: def __init__(self, sdkver, abi, cursor): self._sdkver = sdkver @@ -394,6 +428,28 @@ class Class: self.filename = SDK_CLASSES[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): return self._cursor.get_children() @@ -422,20 +478,6 @@ def get_path_converter(parent): 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): if type(decl) is Cursor: decl = decl.type @@ -485,16 +527,16 @@ def declspec(decl, 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)} ' params = [declspec(p, "") for p in method.get_arguments()] 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} ' 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 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") do_lin_to_win = None @@ -608,7 +650,7 @@ def handle_method_cpp(method_name, classname, cppname, method, cpp): 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 if returns_record: parambytes = 8 #_this + return pointer @@ -616,14 +658,14 @@ def handle_method_c(used_name, classname, winclassname, cppname, method, iface_v parambytes = 4 #_this for param in method.get_arguments(): 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"): cfile.write("win%s " % (strip_ns(method.result_type.spelling))) elif returns_record: cfile.write("%s *" % strip_ns(method.result_type.spelling)) else: 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: cfile.write(", %s *_r" % strip_ns(method.result_type.spelling)) unnamed = 'a' @@ -691,14 +733,14 @@ def handle_method_c(used_name, classname, winclassname, cppname, method, iface_v is_method_overridden = False 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) 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 break else: - cfile.write("%s_%s(_this->linux_side" % (cppname, used_name)) + cfile.write("%s_%s(_this->linux_side" % (cppname, method.name)) unnamed = 'a' first = True @@ -807,23 +849,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(vrclient); cfile.write(" %s user_data;\n" % user_data_type) break cfile.write("} %s;\n\n" % winclassname) - methods = [] - method_names = [] - for child in klass.get_children(): - if child.kind == CursorKind.CXX_METHOD: - method_name = method_unique_name(child, method_names) - handle_method_hpp(method_name, cppname, child, cpp_h) - handle_method_cpp(method_name, klass.spelling, cppname, child, cpp) - handle_method_c(method_name, klass.spelling, winclassname, cppname, child, klass.version, cfile) - methods.append(child) + + for method in klass.methods: + if type(method) is Destructor: + continue + handle_method_hpp(method, cppname, cpp_h) + + for method in klass.methods: + if type(method) is Destructor: + 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("#ifndef __GNUC__\n") cfile.write("void __asm_dummy_vtables(void) {\n") cfile.write("#endif\n") cfile.write(" __ASM_VTABLE(%s,\n" % winclassname) - for method in method_names: - cfile.write(" VTABLE_ADD_FUNC(%s_%s)\n" % (winclassname, method)) + for method in sorted(klass.methods, key=lambda x: (x._index, -x._override)): + cfile.write(f" VTABLE_ADD_FUNC({winclassname}_{method.name})\n") cfile.write(" );\n") cfile.write("#ifndef __GNUC__\n") cfile.write("}\n") @@ -846,17 +895,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(vrclient); # flat (FnTable) API 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(" struct thunk *thunks = alloc_thunks(%d);\n" % len(methods)) - cfile.write(" struct thunk **vtable = HeapAlloc(GetProcessHeap(), 0, %d * sizeof(*vtable));\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(klass.methods)) cfile.write(" int i;\n\n") cfile.write(" TRACE(\"-> %p, vtable %p, thunks %p\\n\", r, vtable, thunks);\n") - for i in range(len(methods)): - thunk_params = get_capi_thunk_params(methods[i]) - arguments = list(methods[i].get_arguments()) + for i, method in enumerate(klass.methods): + thunk_params = get_capi_thunk_params(method) + arguments = list(method.get_arguments()) global 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(" for (i = 0; i < %d; i++)\n" % len(methods)) + 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(klass.methods)) cfile.write(" vtable[i] = &thunks[i];\n") cfile.write(" r->linux_side = linux_side;\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" {{\"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): @@ -1232,9 +1281,9 @@ extern void call_flat_method_f(void); 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) - 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(" ") header.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)) thunks_c.write("%s " % strip_ns(method.result_type.spelling)) first_param = True - f.write('(__stdcall *capi_%s_%s)(' % (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)) + f.write('(__stdcall *capi_%s_%s)(' % (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)) if returns_record: f.write("%s *_r" % strip_ns(method.result_type.spelling)) first_param = False @@ -1311,11 +1360,11 @@ def generate_c_api_method_test(f, header, thunks_c, class_name, method_name, met parameter_checks = [] 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") f.write("\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 if returns_record: 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: 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) 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(" struct thunk *t = alloc_thunks(1);\n"); - for i in range(len(methods)): - generate_c_api_method_test(f, header, thunks_c, class_name, method_names[i], methods[i]) + for method in methods: + generate_c_api_method_test(f, header, thunks_c, class_name, method) f.write(" VirtualFree(t, 0, MEM_RELEASE);\n") f.write("}\n")