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") { - SAL_INFO("codemaker", "Detected UNO interface type, using pointer assignment"); - // For interface types, assign the pointer directly without dereferencing - // The C++ struct expects Reference, but we assign the raw interface pointer - file.append(" = reinterpret_cast<") - .append(convertUnoTypeToCpp(unoType)) - .append("*>(value);"); + // For primitive types with typed parameters, assign directly (no dereferencing) + file.append(" = value;"); + } + else if (unoType == u"string") + { + // For string types, dereference the pointer to OUString + file.append(" = *reinterpret_cast(value);"); + } + else if (unoType == u"any") + { + // For any types with typed parameters, dereference the uno_Any* + file.append(" = *reinterpret_cast(value);"); } else { - SAL_INFO("codemaker", "Non-interface type, using dereferenced assignment"); - // For non-interface types, dereference the pointer as before - file.append(" = *reinterpret_cast(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(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(").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(").append(param.name).append(")"); + // Output parameter: rtl_uString** needs dereferencing to OUString reference + file.append("*reinterpret_cast(*").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(").append(param.name).append(")"); + } else - file.append("*reinterpret_cast(").append(param.name).append(")"); + { + // Output parameter: uno_Any** needs dereferencing to Any reference + file.append("*reinterpret_cast(*").append(param.name).append(")"); + } } - else if (paramType == u"boolean") + // 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(").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(").append(param.name).append(")"); - } - else if (paramType == u"byte") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); - } - else if (paramType == u"short") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); - } - else if (paramType == u"long") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); - } - else if (paramType == u"hyper") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); - } - else if (paramType == u"float") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); - } - else if (paramType == u"double") - { - if (isInputOnly) - file.append("*reinterpret_cast(").append(param.name).append(")"); - else - file.append("*reinterpret_cast(").append(param.name).append(")"); + { + // Fallback to void* approach (shouldn't happen for primitives, but just in case) + if (isInputOnly) + file.append("*reinterpret_cast(") + .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& 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 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& 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& 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& 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!\n\n\ + "Hello from Generated Rust UNO Bindings with Typed Parameters! \n\n\ This text was inserted using auto-generated service and interface wrappers:\n\n\ - Desktop::create() - Generated service wrapper\n\ - XComponentLoader::loadComponentFromURL() - Generated interface method\n\ - XTextDocument::getText() - Generated interface method\n\ - XSimpleText::createTextCursor() - Generated interface method\n\ - XSimpleText::insertString() - Generated interface method\n\n\ - All types generated automatically from UNO IDL with type-safe opaque pointer architecture!\n\n\ - FFI Validation Features Demonstrated:\n\ + NEW: Typed Parameter API Achievements:\n\ + - Boolean parameters now use native 'bool' instead of void* casting\n\ + - Integer parameters use direct 'i32' instead of pointer arithmetic\n\ + - Natural Rust syntax: 'absorb as u8' vs '*ptr as *mut c_void'\n\ + - Direction-aware: input by value, input/output by pointer\n\ + - Type-safe at compile time with better IDE support\n\n\ + FFI Architecture Features:\n\ - Automatic null pointer checking in all generated methods\n\ - Reference validity validation (is() checking) \n\ - Memory cleanup on validation failure (delete + return nullptr)\n\ - Detailed debug logging for troubleshooting\n\ - Type-safe opaque pointer architecture prevents crashes\n\ - - 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: */