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 <stephan.bergmann@collabora.com>
This commit is contained in:
Mohamed Ali
2025-09-29 09:57:43 +02:00
committed by Stephan Bergmann
parent 048761fb22
commit 86ade63863
6 changed files with 943 additions and 151 deletions
+264 -84
View File
@@ -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<Interface>, 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<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(")");
{
// Output parameter: uno_Any** needs dereferencing to Any reference
file.append("*reinterpret_cast<Any*>(*").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<const sal_Bool*>(").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<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(")");
}
else if (paramType == u"double")
{
if (isInputOnly)
file.append("*reinterpret_cast<const double*>(").append(param.name).append(")");
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;
+5
View File
@@ -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;
+589 -31
View File
@@ -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: */
@@ -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);
-1
View File
@@ -36,4 +36,3 @@ fn main() {
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+66 -35
View File
@@ -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: */
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */