1
0
Fork 0
libreoffice/static/source/unoembindhelpers/PrimaryBindings.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

454 lines
18 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifdef EMSCRIPTEN
#include <com/sun/star/frame/XModel.hpp>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <bridges/emscriptencxxabi/cxxabi.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/TypeClass.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <comphelper/processfactory.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <o3tl/any.hxx>
#include <o3tl/temporary.hxx>
#include <o3tl/unreachable.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sfx2/viewsh.hxx>
#include <static/unoembindhelpers/PrimaryBindings.hxx>
#include <typelib/typeclass.h>
#include <typelib/typedescription.h>
#include <typelib/typedescription.hxx>
#include <uno/data.h>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <utility>
using namespace emscripten;
using namespace css::uno;
template <> struct emscripten::smart_ptr_trait<css::uno::Type>
{
using PointerType = css::uno::Type;
using element_type = typelib_TypeDescriptionReference;
static typelib_TypeDescriptionReference* get(css::uno::Type const& ptr)
{
return ptr.getTypeLibType();
}
static sharing_policy get_sharing_policy() { return sharing_policy::NONE; }
static css::uno::Type* share(typelib_TypeDescriptionReference* v)
{
return new css::uno::Type(v);
}
static css::uno::Type* construct_null() { return new css::uno::Type(); }
};
EM_JS(void, jsRegisterChar, (std::type_info const* raw),
// clang-format off
{
Module.registerType(raw, {
name: 'sal_Unicode',
fromWireType(ptr) {
let str = String.fromCharCode(Module.HEAPU16[ptr >> 1]);
return str;
},
toWireType(destructors, value) {
if (typeof value != 'string' || value.length !== 1) {
Module.throwBindingError(
'Cannot pass anything but 1-element string to C++ char16_t');
}
let data = Module._malloc(2);
Module.HEAPU16[data >> 1] = value.charCodeAt(0);
if (destructors !== null) {
destructors.push(Module._free, data);
}
return data;
},
argPackAdvance: 8,
readValueFromPointer(pointer) {
return this.fromWireType(Module.HEAPU32[((pointer)>>2)]);
},
destructorFunction(ptr) {
Module._free(ptr);
},
});
}
// clang-format on
);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winvalid-pp-token"
EM_JS(void, jsRegisterString, (std::type_info const* raw),
// clang-format off
{
Module.registerType(raw, {
name: 'rtl::OUString',
fromWireType(ptr) {
let data = Module.HEAPU32[ptr >> 2];
let length = Module.HEAPU32[(data >> 2) + 1];
let buffer = data + 8;
let str = '';
for (let i = 0; i < length; ++i) {
let c = Module.HEAPU16[(buffer >> 1) + i];
str += String.fromCharCode(c);
}
Module.rtl_uString_release(data);
Module._free(ptr);
return str;
},
toWireType(destructors, value) {
if (typeof value != 'string') {
Module.throwBindingError('Cannot pass non-string to C++ OUString');
}
let data = Module._malloc(8 + (value.length + 1) * 2);
Module.HEAPU32[data >> 2] = 1;
Module.HEAPU32[(data >> 2) + 1] = value.length;
let buffer = data + 8;
for (let i = 0; i < value.length; ++i) {
Module.HEAPU16[(buffer >> 1) + i] = value.charCodeAt(i);
}
Module.HEAPU16[(buffer >> 1) + value.length] = 0;
let ptr = Module._malloc(4);
Module.HEAPU32[ptr >> 2] = data;
if (destructors !== null) {
destructors.push(Module._free, ptr);
}
return ptr;
},
argPackAdvance: 8,
readValueFromPointer(pointer) {
return this.fromWireType(Module.HEAPU32[((pointer)>>2)]);
},
destructorFunction(ptr) {
Module._free(ptr);
},
});
}
// clang-format on
);
#pragma clang diagnostic pop
namespace
{
void copyStruct(typelib_CompoundTypeDescription* desc, void const* source, void* dest)
{
if (desc->pBaseTypeDescription != nullptr)
{
copyStruct(desc->pBaseTypeDescription, source, dest);
}
for (sal_Int32 i = 0; i != desc->nMembers; ++i)
{
uno_type_copyData(
static_cast<char*>(dest) + desc->pMemberOffsets[i],
const_cast<char*>(static_cast<char const*>(source) + desc->pMemberOffsets[i]),
desc->ppTypeRefs[i], cpp_acquire);
}
}
Reference<css::frame::XModel> getCurrentModelFromViewSh()
{
SfxViewShell* pSh = nullptr;
pSh = SfxViewShell::Current();
if (!pSh)
{
return {};
}
return pSh->GetCurrentDocument();
}
struct LessType
{
bool operator()(css::uno::Type const& type1, css::uno::Type const& type2) const
{
return type1.getTypeLibType() < type2.getTypeLibType();
}
};
std::map<css::uno::Type, std::type_info const*, LessType> unoTypes;
std::type_info const* getTypeId(css::uno::Type const& type)
{
auto const i = unoTypes.find(type);
if (i == unoTypes.end())
{
throw std::runtime_error("unregistered UNO type");
}
return i->second;
}
Any constructAny(const css::uno::Type& rUnoType, const val& rObject)
{
switch (rUnoType.getTypeClass())
{
case TypeClass_VOID:
return {};
case TypeClass_BOOLEAN:
return Any{ rObject.as<bool>() };
case TypeClass_BYTE:
return Any{ rObject.as<sal_Int8>() };
case TypeClass_SHORT:
return Any{ rObject.as<sal_Int16>() };
case TypeClass_UNSIGNED_SHORT:
return Any{ rObject.as<sal_uInt16>() };
case TypeClass_LONG:
return Any{ rObject.as<sal_Int32>() };
case TypeClass_UNSIGNED_LONG:
return Any{ rObject.as<sal_uInt32>() };
case TypeClass_HYPER:
return Any{ rObject.as<sal_Int64>() };
case TypeClass_UNSIGNED_HYPER:
return Any{ rObject.as<sal_uInt64>() };
case TypeClass_FLOAT:
return Any{ rObject.as<float>() };
case TypeClass_DOUBLE:
return Any{ rObject.as<double>() };
case TypeClass_CHAR:
return Any{ rObject.as<char16_t>() };
case TypeClass_STRING:
return Any{ OUString(rObject.as<std::u16string>()) };
case TypeClass_TYPE:
return css::uno::Any(rObject.as<css::uno::Type>());
case TypeClass_SEQUENCE:
case TypeClass_STRUCT:
case TypeClass_EXCEPTION:
case TypeClass_INTERFACE:
{
emscripten::internal::EM_DESTRUCTORS destructors = nullptr;
emscripten::internal::EM_GENERIC_WIRE_TYPE result
= _emval_as(rObject.as_handle(), getTypeId(rUnoType), &destructors);
emscripten::internal::DestructorsRunner dr(destructors);
return css::uno::Any(emscripten::internal::fromGenericWireType<void const*>(result),
rUnoType);
}
case TypeClass_ENUM:
{
emscripten::internal::EM_DESTRUCTORS destructors = nullptr;
emscripten::internal::EM_GENERIC_WIRE_TYPE result
= _emval_as(rObject.as_handle(), getTypeId(rUnoType), &destructors);
emscripten::internal::DestructorsRunner dr(destructors);
return css::uno::Any(
&o3tl::temporary(emscripten::internal::fromGenericWireType<sal_Int32>(result)),
rUnoType);
}
default:
throw std::invalid_argument("bad type class");
}
}
}
namespace unoembindhelpers::detail
{
void registerUnoType(css::uno::Type const& type, std::type_info const* id) { unoTypes[type] = id; }
}
EMSCRIPTEN_BINDINGS(PrimaryBindings)
{
enum_<unoembindhelpers::uno_Sequence>("uno_Sequence")
.value("FromSize", unoembindhelpers::uno_Sequence::FromSize);
emscripten::class_<typelib_TypeDescriptionReference>("uno_Type")
.smart_ptr<css::uno::Type>("uno_Type$")
.class_function("Void", +[]() { return cppu::UnoType<void>::get(); })
.class_function("Boolean", +[]() { return cppu::UnoType<bool>::get(); })
.class_function("Byte", +[]() { return cppu::UnoType<sal_Int8>::get(); })
.class_function("Short", +[]() { return cppu::UnoType<sal_Int16>::get(); })
.class_function("UnsignedShort", +[]() { return cppu::UnoType<sal_uInt16>::get(); })
.class_function("Long", +[]() { return cppu::UnoType<sal_Int32>::get(); })
.class_function("UnsignedLong", +[]() { return cppu::UnoType<sal_uInt32>::get(); })
.class_function("Hyper", +[]() { return cppu::UnoType<sal_Int64>::get(); })
.class_function("UnsignedHyper", +[]() { return cppu::UnoType<sal_uInt64>::get(); })
.class_function("Float", +[]() { return cppu::UnoType<float>::get(); })
.class_function("Double", +[]() { return cppu::UnoType<double>::get(); })
.class_function("Char", +[]() { return cppu::UnoType<sal_Unicode>::get(); })
.class_function("String", +[]() { return cppu::UnoType<OUString>::get(); })
.class_function("Type", +[]() { return cppu::UnoType<css::uno::Type>::get(); })
.class_function("Any", +[]() { return cppu::UnoType<css::uno::Any>::get(); })
.class_function("Sequence",
+[](css::uno::Type const& type) {
return css::uno::Type(css::uno::TypeClass_SEQUENCE,
"[]" + type.getTypeName());
})
.class_function("Enum",
+[](std::u16string const& name) {
return css::uno::Type(css::uno::TypeClass_ENUM, OUString(name));
})
.class_function("Struct",
+[](std::u16string const& name) {
return css::uno::Type(css::uno::TypeClass_STRUCT, OUString(name));
})
.class_function("Exception",
+[](std::u16string const& name) {
return css::uno::Type(css::uno::TypeClass_EXCEPTION, OUString(name));
})
.class_function("Interface",
+[](std::u16string const& name) {
return css::uno::Type(css::uno::TypeClass_INTERFACE, OUString(name));
})
.function("getTypeClass", +[](css::uno::Type const& self) { return self.getTypeClass(); })
.function(
"getSequenceComponentType",
+[](css::uno::Type const& self) {
if (self.getTypeClass() != css::uno::TypeClass_SEQUENCE)
{
throw std::invalid_argument("bad non-sequence type");
}
css::uno::TypeDescription desc;
self.getDescription(reinterpret_cast<typelib_TypeDescription**>(&desc));
if (!desc.is())
{
throw std::invalid_argument("bad sequence type");
}
assert(desc.get()->eTypeClass == typelib_TypeClass_SEQUENCE);
return css::uno::Type(
reinterpret_cast<typelib_IndirectTypeDescription const*>(desc.get())->pType);
})
.function("toString", +[](css::uno::Type const& self) {
auto const name = self.getTypeName();
return std::u16string(name.getStr(), name.getLength());
});
// Any
class_<Any>("uno_Any")
.constructor(&constructAny)
.function("getType", &css::uno::Any::getValueType)
.function("get", +[](css::uno::Any const& self) {
switch (self.getValueTypeClass())
{
case css::uno::TypeClass_VOID:
return emscripten::val::undefined();
case css::uno::TypeClass_BOOLEAN:
return emscripten::val(*o3tl::forceAccess<bool>(self));
case css::uno::TypeClass_BYTE:
return emscripten::val(*o3tl::forceAccess<sal_Int8>(self));
case css::uno::TypeClass_SHORT:
return emscripten::val(*o3tl::forceAccess<sal_Int16>(self));
case css::uno::TypeClass_UNSIGNED_SHORT:
return emscripten::val(*o3tl::forceAccess<sal_uInt16>(self));
case css::uno::TypeClass_LONG:
return emscripten::val(*o3tl::forceAccess<sal_Int32>(self));
case css::uno::TypeClass_UNSIGNED_LONG:
return emscripten::val(*o3tl::forceAccess<sal_uInt32>(self));
case css::uno::TypeClass_HYPER:
return emscripten::val(*o3tl::forceAccess<sal_Int64>(self));
case css::uno::TypeClass_UNSIGNED_HYPER:
return emscripten::val(*o3tl::forceAccess<sal_uInt64>(self));
case css::uno::TypeClass_FLOAT:
return emscripten::val(*o3tl::forceAccess<float>(self));
case css::uno::TypeClass_DOUBLE:
return emscripten::val(*o3tl::forceAccess<double>(self));
case css::uno::TypeClass_CHAR:
return emscripten::val(*o3tl::forceAccess<sal_Unicode>(self));
case css::uno::TypeClass_STRING:
return emscripten::val(*o3tl::forceAccess<OUString>(self));
case css::uno::TypeClass_TYPE:
return emscripten::val(*o3tl::forceAccess<css::uno::Type>(self));
case css::uno::TypeClass_SEQUENCE:
{
auto const seq = *static_cast<uno_Sequence* const*>(self.getValue());
auto const copy = std::malloc(sizeof(uno_Sequence*));
*static_cast<uno_Sequence**>(copy) = seq;
osl_atomic_increment(&seq->nRefCount);
emscripten::internal::WireTypePack argv(std::move(copy));
return emscripten::val::take_ownership(
_emval_take_value(getTypeId(self.getValueType()), argv));
}
case css::uno::TypeClass_ENUM:
{
emscripten::internal::WireTypePack argv(
std::move(*static_cast<sal_Int32 const*>(self.getValue())));
return emscripten::val::take_ownership(
_emval_take_value(getTypeId(self.getValueType()), argv));
}
case css::uno::TypeClass_STRUCT:
case css::uno::TypeClass_EXCEPTION:
{
css::uno::TypeDescription desc(self.getValueType().getTypeLibType());
assert(desc.is());
auto const td = reinterpret_cast<typelib_CompoundTypeDescription*>(desc.get());
auto const copy = std::malloc(td->aBase.nSize);
copyStruct(td, self.getValue(), copy);
emscripten::internal::WireTypePack argv(std::move(copy));
return emscripten::val::take_ownership(
_emval_take_value(getTypeId(self.getValueType()), argv));
}
case css::uno::TypeClass_INTERFACE:
{
auto const ifc = *static_cast<css::uno::XInterface* const*>(self.getValue());
auto const copy = std::malloc(sizeof(css::uno::XInterface*));
*static_cast<css::uno::XInterface**>(copy) = ifc;
if (ifc != nullptr)
{
ifc->acquire();
}
emscripten::internal::WireTypePack argv(std::move(copy));
return emscripten::val::take_ownership(
_emval_take_value(getTypeId(self.getValueType()), argv));
}
default:
O3TL_UNREACHABLE;
};
});
function("getCurrentModelFromViewSh", &getCurrentModelFromViewSh);
function("getUnoComponentContext", &comphelper::getProcessComponentContext);
function("throwUnoException", +[](css::uno::Type const& type, emscripten::val const& value,
emscripten::val const& toDelete) {
auto const any = constructAny(type, value);
auto const len = toDelete["length"].as<std::size_t>();
for (std::size_t i = 0; i != len; ++i)
{
toDelete[i].call<void>("delete");
}
cppu::throwException(any);
});
function("sameUnoObject",
+[](css::uno::Reference<css::uno::XInterface> const& ref1,
css::uno::Reference<css::uno::XInterface> const& ref2) { return ref1 == ref2; });
function("rtl_uString_release",
+[](std::uintptr_t ptr) { rtl_uString_release(reinterpret_cast<rtl_uString*>(ptr)); });
function("getUnoExceptionFromCxaException", +[](std::uintptr_t ptr) {
// Cf. __get_exception_message in <https://github.com/emscripten-core/emscripten/>,
// system/lib/libcxxabi/src/cxa_exception_js_utils.cpp:
auto const header = reinterpret_cast<__cxxabiv1::__cxa_exception const*>(ptr) - 1;
css::uno::Any exc;
OUString unoName(emscriptencxxabi::toUnoName(header->exceptionType->name()));
typelib_TypeDescription* td = nullptr;
typelib_typedescription_getByName(&td, unoName.pData);
if (td == nullptr)
{
css::uno::RuntimeException e("exception type not found: " + unoName);
uno_type_any_construct(
&exc, &e, cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(),
cpp_acquire);
}
else
{
uno_any_construct(&exc, reinterpret_cast<void*>(ptr), td, cpp_acquire);
typelib_typedescription_release(td);
}
return exc;
});
jsRegisterChar(&typeid(char16_t));
jsRegisterString(&typeid(OUString));
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */