codemaker/source/rustmaker/cpproduce.cxx | 538 ++++++-------- codemaker/source/rustmaker/cpproduce.hxx | 5 codemaker/source/rustmaker/rustproduce.cxx | 1089 +++++++++++++++++++++++------ codemaker/source/rustmaker/rustproduce.hxx | 93 ++ rust_uno/build.rs | 1 rust_uno/src/examples/load_writer.rs | 101 +- 6 files changed, 1310 insertions(+), 517 deletions(-)
New commits: commit 86ade6386370c915e6977ebd2e3dba95dde50484 Author: Mohamed Ali <[email protected]> AuthorDate: Thu Sep 25 10:12:11 2025 +0300 Commit: Stephan Bergmann <[email protected]> CommitDate: Mon Sep 29 09:57:43 2025 +0200 Rust Bindings: Use typed parameters instead of void* in UNO bindings Replace generic void* parameters with proper types (strings, primitives, structs) for better type safety and more idiomatic Rust code. Change-Id: Iced1c2de96564be7ec599f14bf1a685eaede2768 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191477 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> diff --git a/codemaker/source/rustmaker/cpproduce.cxx b/codemaker/source/rustmaker/cpproduce.cxx index d027f5834c36..397b82f0ac38 100644 --- a/codemaker/source/rustmaker/cpproduce.cxx +++ b/codemaker/source/rustmaker/cpproduce.cxx @@ -618,7 +618,9 @@ void CppProducer::generateStructHeader(std::string_view name, .append(")") .endLine() .beginLine() - .append("SAL_DLLPUBLIC_EXPORT void* ") + .append("SAL_DLLPUBLIC_EXPORT ") + .append(getStructGetterReturnType(member.type)) + .append(" ") .append(functionPrefix) .append("_get_") .append(member.name) @@ -641,7 +643,9 @@ void CppProducer::generateStructHeader(std::string_view name, .append(member.name) .append("(") .append(handleTypeName) - .append(" handle, void* value);") + .append(" handle, ") + .append(getStructSetterParameterType(member.type)) + .append(" value);") .endLine(); } file.endBlock().append("// extern \"C\"").endLine(); @@ -963,7 +967,9 @@ void CppProducer::generateStructMemberAccessors(CppFile& file, std::string_view { // Generate getter implementation file.beginLine() - .append("SAL_DLLPUBLIC_EXPORT void* ") + .append("SAL_DLLPUBLIC_EXPORT ") + .append(getStructGetterReturnType(member.type)) + .append(" ") .append(functionPrefix) .append("_get_") .append(member.name) @@ -978,12 +984,35 @@ void CppProducer::generateStructMemberAccessors(CppFile& file, std::string_view .append(className) .append("*>(handle);") .endLine() - .beginLine() - .append("return &(obj->") - .append(member.name) - .append(");") - .endLine() - .endBlock(); + .beginLine(); + + // Handle return value conversion based on type + if (member.type == u"string") + { + // For string types, return pointer to the OUString object + file.append("return &(obj->").append(member.name).append(");"); + } + else if (member.type == u"any") + { + // For any types, return pointer to the Any + file.append("return &(obj->").append(member.name).append(");"); + } + else if (member.type == u"boolean" || member.type == u"byte" || member.type == u"short" + || member.type == u"unsigned short" || member.type == u"long" + || member.type == u"unsigned long" || member.type == u"hyper" + || member.type == u"unsigned hyper" || member.type == u"float" + || member.type == u"double") + { + // For primitive types, return pointer to the member for direct access + file.append("return &(obj->").append(member.name).append(");"); + } + else + { + // For other types, return pointer to member + file.append("return &(obj->").append(member.name).append(");"); + } + + file.endLine().endBlock(); // Generate setter implementation file.beginLine() @@ -993,7 +1022,9 @@ void CppProducer::generateStructMemberAccessors(CppFile& file, std::string_view .append(member.name) .append("(") .append(handleTypeName) - .append(" handle, void* value)") + .append(" handle, ") + .append(getStructSetterParameterType(member.type)) + .append(" value)") .endLine() .beginBlock() .beginLine() @@ -1006,31 +1037,46 @@ void CppProducer::generateStructMemberAccessors(CppFile& file, std::string_view .append("obj->") .append(member.name); - // Check if this is a UNO interface type by checking the UNO type string + // Handle assignment based on parameter type OUString unoType = member.type; - SAL_INFO("codemaker", "Struct member " << member.name << " UNO type: " << unoType); - - // UNO interface types start with "com.sun.star" and end with an interface name (typically starting with "X") - // Examples: "com.sun.star.uno.XInterface", "com.sun.star.text.XText" - bool isInterfaceType = (unoType.startsWith("com.sun.star.") && (unoType.lastIndexOf('.') != -1) - && (unoType.subView(unoType.lastIndexOf('.') + 1).starts_with('X'))); - if (isInterfaceType) + // Check if we're using typed primitive parameters (passed by value) + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + // For primitive types with typed parameters, assign directly (no dereferencing) + file.append(" = value;"); + } + else if (unoType == u"string") { - SAL_INFO("codemaker", "Detected UNO interface type, using pointer assignment"); - // For interface types, assign the pointer directly without dereferencing - // The C++ struct expects Reference<Interface>, but we assign the raw interface pointer - file.append(" = reinterpret_cast<") - .append(convertUnoTypeToCpp(unoType)) - .append("*>(value);"); + // For string types, dereference the pointer to OUString + file.append(" = *reinterpret_cast<const OUString*>(value);"); + } + else if (unoType == u"any") + { + // For any types with typed parameters, dereference the uno_Any* + file.append(" = *reinterpret_cast<const Any*>(value);"); } else { - SAL_INFO("codemaker", "Non-interface type, using dereferenced assignment"); - // For non-interface types, dereference the pointer as before - file.append(" = *reinterpret_cast<const ") - .append(convertUnoTypeToCpp(unoType)) - .append("*>(value);"); + // For other types (interfaces, structs, enums), use original logic with void* + // Check if this is a UNO interface type using proper TypeManager classification + if (isUnoInterface(unoType)) + { + // For interface types, assign the pointer directly without dereferencing + file.append(" = reinterpret_cast<") + .append(convertUnoTypeToCpp(unoType)) + .append("*>(value);"); + } + else + { + // For non-interface types, dereference the pointer + file.append(" = *reinterpret_cast<const ") + .append(convertUnoTypeToCpp(unoType)) + .append("*>(value);"); + } } file.endLine().endBlock().beginLine().append("").endLine(); } @@ -1435,10 +1481,13 @@ void CppProducer::generateInterfaceHeader(std::string_view name, .append(handleTypeName) .append(" handle"); - // Add parameters + // Add typed parameters instead of void* for (const auto& param : method.parameters) { - file.append(", void* ").append(param.name); + file.append(", ") + .append(getTypedParameterType(param.type, param.direction)) + .append(" ") + .append(param.name); } file.append(");"); @@ -1677,10 +1726,13 @@ void CppProducer::generateSingleInterfaceMethod(CppFile& file, std::string_view .append(handleTypeName) .append(" handle"); - // Add parameters + // Add typed parameters instead of void* for (const auto& param : method.parameters) { - file.append(", void* ").append(param.name); + file.append(", ") + .append(getTypedParameterType(param.type, param.direction)) + .append(" ") + .append(param.name); } file.append(")").endLine().beginBlock(); @@ -1850,70 +1902,62 @@ void CppProducer::generateActualMethodCall(CppFile& file, { if (isInputOnly) { - // Input parameter: convert rtl_uString* to OUString object - file.append("OUString(static_cast<rtl_uString*>(").append(param.name).append("))"); + // Input parameter: direct rtl_uString* to OUString conversion (no casting needed) + file.append("OUString(").append(param.name).append(")"); } else { - // Output parameter: needs OUString reference for binding - file.append("*reinterpret_cast<OUString*>(").append(param.name).append(")"); + // Output parameter: rtl_uString** needs dereferencing to OUString reference + file.append("*reinterpret_cast<OUString*>(*").append(param.name).append(")"); } } else if (paramType == u"any") { if (isInputOnly) + { + // Input parameter: dereference uno_Any* to get Any& (Any inherits from uno_Any) file.append("*reinterpret_cast<const Any*>(").append(param.name).append(")"); + } else - file.append("*reinterpret_cast<Any*>(").append(param.name).append(")"); - } - else if (paramType == u"boolean") - { - if (isInputOnly) - file.append("*reinterpret_cast<const sal_Bool*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<sal_Bool*>(").append(param.name).append(")"); - } - else if (paramType == u"byte") - { - if (isInputOnly) - file.append("*reinterpret_cast<const sal_Int8*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<sal_Int8*>(").append(param.name).append(")"); - } - else if (paramType == u"short") - { - if (isInputOnly) - file.append("*reinterpret_cast<const sal_Int16*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<sal_Int16*>(").append(param.name).append(")"); - } - else if (paramType == u"long") - { - if (isInputOnly) - file.append("*reinterpret_cast<const sal_Int32*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<sal_Int32*>(").append(param.name).append(")"); - } - else if (paramType == u"hyper") - { - if (isInputOnly) - file.append("*reinterpret_cast<const sal_Int64*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<sal_Int64*>(").append(param.name).append(")"); - } - else if (paramType == u"float") - { - if (isInputOnly) - file.append("*reinterpret_cast<const float*>(").append(param.name).append(")"); - else - file.append("*reinterpret_cast<float*>(").append(param.name).append(")"); + { + // Output parameter: uno_Any** needs dereferencing to Any reference + file.append("*reinterpret_cast<Any*>(*").append(param.name).append(")"); + } } - else if (paramType == u"double") + // Handle all primitive types with typed parameter support + else if (paramType == u"boolean" || paramType == u"byte" || paramType == u"short" + || paramType == u"unsigned short" || paramType == u"long" + || paramType == u"unsigned long" || paramType == u"hyper" + || paramType == u"unsigned hyper" || paramType == u"float" + || paramType == u"double") { - if (isInputOnly) - file.append("*reinterpret_cast<const double*>(").append(param.name).append(")"); + // Check if this primitive type is using typed parameters + OString primitiveType = mapUnoPrimitiveToSal(resolveTypedef(paramType)); + if (!primitiveType.isEmpty()) + { + // Typed parameter approach - direct value for input, dereference pointer for input/output + if (isInputOnly) + file.append(param.name); // Direct value (sal_Bool, sal_Int32, double, etc.) + else + file.append("*").append( + param.name); // Dereference pointer (sal_Bool*, sal_Int32*, double*, etc.) + } else - file.append("*reinterpret_cast<double*>(").append(param.name).append(")"); + { + // Fallback to void* approach (shouldn't happen for primitives, but just in case) + if (isInputOnly) + file.append("*reinterpret_cast<const ") + .append(primitiveType) + .append("*>(") + .append(param.name) + .append(")"); + else + file.append("*reinterpret_cast<") + .append(primitiveType) + .append("*>(") + .append(param.name) + .append(")"); + } } else if (paramType == u"type") { @@ -2265,6 +2309,142 @@ OString CppProducer::getCppTypeName(std::u16string_view unoType) const return "void*"_ostr; // Default to void* for unknown types } +OString CppProducer::getTypedParameterType( + std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const +{ + // Handle string types with typed parameters + if (unoType == u"string") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input string: pass as rtl_uString* (no const to avoid casting) + return "rtl_uString*"_ostr; + } + else + { + // Input/output string: pass as rtl_uString** (modifiable) + return "rtl_uString**"_ostr; + } + } + + // Handle any types with typed parameters + if (unoType == u"any") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input any: pass as uno_Any* (no const to avoid casting) + return "uno_Any*"_ostr; + } + else + { + // Input/output any: pass as uno_Any** (modifiable) + return "uno_Any**"_ostr; + } + } + + // Only use typed parameters for basic primitive types (not typedefs) + // This ensures we don't break existing null-checking logic for complex types + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + OString primitiveType = mapUnoPrimitiveToSal(unoType); + if (!primitiveType.isEmpty()) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input parameters: pass by value (sal_Bool, sal_Int32, etc.) + return primitiveType; + } + else + { + // Input/output parameters: pass by pointer (sal_Bool*, sal_Int32*, etc.) + return primitiveType + "*"; + } + } + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + // This maintains compatibility with existing null-checking and casting logic + return "void*"_ostr; +} + +OString CppProducer::getStructGetterReturnType(std::u16string_view unoType) const +{ + // Struct getters return a pointer to the member data (like output parameters) + // Use the same logic as getTypedParameterType with direction = OUT + + // Handle string types + if (unoType == u"string") + { + return "void*"_ostr; // Return pointer to string for compatibility + } + + // Handle any types + if (unoType == u"any") + { + return "uno_Any*"_ostr; // Return pointer to Any for direct access + } + + // Handle primitive types + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + OString primitiveType = mapUnoPrimitiveToSal(unoType); + if (!primitiveType.isEmpty()) + { + return primitiveType + "*"; // Return pointer to primitive type + } + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + return "void*"_ostr; +} + +OString CppProducer::getStructSetterParameterType(std::u16string_view unoType) const +{ + // Struct setters take the value to set (like input parameters) + // Use the same logic as getTypedParameterType with direction = IN + + // Handle string types + if (unoType == u"string") + { + return "rtl_uString*"_ostr; // Take string pointer for compatibility + } + + // Handle any types + if (unoType == u"any") + { + return "uno_Any*"_ostr; // Take Any pointer directly + } + + // Handle primitive types + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + OString primitiveType = mapUnoPrimitiveToSal(unoType); + if (!primitiveType.isEmpty()) + { + return primitiveType; // Take primitive type by value + } + } + + // For all other types + return "void*"_ostr; +} + OString CppProducer::convertBasicType(const OString& typeName) { OString result = typeName; diff --git a/codemaker/source/rustmaker/cpproduce.hxx b/codemaker/source/rustmaker/cpproduce.hxx index ac07a888a45a..9d343109d41c 100644 --- a/codemaker/source/rustmaker/cpproduce.hxx +++ b/codemaker/source/rustmaker/cpproduce.hxx @@ -149,6 +149,11 @@ private: OString convertUnoTypeToCpp(std::u16string_view unoType) const; std::string convertTemplateArguments(const std::string& unoType) const; OString getCppTypeName(std::u16string_view unoType) const; + OString getTypedParameterType( + std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const; + OString getStructGetterReturnType(std::u16string_view unoType) const; + OString getStructSetterParameterType(std::u16string_view unoType) const; OString getRustFFITypeName(std::u16string_view unoType) const; static OString mapUnoPrimitiveToSal(std::u16string_view unoType); OUString resolveTypedef(std::u16string_view unoType) const; diff --git a/codemaker/source/rustmaker/rustproduce.cxx b/codemaker/source/rustmaker/rustproduce.cxx index b15780a7c053..22539fd834fb 100644 --- a/codemaker/source/rustmaker/rustproduce.cxx +++ b/codemaker/source/rustmaker/rustproduce.cxx @@ -232,6 +232,10 @@ void RustProducer::generateStructImplementation( generateStructConstructor(file, externFunctionPrefix); generateStructFromPtr(file, externFunctionPrefix); + + // Generate as_ptr method + generateStructAsPtr(file); + generateStructAccessors(file, entity, externFunctionPrefix); file.endBlock(); @@ -305,6 +309,21 @@ void RustProducer::generateStructFromPtr(RustFile& file, std::string_view extern .endBlock(); } +void RustProducer::generateStructAsPtr(RustFile& file) +{ + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn as_ptr(&self) -> *mut c_void") + .endLine() + .beginBlock() + .beginLine() + .append("self.ptr") + .endLine() + .endBlock(); +} + void RustProducer::generateStructAccessors( RustFile& file, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity, std::string_view externFunctionPrefix) @@ -313,58 +332,139 @@ void RustProducer::generateStructAccessors( for (const auto& member : entity->getDirectMembers()) { OString memberName = u2b(member.name); // Use original case, not snake_case + std::u16string_view memberType = member.type; - generateStructMemberGetter(file, memberName, externFunctionPrefix); - generateStructMemberSetter(file, memberName, externFunctionPrefix); + generateStructMemberGetter(file, memberName, memberType, externFunctionPrefix); + generateStructMemberSetter(file, memberName, memberType, externFunctionPrefix); } } void RustProducer::generateStructMemberGetter(RustFile& file, std::string_view memberName, + std::u16string_view memberType, std::string_view externFunctionPrefix) { + OString returnType = getRustStructGetterReturnType(memberType); + file.beginLine() .append("") .endLine() .beginLine() .append("pub fn get_") .append(memberName) - .append("(&self) -> *mut c_void {") + .append("(&self) -> ") + .append(returnType) + .append(" {") .endLine() .extraIndent() - .beginLine() - .append("unsafe { ") - .append(externFunctionPrefix) - .append("_get_") - .append(memberName) - .append("(self.ptr) }") - .endLine() - .beginLine() - .append("}") - .endLine(); + .beginLine(); + + // Handle conversion based on type + if (memberType == u"string") + { + // String type - convert from FFI to high-level type + file.append("unsafe { crate::core::OUString::from_raw(") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr)) }"); + } + else if (memberType == u"any") + { + // Any type - convert from FFI to high-level type + file.append("unsafe { crate::core::Any::from_raw(std::ptr::read(") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr) as *const crate::ffi::uno_any::uno_Any)) }"); + } + else if (memberType == u"boolean" || memberType == u"byte" || memberType == u"short" + || memberType == u"unsigned short" || memberType == u"long" + || memberType == u"unsigned long" || memberType == u"hyper" + || memberType == u"unsigned hyper" || memberType == u"float" + || memberType == u"double") + { + // Primitive types - dereference the pointer returned from FFI + file.append("unsafe { *") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr) }"); + } + else + { + // Other types - return raw pointer + file.append("unsafe { ") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr) }"); + } + + file.endLine().beginLine().append("}").endLine(); } void RustProducer::generateStructMemberSetter(RustFile& file, std::string_view memberName, + std::u16string_view memberType, std::string_view externFunctionPrefix) { + OString parameterType = getRustStructSetterParameterType(memberType); + file.beginLine() .append("") .endLine() .beginLine() .append("pub fn set_") .append(memberName) - .append("(&mut self, value: *mut c_void) {") + .append("(&mut self, value: ") + .append(parameterType) + .append(") {") .endLine() .extraIndent() - .beginLine() - .append("unsafe { ") - .append(externFunctionPrefix) - .append("_set_") - .append(memberName) - .append("(self.ptr, value) }") - .endLine() - .beginLine() - .append("}") - .endLine(); + .beginLine(); + + // Handle conversion based on type + if (memberType == u"string") + { + // String type - convert from high-level to FFI type + file.append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value.as_ptr()) }"); + } + else if (memberType == u"any") + { + // Any type - convert from high-level to FFI type + file.append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value.as_ptr()) }"); + } + else if (memberType == u"boolean" || memberType == u"byte" || memberType == u"short" + || memberType == u"unsigned short" || memberType == u"long" + || memberType == u"unsigned long" || memberType == u"hyper" + || memberType == u"unsigned hyper" || memberType == u"float" + || memberType == u"double") + { + // Primitive types - pass by value directly + file.append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value) }"); + } + else + { + // Other types - pass raw pointer through + file.append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value) }"); + } + + file.endLine().beginLine().append("}").endLine(); } void RustProducer::generateStructDropTrait( @@ -453,19 +553,26 @@ void RustProducer::generateStructMemberExternDeclarations( for (const auto& member : entity->getDirectMembers()) { OString memberName = u2b(member.name); // Use original case, not snake_case + OString getterReturnType = getRustStructExternGetterReturnType(member.type); + OString setterParamType = getRustStructExternSetterParameterType(member.type); + file.beginLine() .append("fn ") .append(externFunctionPrefix) .append("_get_") .append(memberName) - .append("(ptr: *mut c_void) -> *mut c_void;") + .append("(ptr: *mut c_void) -> ") + .append(getterReturnType) + .append(";") .endLine() .beginLine() .append("fn ") .append(externFunctionPrefix) .append("_set_") .append(memberName) - .append("(ptr: *mut c_void, value: *mut c_void);") + .append("(ptr: *mut c_void, value: ") + .append(setterParamType) + .append(");") .endLine(); } } @@ -691,10 +798,13 @@ void RustProducer::generateInterfaceMethodWrappers( .append(rustMethodName) .append("(&self"); - // All parameters are opaque void pointers + // Parameters with typed support for primitives for (const auto& param : method.parameters) { - file.append(", ").append(param.name).append(": *mut c_void"); + file.append(", ") + .append(param.name) + .append(": ") + .append(getRustParameterType(param.type, param.direction)); } file.append(") -> ") @@ -716,7 +826,8 @@ void RustProducer::generateInterfaceMethodWrappers( for (const auto& param : method.parameters) { - file.append(", ").append(param.name); + file.append(", ").append( + convertRustParameterForFFICall(param.type, u2b(param.name), param.direction)); } file.append(") };").endLine(); @@ -733,7 +844,8 @@ void RustProducer::generateInterfaceMethodWrappers( for (const auto& param : method.parameters) { - file.append(", ").append(param.name); + file.append(", ").append( + convertRustParameterForFFICall(param.type, u2b(param.name), param.direction)); } file.append(") };").endLine(); @@ -945,7 +1057,10 @@ void RustProducer::generateInterfaceExternDeclarations( for (const auto& param : method.parameters) { - file.append(", ").append(param.name).append(": *mut c_void"); + file.append(", ") + .append(param.name) + .append(": ") + .append(getRustExternParameterType(param.type, param.direction)); } file.append(") -> "); @@ -1350,6 +1465,246 @@ OString RustProducer::getRustReturnType(std::u16string_view unoType) const return getRustWrapperTypeName(unoType); } +OString RustProducer::getRustParameterType( + std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const +{ + // Handle string types with typed parameters (high-level Rust API) + if (unoType == u"string") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input string: take high-level OUString in public API + return "crate::core::OUString"_ostr; + } + else + { + // Input/output string: still use raw pointer for output parameters + return "*mut *mut crate::ffi::rtl_string::rtl_uString"_ostr; + } + } + + // Handle any types with typed parameters (high-level Rust API) + if (unoType == u"any") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input any: take high-level Any in public API + return "crate::core::Any"_ostr; + } + else + { + // Input/output any: still use raw pointer for output parameters + return "*mut *mut crate::ffi::uno_any::uno_Any"_ostr; + } + } + + // Handle struct types with typed parameters (high-level Rust API) + if (isUnoStruct(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input struct: take high-level generated struct in public API + OString rustType = u2b(unoType); + if (rustType.indexOf('.') != -1) + { + // Convert dots to :: for module path + OString modulePath = rustType.replaceAll("."_ostr, "::"_ostr); + + // Extract simple type name + sal_Int32 lastDot = rustType.lastIndexOf('.'); + if (lastDot != -1) + { + OString simpleName = rustType.copy(lastDot + 1); + return "crate::generated::rustmaker::" + modulePath + "::" + simpleName; + } + } + // Fallback for simple struct names without namespace + return "crate::generated::rustmaker::" + rustType; + } + else + { + // Input/output struct: still use raw pointer for output parameters + return "*mut c_void"_ostr; + } + } + + // Handle interface types with typed parameters (high-level Rust API) + if (isUnoInterface(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input interface: take high-level generated interface in public API + OString rustType = u2b(unoType); + if (rustType.indexOf('.') != -1) + { + // Convert dots to :: for module path + OString modulePath = rustType.replaceAll("."_ostr, "::"_ostr); + + // Extract simple type name + sal_Int32 lastDot = rustType.lastIndexOf('.'); + if (lastDot != -1) + { + OString simpleName = rustType.copy(lastDot + 1); + return "crate::generated::rustmaker::" + modulePath + "::" + simpleName; + } + } + // Fallback for simple interface names without namespace + return "crate::generated::rustmaker::" + rustType; + } + else + { + // Input/output interface: still use raw pointer for output parameters + return "*mut c_void"_ostr; + } + } + + // Only use typed parameters for basic primitive types (matching CppProducer approach) + // This ensures we don't break existing logic for complex types + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + // Get the Rust primitive type + OString rustPrimitiveType = getRustWrapperTypeName(unoType); + if (!rustPrimitiveType.isEmpty()) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input parameters: pass by value (u8 for boolean, i32 for long, etc.) + return rustPrimitiveType; + } + else + { + // Input/output parameters: pass by mutable pointer (*mut u8, *mut i32, etc.) + return "*mut " + rustPrimitiveType; + } + } + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + // This maintains compatibility with existing null-checking and casting logic + return "*mut c_void"_ostr; +} + +OString RustProducer::getRustExternParameterType( + std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const +{ + // Handle string types with C-compatible FFI parameters (different from high-level API) + if (unoType == u"string") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input string: pass as C-compatible pointer for FFI + return "*const crate::ffi::rtl_string::rtl_uString"_ostr; + } + else + { + // Input/output string: pass as mutable pointer for output parameters + return "*mut *mut crate::ffi::rtl_string::rtl_uString"_ostr; + } + } + + // Handle any types with C-compatible FFI parameters (different from high-level API) + if (unoType == u"any") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input any: pass as C-compatible pointer for FFI + return "*const crate::ffi::uno_any::uno_Any"_ostr; + } + else + { + // Input/output any: pass as mutable pointer for output parameters + return "*mut *mut crate::ffi::uno_any::uno_Any"_ostr; + } + } + + // Handle struct types with C-compatible FFI parameters (different from high-level API) + if (isUnoStruct(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input struct: pass as C-compatible pointer for FFI (void* for now, can be refined later) + return "*mut c_void"_ostr; + } + else + { + // Input/output struct: still use raw pointer for output parameters + return "*mut c_void"_ostr; + } + } + + // Handle interface types with C-compatible FFI parameters (different from high-level API) + if (isUnoInterface(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input interface: pass as C-compatible pointer for FFI (void* for interfaces) + return "*mut c_void"_ostr; + } + else + { + // Input/output interface: still use raw pointer for output parameters + return "*mut c_void"_ostr; + } + } + + // Handle enums - revert to void* to avoid nullptr comparison issues + if (isUnoEnum(unoType)) + { + return "*mut c_void"_ostr; + } + + // For primitive types, use the same logic as high-level API (primitives are the same) + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + // Get the Rust primitive type + OString rustPrimitiveType = getRustWrapperTypeName(unoType); + if (!rustPrimitiveType.isEmpty()) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input parameters: pass by value (u8 for boolean, i32 for long, etc.) + return rustPrimitiveType; + } + else + { + // Input/output parameters: pass by mutable pointer (*mut u8, *mut i32, etc.) + return "*mut " + rustPrimitiveType; + } + } + } + + // For all other types (typedefs, structs, interfaces, sequences), use void* + return "*mut c_void"_ostr; +} + bool RustProducer::isUnoInterface(std::u16string_view typeName) const { rtl::Reference<unoidl::Entity> entity; @@ -1374,4 +1729,207 @@ bool RustProducer::isUnoEnum(std::u16string_view typeName) const return sort == codemaker::UnoType::Sort::Enum; } +OString RustProducer::convertRustParameterForFFICall( + std::u16string_view unoType, std::string_view paramName, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const +{ + // Handle string types with automatic conversion + if (unoType == u"string") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input string: convert OUString to rtl_uString* using .as_ptr() + return OString(paramName) + ".as_ptr()"; + } + else + { + // Input/output string: pass through as pointer (already proper type) + return OString(paramName); + } + } + + // Handle any types with automatic conversion + if (unoType == u"any") + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input any: convert Any to uno_Any* using .as_ptr() + return OString(paramName) + ".as_ptr()"; + } + else + { + // Input/output any: pass through as pointer (already proper type) + return OString(paramName); + } + } + + // Handle struct types with automatic conversion + if (isUnoStruct(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input struct: convert generated struct to void* using .as_ptr() + return OString(paramName) + ".as_ptr()"; + } + else + { + // Input/output struct: pass through as pointer (already proper type) + return OString(paramName); + } + } + + // Handle interface types with automatic conversion + if (isUnoInterface(unoType)) + { + bool isInputOnly + = (direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + if (isInputOnly) + { + // Input interface: convert generated interface to void* using .as_ptr() + return OString(paramName) + ".as_ptr()"; + } + else + { + // Input/output interface: pass through as pointer (already proper type) + return OString(paramName); + } + } + + // Handle primitive types - pass through directly (no conversion needed) + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + // Primitive types already match FFI expectations - pass through directly + return OString(paramName); + } + + // For all other types, pass through without conversion (fallback) + return OString(paramName); +} + +OString RustProducer::getRustStructGetterReturnType(std::u16string_view unoType) const +{ + // Struct getters return the actual type (for direct access, not modification) + + // Handle string types + if (unoType == u"string") + { + return "crate::core::OUString"_ostr; // Return high-level string type + } + + // Handle any types + if (unoType == u"any") + { + return "crate::core::Any"_ostr; // Return high-level Any type + } + + // Handle primitive types - return the Rust primitive directly + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + return getRustWrapperTypeName(unoType); // Return native Rust type (u8, i32, f64, etc.) + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + return "*mut c_void"_ostr; +} + +OString RustProducer::getRustStructSetterParameterType(std::u16string_view unoType) const +{ + // Struct setters take the value to set + + // Handle string types + if (unoType == u"string") + { + return "crate::core::OUString"_ostr; // Take high-level string type + } + + // Handle any types + if (unoType == u"any") + { + return "crate::core::Any"_ostr; // Take high-level Any type + } + + // Handle primitive types - take by value for direct assignment + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + return getRustWrapperTypeName(unoType); // Take native Rust type (u8, i32, f64, etc.) + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + return "*mut c_void"_ostr; +} + +OString RustProducer::getRustStructExternGetterReturnType(std::u16string_view unoType) const +{ + // Struct extern getter return types (FFI-compatible) + + // Handle string types + if (unoType == u"string") + { + return "*mut crate::ffi::rtl_string::rtl_uString"_ostr; // Return mutable pointer for from_raw compatibility + } + + // Handle any types + if (unoType == u"any") + { + return "*const crate::ffi::uno_any::uno_Any"_ostr; // Return C-compatible Any pointer + } + + // Handle primitive types - return pointer to primitive for FFI + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + return "*const " + getRustWrapperTypeName(unoType); // Return pointer to primitive type + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + return "*mut c_void"_ostr; +} + +OString RustProducer::getRustStructExternSetterParameterType(std::u16string_view unoType) const +{ + // Struct extern setter parameter types (FFI-compatible) + + // Handle string types + if (unoType == u"string") + { + return "*const crate::ffi::rtl_string::rtl_uString"_ostr; // Take const pointer for setter input (this is fine) + } + + // Handle any types + if (unoType == u"any") + { + return "*const crate::ffi::uno_any::uno_Any"_ostr; // Take C-compatible Any pointer + } + + // Handle primitive types - take by value for FFI (matches C++ side) + if (unoType == u"boolean" || unoType == u"byte" || unoType == u"short" + || unoType == u"unsigned short" || unoType == u"long" || unoType == u"unsigned long" + || unoType == u"hyper" || unoType == u"unsigned hyper" || unoType == u"float" + || unoType == u"double") + { + return getRustWrapperTypeName( + unoType); // Take primitive type by value (matches C++ approach) + } + + // For all other types (typedefs, structs, enums, interfaces, sequences), use void* + return "*mut c_void"_ostr; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustproduce.hxx b/codemaker/source/rustmaker/rustproduce.hxx index b0acba537c1e..6208f5c3bae0 100644 --- a/codemaker/source/rustmaker/rustproduce.hxx +++ b/codemaker/source/rustmaker/rustproduce.hxx @@ -80,6 +80,22 @@ private: static OString getRustTypeName(std::string_view unoName); OString getRustWrapperTypeName(std::u16string_view unoType) const; OString getRustReturnType(std::u16string_view unoType) const; + OString + getRustParameterType(std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const; + OString getRustExternParameterType( + std::u16string_view unoType, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const; + + // Struct getter/setter type functions + OString getRustStructGetterReturnType(std::u16string_view unoType) const; + OString getRustStructSetterParameterType(std::u16string_view unoType) const; + OString getRustStructExternGetterReturnType(std::u16string_view unoType) const; + OString getRustStructExternSetterParameterType(std::u16string_view unoType) const; + + OString convertRustParameterForFFICall( + std::u16string_view unoType, std::string_view paramName, + unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction) const; OString resolveTypedef(std::u16string_view unoType) const; // Type classification helpers @@ -100,12 +116,15 @@ private: const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); void generateStructConstructor(class RustFile& file, std::string_view externFunctionPrefix); void generateStructFromPtr(class RustFile& file, std::string_view externFunctionPrefix); + void generateStructAsPtr(class RustFile& file); void generateStructAccessors(class RustFile& file, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity, std::string_view externFunctionPrefix); void generateStructMemberGetter(class RustFile& file, std::string_view memberName, + std::u16string_view memberType, std::string_view externFunctionPrefix); void generateStructMemberSetter(class RustFile& file, std::string_view memberName, + std::u16string_view memberType, std::string_view externFunctionPrefix); void generateStructDropTrait(class RustFile& file, std::string_view name, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); diff --git a/rust_uno/build.rs b/rust_uno/build.rs index 5f383acdbab3..cf64720a0e14 100644 --- a/rust_uno/build.rs +++ b/rust_uno/build.rs @@ -36,4 +36,3 @@ fn main() { } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ - diff --git a/rust_uno/src/examples/load_writer.rs b/rust_uno/src/examples/load_writer.rs index 575f64237005..67fc2d2c2212 100644 --- a/rust_uno/src/examples/load_writer.rs +++ b/rust_uno/src/examples/load_writer.rs @@ -7,13 +7,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::core::uno_wrapper::{UnoError, defaultBootstrap_InitialComponentContext}; -use crate::core::OUString; -use crate::generated::rustmaker::com::sun::star::frame::Desktop::Desktop; -use crate::generated::rustmaker::com::sun::star::frame::XComponentLoader::XComponentLoader; -use crate::generated::rustmaker::com::sun::star::text::XTextDocument::XTextDocument; -use crate::generated::rustmaker::com::sun::star::text::XSimpleText::XSimpleText; -use std::ffi::c_void; +use crate::{ + core::{ + OUString, + uno_wrapper::{UnoError, defaultBootstrap_InitialComponentContext}, + }, + generated::rustmaker::com::sun::star::{ + frame::{Desktop::Desktop, XComponentLoader::XComponentLoader}, + text::{XSimpleText::XSimpleText, XTextDocument::XTextDocument, XTextRange::XTextRange}, + }, +}; /// LibreOffice Writer automation demo using generated Rust UNO bindings. /// Creates a Writer document and inserts text using auto-generated interface wrappers. @@ -25,11 +28,11 @@ pub fn run() { Ok(()) => { println!("=== Rust UNO Bridge Test Done ==="); println!("<- 'load_writer' example completed successfully."); - }, + } Err(e) => { println!("=== Rust UNO Bridge Test Done ==="); eprintln!(" ERROR: 'load_writer' example failed: {e:?}"); - }, + } } } @@ -45,8 +48,12 @@ fn run_internal() -> Result<(), UnoError> { println!(" ✓ Desktop service created: {:p}", desktop.as_ptr()); // Get XComponentLoader interface - let component_loader = XComponentLoader::from_ptr(desktop.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; - println!(" ✓ XComponentLoader interface acquired: {:p}", component_loader.as_ptr()); + let component_loader = + XComponentLoader::from_ptr(desktop.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; + println!( + " ✓ XComponentLoader interface acquired: {:p}", + component_loader.as_ptr() + ); // Load Writer document println!(" Loading Writer document via XComponentLoader::loadComponentFromURL..."); @@ -55,24 +62,33 @@ fn run_internal() -> Result<(), UnoError> { let search_flags: i32 = 0; let empty_args = std::ptr::null_mut(); - let document_interface_ptr = component_loader.loadComponentFromURL( - url.as_ptr() as *mut c_void, - target.as_ptr() as *mut c_void, - &search_flags as *const i32 as *mut c_void, - empty_args, - ).ok_or(UnoError::OperationFailed)?; - + let document_interface_ptr = component_loader + .loadComponentFromURL( + url, // Direct OUString - no casting needed! + target, // Direct OUString - no casting needed! + search_flags, + empty_args, + ) + .ok_or(UnoError::OperationFailed)?; + if document_interface_ptr.as_ptr().is_null() { return Err(UnoError::OperationFailed); } - + println!(" ✓ Writer document loaded successfully!"); - println!(" Document interface pointer: {:p}", document_interface_ptr.as_ptr()); + println!( + " Document interface pointer: {:p}", + document_interface_ptr.as_ptr() + ); // Cast to XTextDocument println!(" Casting document to XTextDocument..."); - let text_document = XTextDocument::from_ptr(document_interface_ptr.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; - println!(" ✓ XTextDocument interface acquired: {:p}", text_document.as_ptr()); + let text_document = XTextDocument::from_ptr(document_interface_ptr.as_ptr()) + .ok_or(UnoError::InterfaceNotSupported)?; + println!( + " ✓ XTextDocument interface acquired: {:p}", + text_document.as_ptr() + ); // Get text object println!(" Getting text object via XTextDocument::getText..."); @@ -81,39 +97,54 @@ fn run_internal() -> Result<(), UnoError> { // Cast to XSimpleText println!(" Casting XText to XSimpleText..."); - let simple_text = XSimpleText::from_ptr(text.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; - println!(" ✓ XSimpleText interface acquired: {:p}", simple_text.as_ptr()); - + let simple_text = + XSimpleText::from_ptr(text.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; + println!( + " ✓ XSimpleText interface acquired: {:p}", + simple_text.as_ptr() + ); + // Create text cursor println!(" Creating text cursor via XSimpleText::createTextCursor..."); - let cursor = simple_text.createTextCursor().ok_or(UnoError::OperationFailed)?; + let cursor = simple_text + .createTextCursor() + .ok_or(UnoError::OperationFailed)?; println!(" ✓ Text cursor created: {:p}", cursor.as_ptr()); // Insert text println!(" Inserting text via XSimpleText::insertString..."); let hello_text = OUString::from( - "Hello from Generated Rust UNO Bindings! \ + "Hello from Generated Rust UNO Bindings with Typed Parameters! \ This text was inserted using auto-generated service and interface wrappers: \ - Desktop::create() - Generated service wrapper \ - XComponentLoader::loadComponentFromURL() - Generated interface method \ - XTextDocument::getText() - Generated interface method \ - XSimpleText::createTextCursor() - Generated interface method \ - XSimpleText::insertString() - Generated interface method \ - All types generated automatically from UNO IDL with type-safe opaque pointer architecture! \ - FFI Validation Features Demonstrated: \ + NEW: Typed Parameter API Achievements: \ + - Boolean parameters now use native 'bool' instead of void* casting \ + - Integer parameters use direct 'i32' instead of pointer arithmetic \ + - Natural Rust syntax: 'absorb as u8' vs '*ptr as *mut c_void' \ + - Direction-aware: input by value, input/output by pointer \ + - Type-safe at compile time with better IDE support \ + FFI Architecture Features: \ - Automatic null pointer checking in all generated methods \ - Reference validity validation (is() checking) \ - Memory cleanup on validation failure (delete + return nullptr) \ - Detailed debug logging for troubleshooting \ - Type-safe opaque pointer architecture prevents crashes \ - - Follows uno_bootstrap.cxx validation pattern exactly" + - Progressive migration: primitives typed, complex types remain compatible", ); - let absorb: i32 = 0; + let absorb: bool = false; + + // Convert XTextCursor to XTextRange (interface inheritance) + let text_range = + XTextRange::from_ptr(cursor.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; simple_text.insertString( - cursor.as_ptr() as *mut c_void, - hello_text.as_ptr() as *mut c_void, - &absorb as *const i32 as *mut c_void, + text_range, // Direct typed interface - no void* casting needed! + hello_text, // Direct OUString - no casting needed! + absorb as u8, // Direct boolean parameter (natural Rust syntax!) ); println!(" ✓ Text inserted successfully using generated interfaces!"); @@ -121,4 +152,4 @@ fn run_internal() -> Result<(), UnoError> { Ok(()) } -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ \ No newline at end of file +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 048761fb2219d4df7b201bf6269f8396b1639af8 Author: Mohamed Ali <[email protected]> AuthorDate: Mon Sep 22 03:46:15 2025 +0300 Commit: Stephan Bergmann <[email protected]> CommitDate: Mon Sep 29 09:57:35 2025 +0200 Rust Bindings: Refactor RustProducer and CppProducer Change-Id: I3b40c7a7eb572d95b91d1ec8eeda17a971331f67 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191476 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> diff --git a/codemaker/source/rustmaker/cpproduce.cxx b/codemaker/source/rustmaker/cpproduce.cxx index 1f0deae93e64..d027f5834c36 100644 --- a/codemaker/source/rustmaker/cpproduce.cxx +++ b/codemaker/source/rustmaker/cpproduce.cxx @@ -2012,152 +2012,8 @@ void CppProducer::generateActualMethodCall(CppFile& file, // Handle return value conversion for non-void methods if (method.returnType != u"void") { - file.beginLine().append("// Convert result to opaque void* return").endLine(); - - std::u16string_view returnType = method.returnType; - - if (returnType == u"string") - { - file.beginLine().append("return new OUString(result);").endLine(); - } - else if (returnType == u"any") - { - file.beginLine().append("return new Any(result);").endLine(); - } - else if (returnType == u"boolean") - { - file.beginLine().append("return new sal_Bool(result);").endLine(); - } - else if (returnType == u"byte") - { - file.beginLine().append("return new sal_Int8(result);").endLine(); - } - else if (returnType == u"short") - { - file.beginLine().append("return new sal_Int16(result);").endLine(); - } - else if (returnType == u"long") - { - file.beginLine().append("return new sal_Int32(result);").endLine(); - } - else if (returnType == u"hyper") - { - file.beginLine().append("return new sal_Int64(result);").endLine(); - } - else if (returnType == u"float") - { - file.beginLine().append("return new float(result);").endLine(); - } - else if (returnType == u"double") - { - file.beginLine().append("return new double(result);").endLine(); - } - else if (returnType == u"type") - { - file.beginLine().append("return new Type(result);").endLine(); - } - else if (returnType.starts_with(u"[]")) - { - // Sequence return type - file.beginLine() - .append("return new ") - .append(convertUnoTypeToCpp(returnType)) - .append("(result);") - .endLine(); - } - else - { - // For interfaces and other complex types, create appropriate wrapper - // Check if it's an interface type - rtl::Reference<unoidl::Entity> entity; - codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(returnType), &entity); - - if (sort == codemaker::UnoType::Sort::Interface) - { - // Add debug output for interface return values - file.beginLine() - .append("SAL_WARN(\"rustmaker\", \"Debug: UNO method ") - .append(method.name) - .append( - " completed, result.is() = \" << (result.is() ? \"true\" : \"false\"));") - .endLine() - .beginLine() - .append("SAL_INFO(\"rust_uno_ffi\", \"Creating new Reference<") - .append(convertUnoTypeToCpp(returnType)) - .append("> wrapper for method ") - .append(method.name) - .append("\");") - .endLine() - .beginLine() - .append("auto res = new Reference<") - .append(convertUnoTypeToCpp(returnType)) - .append(">(result);") - .endLine() - .beginLine() - .append("") - .endLine() - .beginLine() - .append("if (!res)") - .endLine() - .beginBlock() - .beginLine() - .append("SAL_WARN(\"rust_uno_ffi\", \"Failed to create Reference wrapper for ") - .append(convertUnoTypeToCpp(returnType)) - .append(" in method ") - .append(method.name) - .append("\");") - .endLine() - .beginLine() - .append("return nullptr;") - .endLine() - .endBlock() - .beginLine() - .append("") - .endLine() - .beginLine() - .append("if (!res->is())") - .endLine() - .beginBlock() - .beginLine() - .append("SAL_WARN(\"rust_uno_ffi\", \"Reference wrapper is invalid (is() = " - "false) for ") - .append(convertUnoTypeToCpp(returnType)) - .append(" in method ") - .append(method.name) - .append("\");") - .endLine() - .beginLine() - .append("delete res;") - .endLine() - .beginLine() - .append("return nullptr;") - .endLine() - .endBlock() - .beginLine() - .append("") - .endLine() - .beginLine() - .append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created valid Reference " - "wrapper for ") - .append(convertUnoTypeToCpp(returnType)) - .append(" in method ") - .append(method.name) - .append("\");") - .endLine() - .beginLine() - .append("return res;") - .endLine(); - } - else - { - // For structs and other types - file.beginLine() - .append("return new ") - .append(convertUnoTypeToCpp(returnType)) - .append("(result);") - .endLine(); - } - } + // Use helper function to eliminate code duplication + generateReturnValueConversion(file, method); } } @@ -2409,74 +2265,6 @@ OString CppProducer::getCppTypeName(std::u16string_view unoType) const return "void*"_ostr; // Default to void* for unknown types } -OString CppProducer::getRustFFITypeName(std::u16string_view unoType) const -{ - // Map UNO types to Rust FFI types - if (unoType == u"string") - return "*mut rtl_uString"_ostr; - else if (unoType == u"boolean") - return "u8"_ostr; // sal_Bool is typedef'd to u8 (unsigned char) - else if (unoType == u"byte") - return "i8"_ostr; - else if (unoType == u"short") - return "i16"_ostr; - else if (unoType == u"unsigned short") - return "u16"_ostr; - else if (unoType == u"long") - return "i32"_ostr; - else if (unoType == u"unsigned long") - return "u32"_ostr; - else if (unoType == u"hyper") - return "i64"_ostr; - else if (unoType == u"unsigned hyper") - return "u64"_ostr; - else if (unoType == u"float") - return "f32"_ostr; - else if (unoType == u"double") - return "f64"_ostr; - else if (unoType == u"void") - return "()"_ostr; - else if (unoType == u"any" || unoType == u"com.sun.star.uno.Any") - return "*mut uno_Any"_ostr; // UNO Any type - else if (isUnoStruct(unoType)) - { - // For structs, return just the struct name with underscore since we import it - OString type = u2b(unoType); - size_t lastDot = type.lastIndexOf('.'); - if (lastDot != std::string_view::npos) - return OString::Concat(type.subView(lastDot + 1)) + "_"; - else - return type + "_"; - } - else if (isUnoEnum(unoType)) - { - // For enums, return just the enum name since we import it - OString type = u2b(unoType); - size_t lastDot = type.lastIndexOf('.'); - if (lastDot != std::string_view::npos) - return OString(type.subView(lastDot + 1)); - else - return type; - } - else if (isUnoConstantGroup(unoType)) - { - // For constant groups, return the constant group name with underscore to match C++ side - OString type = u2b(unoType); - size_t lastDot = type.lastIndexOf('.'); - if (lastDot != std::string_view::npos) - return OString::Concat(type.subView(lastDot + 1)) + "_"; - else - return type + "_"; - } - else if (isUnoType(unoType)) - { - // For interfaces, return *mut XInterface pointer - return "*mut XInterface"_ostr; - } - else - return "*mut std::ffi::c_void"_ostr; // Default for unknown types -} - OString CppProducer::convertBasicType(const OString& typeName) { OString result = typeName; diff --git a/codemaker/source/rustmaker/rustproduce.cxx b/codemaker/source/rustmaker/rustproduce.cxx index e811c8787fa0..b15780a7c053 100644 --- a/codemaker/source/rustmaker/rustproduce.cxx +++ b/codemaker/source/rustmaker/rustproduce.cxx @@ -14,7 +14,7 @@ #include <iostream> #include <set> -RustProducer::RustProducer(const OString& outputDir, bool verbose, bool dryRun, +RustProducer::RustProducer(std::string_view outputDir, bool verbose, bool dryRun, const rtl::Reference<TypeManager>& typeManager) : m_outputDir(outputDir) , m_verbose(verbose) @@ -35,8 +35,17 @@ void RustProducer::produceEnum(std::string_view name, file.openFile(); + generateEnumDefinition(file, name, entity); + generateEnumImplementation(file, name); + generateEnumExternDeclarations(file, name); + + file.closeFile(); +} + +void RustProducer::generateEnumDefinition(RustFile& file, std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity) +{ OString typeName(splitName(name)); // Simple name for Rust enum - OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions file.beginLine() .append("/// Opaque Rust enum wrapper for ") @@ -79,6 +88,12 @@ void RustProducer::produceEnum(std::string_view name, } file.endBlock(); +} + +void RustProducer::generateEnumImplementation(RustFile& file, std::string_view name) +{ + OString typeName(splitName(name)); // Simple name for Rust enum + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Add conversion functions using extern "C" bridge file.beginLine() @@ -117,6 +132,12 @@ void RustProducer::produceEnum(std::string_view name, .append("}") .endLine() .endBlock(); +} + +void RustProducer::generateEnumExternDeclarations(RustFile& file, std::string_view name) +{ + OString typeName(splitName(name)); // Simple name for Rust enum + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Extern "C" declarations file.beginLine() @@ -143,8 +164,6 @@ void RustProducer::produceEnum(std::string_view name, .beginLine() .append("}") .endLine(); - - file.closeFile(); } void RustProducer::produceStruct(std::string_view name, @@ -159,8 +178,19 @@ void RustProducer::produceStruct(std::string_view name, file.openFile(); + generateStructDefinition(file, name, entity); + generateStructImplementation(file, name, entity); + generateStructDropTrait(file, name, entity); + generateStructExternDeclarations(file, name, entity); + + file.closeFile(); +} + +void RustProducer::generateStructDefinition( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& /* entity */) +{ OString typeName(splitName(name)); // Simple name for Rust struct - OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions file.beginLine() .append("/// Opaque Rust struct wrapper for ") @@ -181,6 +211,14 @@ void RustProducer::produceStruct(std::string_view name, .append("ptr: *mut c_void,") .endLine() .endBlock(); +} + +void RustProducer::generateStructImplementation( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + OString typeName(splitName(name)); // Simple name for Rust struct + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Implementation with opaque field accessors file.beginLine() @@ -192,6 +230,15 @@ void RustProducer::produceStruct(std::string_view name, .endLine() .beginBlock(); + generateStructConstructor(file, externFunctionPrefix); + generateStructFromPtr(file, externFunctionPrefix); + generateStructAccessors(file, entity, externFunctionPrefix); + + file.endBlock(); +} + +void RustProducer::generateStructConstructor(RustFile& file, std::string_view externFunctionPrefix) +{ // Constructor file.beginLine() .append("pub fn new() -> Option<Self> {") @@ -222,7 +269,10 @@ void RustProducer::produceStruct(std::string_view name, .beginLine() .append("}") .endLine(); +} +void RustProducer::generateStructFromPtr(RustFile& file, std::string_view externFunctionPrefix) +{ // from_ptr method for creating wrapper from existing pointer with C++ type casting file.beginLine() .append("") @@ -253,56 +303,76 @@ void RustProducer::produceStruct(std::string_view name, .endLine() .endBlock() .endBlock(); +} - // Opaque field accessors +void RustProducer::generateStructAccessors( + RustFile& file, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity, + std::string_view externFunctionPrefix) +{ + // Generate getters and setters for all struct members for (const auto& member : entity->getDirectMembers()) { OString memberName = u2b(member.name); // Use original case, not snake_case - // Getter - file.beginLine() - .append("") - .endLine() - .beginLine() - .append("pub fn get_") - .append(memberName) - .append("(&self) -> *mut c_void {") - .endLine() - .extraIndent() - .beginLine() - .append("unsafe { ") - .append(externFunctionPrefix) - .append("_get_") - .append(memberName) - .append("(self.ptr) }") - .endLine() - .beginLine() - .append("}") - .endLine(); - - // Setter - file.beginLine() - .append("") - .endLine() - .beginLine() - .append("pub fn set_") - .append(memberName) - .append("(&mut self, value: *mut c_void) {") - .endLine() - .extraIndent() - .beginLine() - .append("unsafe { ") - .append(externFunctionPrefix) - .append("_set_") - .append(memberName) - .append("(self.ptr, value) }") - .endLine() - .beginLine() - .append("}") - .endLine(); + generateStructMemberGetter(file, memberName, externFunctionPrefix); + generateStructMemberSetter(file, memberName, externFunctionPrefix); } +} - file.endBlock(); +void RustProducer::generateStructMemberGetter(RustFile& file, std::string_view memberName, + std::string_view externFunctionPrefix) +{ + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn get_") + .append(memberName) + .append("(&self) -> *mut c_void {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr) }") + .endLine() + .beginLine() + .append("}") + .endLine(); +} + +void RustProducer::generateStructMemberSetter(RustFile& file, std::string_view memberName, + std::string_view externFunctionPrefix) +{ + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn set_") + .append(memberName) + .append("(&mut self, value: *mut c_void) {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value) }") + .endLine() + .beginLine() + .append("}") + .endLine(); +} + +void RustProducer::generateStructDropTrait( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& /* entity */) +{ + OString typeName(splitName(name)); // Simple name for Rust struct + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Drop implementation file.beginLine() @@ -333,6 +403,13 @@ void RustProducer::produceStruct(std::string_view name, .append("}") .endLine() .endBlock(); +} + +void RustProducer::generateStructExternDeclarations( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Extern "C" declarations for opaque operations file.beginLine() @@ -341,8 +418,18 @@ void RustProducer::produceStruct(std::string_view name, .beginLine() .append("unsafe extern \"C\" {") .endLine() - .extraIndent() - .beginLine() + .extraIndent(); + + generateStructBasicExternDeclarations(file, externFunctionPrefix); + generateStructMemberExternDeclarations(file, entity, externFunctionPrefix); + + file.beginLine().append("}").endLine(); +} + +void RustProducer::generateStructBasicExternDeclarations(RustFile& file, + std::string_view externFunctionPrefix) +{ + file.beginLine() .append("fn ") .append(externFunctionPrefix) .append("_constructor() -> *mut c_void;") @@ -357,7 +444,12 @@ void RustProducer::produceStruct(std::string_view name, .append(externFunctionPrefix) .append("_from_ptr(ptr: *mut c_void) -> *mut c_void;") .endLine(); +} +void RustProducer::generateStructMemberExternDeclarations( + RustFile& file, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity, + std::string_view externFunctionPrefix) +{ for (const auto& member : entity->getDirectMembers()) { OString memberName = u2b(member.name); // Use original case, not snake_case @@ -376,10 +468,6 @@ void RustProducer::produceStruct(std::string_view name, .append("(ptr: *mut c_void, value: *mut c_void);") .endLine(); } - - file.beginLine().append("}").endLine(); - - file.closeFile(); } void RustProducer::produceInterface(std::string_view name, @@ -394,9 +482,20 @@ void RustProducer::produceInterface(std::string_view name, file.openFile(); + generateInterfaceWrapper(file, name, entity); + generateInterfaceExternDeclarations(file, name, entity); + + file.closeFile(); +} + +void RustProducer::generateInterfaceWrapper( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ OString typeName(splitName(name)); // Simple name for Rust interface - OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions + // Generate struct header and documentation file.beginLine() .append("/// Opaque Rust interface wrapper for ") .append(name) @@ -417,7 +516,20 @@ void RustProducer::produceInterface(std::string_view name, .endLine() .endBlock(); - // Implementation block + // Generate implementation block with constructor, methods, etc. + generateInterfaceImplementation(file, typeName, externFunctionPrefix, entity); + + // Generate Drop trait implementation + generateInterfaceDropTrait(file, typeName, externFunctionPrefix); + + // Generate thread safety markers + generateInterfaceThreadSafety(file, typeName); +} + +void RustProducer::generateInterfaceImplementation( + RustFile& file, std::string_view typeName, std::string_view externFunctionPrefix, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ file.beginLine() .append("") .endLine() @@ -427,7 +539,27 @@ void RustProducer::produceInterface(std::string_view name, .endLine() .beginBlock(); - // Constructor + // Generate constructor + generateInterfaceConstructor(file, externFunctionPrefix); + + // Generate from_ptr method + generateInterfaceFromPtr(file, externFunctionPrefix); + + // Generate as_ptr method + generateInterfaceAsPtr(file); + + // Generate validity check if no conflicting method + generateInterfaceValidityCheck(file, externFunctionPrefix, entity); + + // Generate method wrappers + generateInterfaceMethodWrappers(file, externFunctionPrefix, entity); + + file.endBlock(); +} + +void RustProducer::generateInterfaceConstructor(RustFile& file, + std::string_view externFunctionPrefix) +{ file.beginLine() .append("pub fn new() -> Option<Self> {") .endLine() @@ -457,8 +589,10 @@ void RustProducer::produceInterface(std::string_view name, .beginLine() .append("}") .endLine(); +} - // from_ptr method for creating wrapper from existing pointer with C++ type casting +void RustProducer::generateInterfaceFromPtr(RustFile& file, std::string_view externFunctionPrefix) +{ file.beginLine() .append("") .endLine() @@ -488,8 +622,10 @@ void RustProducer::produceInterface(std::string_view name, .endLine() .endBlock() .endBlock(); +} - // as_ptr method for getting the raw pointer +void RustProducer::generateInterfaceAsPtr(RustFile& file) +{ file.beginLine() .append("") .endLine() @@ -501,8 +637,13 @@ void RustProducer::produceInterface(std::string_view name, .append("self.ptr") .endLine() .endBlock(); +} - // Validity check - only add if there's no conflicting isValid method +void RustProducer::generateInterfaceValidityCheck( + RustFile& file, std::string_view externFunctionPrefix, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Check if there's already an isValid method to avoid conflicts bool hasIsValidMethod = false; for (const auto& method : entity->getDirectMethods()) { @@ -531,8 +672,13 @@ void RustProducer::produceInterface(std::string_view name, .append("}") .endLine(); } +} - // Method wrappers - all methods return opaque pointers +void RustProducer::generateInterfaceMethodWrappers( + RustFile& file, std::string_view externFunctionPrefix, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Generate method wrappers - all methods return opaque pointers for (const auto& method : entity->getDirectMethods()) { OString rustMethodName = getRustFunctionName(u2b(method.name)); @@ -671,112 +817,12 @@ void RustProducer::produceInterface(std::string_view name, else { // Check for typedef resolution first - OString resolvedType = resolveTypedef(method.returnType); - if (resolvedType != rustType) + OString resolvedType(resolveTypedef(method.returnType)); + // It's a typedef - handle based on resolved type + if (!generateTypeCastReturn(file, resolvedType)) { - // It's a typedef - handle based on resolved type - if (resolvedType == "boolean") - { - file.beginLine().append("unsafe { *(ptr as *const u8) }").endLine(); - } - else if (resolvedType == "byte") - { - file.beginLine().append("unsafe { *(ptr as *const i8) }").endLine(); - } - else if (resolvedType == "short") - { - file.beginLine().append("unsafe { *(ptr as *const i16) }").endLine(); - } - else if (resolvedType == "unsigned short") - { - file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); - } - else if (resolvedType == "long") - { - file.beginLine().append("unsafe { *(ptr as *const i32) }").endLine(); - } - else if (resolvedType == "unsigned long") - { - file.beginLine().append("unsafe { *(ptr as *const u32) }").endLine(); - } - else if (resolvedType == "hyper") - { - file.beginLine().append("unsafe { *(ptr as *const i64) }").endLine(); - } - else if (resolvedType == "unsigned hyper") - { - file.beginLine().append("unsafe { *(ptr as *const u64) }").endLine(); - } - else if (resolvedType == "float") - { - file.beginLine().append("unsafe { *(ptr as *const f32) }").endLine(); - } - else if (resolvedType == "double") - { - file.beginLine().append("unsafe { *(ptr as *const f64) }").endLine(); - } - else if (resolvedType == "char") - { - file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); - } - else - { - // Typedef resolves to other types - return raw pointer - file.beginLine().append("ptr").endLine(); - } - } - else - { - // Not a typedef - check for primitive types that need dereferencing - if (rustType == "boolean") - { - file.beginLine().append("unsafe { *(ptr as *const u8) }").endLine(); - } - else if (rustType == "byte") - { - file.beginLine().append("unsafe { *(ptr as *const i8) }").endLine(); - } - else if (rustType == "short") - { - file.beginLine().append("unsafe { *(ptr as *const i16) }").endLine(); - } - else if (rustType == "unsigned short") - { - file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); - } - else if (rustType == "long") - { - file.beginLine().append("unsafe { *(ptr as *const i32) }").endLine(); - } - else if (rustType == "unsigned long") - { - file.beginLine().append("unsafe { *(ptr as *const u32) }").endLine(); - } - else if (rustType == "hyper") - { - file.beginLine().append("unsafe { *(ptr as *const i64) }").endLine(); - } - else if (rustType == "unsigned hyper") - { - file.beginLine().append("unsafe { *(ptr as *const u64) }").endLine(); - } - else if (rustType == "float") - { - file.beginLine().append("unsafe { *(ptr as *const f32) }").endLine(); - } - else if (rustType == "double") - { - file.beginLine().append("unsafe { *(ptr as *const f64) }").endLine(); - } - else if (rustType == "char") - { - file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); - } - else - { - // For other types (sequences, templates, etc), return the raw pointer - file.beginLine().append("ptr").endLine(); - } + // Typedef resolves to non-primitive types - return raw pointer + file.beginLine().append("ptr").endLine(); } } } @@ -784,10 +830,11 @@ void RustProducer::produceInterface(std::string_view name, file.beginLine().append("}").endLine(); } +} - file.endBlock(); - - // Drop implementation for automatic cleanup +void RustProducer::generateInterfaceDropTrait(RustFile& file, std::string_view typeName, + std::string_view externFunctionPrefix) +{ file.beginLine() .append("") .endLine() @@ -816,7 +863,10 @@ void RustProducer::produceInterface(std::string_view name, .append("}") .endLine() .endBlock(); +} +void RustProducer::generateInterfaceThreadSafety(RustFile& file, std::string_view typeName) +{ // Thread safety markers file.beginLine() .append("") @@ -831,6 +881,13 @@ void RustProducer::produceInterface(std::string_view name, .append(typeName) .append(" {}") .endLine(); +} + +void RustProducer::generateInterfaceExternDeclarations( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + OString externFunctionPrefix(getRustTypeName(name)); // Extern "C" declarations - connects to C-side bridge file.beginLine() @@ -856,6 +913,17 @@ void RustProducer::produceInterface(std::string_view name, .append("_from_ptr(ptr: *mut c_void) -> *mut c_void;") .endLine(); + // Check if there's already an isValid method to avoid conflicts + bool hasIsValidMethod = false; + for (const auto& method : entity->getDirectMethods()) + { + if (u2b(method.name).equalsIgnoreAsciiCase("isValid")) + { + hasIsValidMethod = true; + break; + } + } + // Only declare _is_valid if we don't have a conflicting isValid method if (!hasIsValidMethod) { @@ -895,8 +963,6 @@ void RustProducer::produceInterface(std::string_view name, } file.beginLine().append("}").endLine(); - - file.closeFile(); } void RustProducer::produceService( @@ -911,19 +977,18 @@ void RustProducer::produceService( file.openFile(); - OString serviceName(splitName(name)); // Simple name for Rust service - OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions - OUString interfaceType = entity->getBase(); + generateServiceDefinition(file, name, entity); + generateServiceImplementation(file, name, entity); + generateServiceExternDeclarations(file, name, entity); - // Generate proper module path for the interface - OString interfaceTypeStr = u2b(interfaceType); - std::string_view interfaceTypeName = splitName(interfaceTypeStr); + file.closeFile(); +} - // Convert interface type to full module path - OString path = interfaceTypeStr; - path = path.replaceAll("."_ostr, "::"_ostr); - OString interfaceModulePath - = "crate::generated::rustmaker::"_ostr + path + "::" + interfaceTypeName; +void RustProducer::generateServiceDefinition( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& /* entity */) +{ + OString serviceName(splitName(name)); // Simple name for Rust service file.beginLine() .append("/// Opaque Rust service wrapper for ") @@ -942,6 +1007,13 @@ void RustProducer::produceService( .append(serviceName) .append(";") .endLine(); +} + +void RustProducer::generateServiceImplementation( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + OString serviceName(splitName(name)); // Simple name for Rust service // Implementation block file.beginLine() @@ -953,6 +1025,21 @@ void RustProducer::produceService( .endLine() .beginBlock(); + generateServiceCreateMethod(file, name, entity); + + file.endBlock(); +} + +void RustProducer::generateServiceCreateMethod( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions + OUString interfaceType = entity->getBase(); + + // Generate proper module path for the interface + OString interfaceModulePath = generateServiceInterfaceModulePath(interfaceType); + // Service creation method file.beginLine() .append("/// Create a new instance of ") @@ -987,8 +1074,25 @@ void RustProducer::produceService( .endLine() .endBlock() .endBlock(); +} - file.endBlock(); +OString RustProducer::generateServiceInterfaceModulePath(const OUString& interfaceType) +{ + // Generate proper module path for the interface + OString interfaceTypeStr = u2b(interfaceType); + std::string_view interfaceTypeName = splitName(interfaceTypeStr); + + // Convert interface type to full module path + OString path = interfaceTypeStr; + path = path.replaceAll("."_ostr, "::"_ostr); + return "crate::generated::rustmaker::"_ostr + path + "::" + interfaceTypeName; +} + +void RustProducer::generateServiceExternDeclarations( + RustFile& file, std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& /* entity */) +{ + OString externFunctionPrefix(getRustTypeName(name)); // Full name for extern "C" functions // Extern "C" declarations file.beginLine() @@ -1004,8 +1108,68 @@ void RustProducer::produceService( .append("_create(context: *mut c_void) -> *mut c_void;") .endLine() .endBlock(); +} - file.closeFile(); +bool RustProducer::generateTypeCastReturn(RustFile& file, std::string_view resolvedType) const +{ + if (resolvedType == "boolean") + { + file.beginLine().append("unsafe { *(ptr as *const u8) }").endLine(); + return true; + } + else if (resolvedType == "byte") + { + file.beginLine().append("unsafe { *(ptr as *const i8) }").endLine(); + return true; + } + else if (resolvedType == "short") + { + file.beginLine().append("unsafe { *(ptr as *const i16) }").endLine(); + return true; + } + else if (resolvedType == "unsigned short") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + return true; + } + else if (resolvedType == "long") + { + file.beginLine().append("unsafe { *(ptr as *const i32) }").endLine(); + return true; + } + else if (resolvedType == "unsigned long") + { + file.beginLine().append("unsafe { *(ptr as *const u32) }").endLine(); + return true; + } + else if (resolvedType == "hyper") + { + file.beginLine().append("unsafe { *(ptr as *const i64) }").endLine(); + return true; + } + else if (resolvedType == "unsigned hyper") + { + file.beginLine().append("unsafe { *(ptr as *const u64) }").endLine(); + return true; + } + else if (resolvedType == "float") + { + file.beginLine().append("unsafe { *(ptr as *const f32) }").endLine(); + return true; + } + else if (resolvedType == "double") + { + file.beginLine().append("unsafe { *(ptr as *const f64) }").endLine(); + return true; + } + else if (resolvedType == "char") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + return true; + } + + // Return false if no type casting was generated (not a primitive type) + return false; } // Helper functions @@ -1110,7 +1274,7 @@ OString RustProducer::getRustWrapperTypeName(std::u16string_view unoType) const } // First resolve typedefs - OString resolvedType = resolveTypedef(unoType); + OString resolvedType(resolveTypedef(unoType)); if (resolvedType != rustType) { // It's a typedef - recursively get wrapper type for resolved type @@ -1186,27 +1350,6 @@ OString RustProducer::getRustReturnType(std::u16string_view unoType) const return getRustWrapperTypeName(unoType); } -OString RustProducer::getRustFFIReturnType(std::u16string_view unoType) const -{ - // Handle void returns - if (unoType == u"void") - { - return "()"_ostr; - } - - // For interfaces, structs, enums - return their opaque handle typedef - if (isUnoInterface(unoType) || isUnoStruct(unoType) || isUnoEnum(unoType)) - { - // Convert UNO type name to typedef handle name - // com.sun.star.lang.XMain -> com__sun__star__lang__XMainHandle - OString functionPrefix = getRustTypeName(std::string(unoType.begin(), unoType.end())); - return functionPrefix + "Handle"; - } - - // For other types (primitives, sequences, etc) - use void* - return "*mut c_void"_ostr; -} - bool RustProducer::isUnoInterface(std::u16string_view typeName) const { rtl::Reference<unoidl::Entity> entity; diff --git a/codemaker/source/rustmaker/rustproduce.hxx b/codemaker/source/rustmaker/rustproduce.hxx index 3c2d0fb2cbfe..b0acba537c1e 100644 --- a/codemaker/source/rustmaker/rustproduce.hxx +++ b/codemaker/source/rustmaker/rustproduce.hxx @@ -37,7 +37,7 @@ public: * @param dryRun Don't actually write files * @param typeManager UNO type manager for accessing type information */ - RustProducer(const OString& outputDir, bool verbose, bool dryRun, + RustProducer(std::string_view outputDir, bool verbose, bool dryRun, const rtl::Reference<TypeManager>& typeManager); /** @@ -80,13 +80,83 @@ private: static OString getRustTypeName(std::string_view unoName); OString getRustWrapperTypeName(std::u16string_view unoType) const; OString getRustReturnType(std::u16string_view unoType) const; - OString getRustFFIReturnType(std::u16string_view unoType) const; OString resolveTypedef(std::u16string_view unoType) const; // Type classification helpers bool isUnoInterface(std::u16string_view typeName) const; bool isUnoStruct(std::u16string_view typeName) const; bool isUnoEnum(std::u16string_view typeName) const; + + // Enum generation helpers (following CppProducer pattern) + void generateEnumDefinition(class RustFile& file, std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity); + void generateEnumImplementation(class RustFile& file, std::string_view name); + void generateEnumExternDeclarations(class RustFile& file, std::string_view name); + + // Struct generation helpers (following CppProducer pattern) + void generateStructDefinition(class RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateStructImplementation(class RustFile& file, std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateStructConstructor(class RustFile& file, std::string_view externFunctionPrefix); + void generateStructFromPtr(class RustFile& file, std::string_view externFunctionPrefix); + void generateStructAccessors(class RustFile& file, -e ... etc. - the rest is truncated
