/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace emscripten; using namespace css::uno; template <> struct emscripten::smart_ptr_trait { 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(dest) + desc->pMemberOffsets[i], const_cast(static_cast(source) + desc->pMemberOffsets[i]), desc->ppTypeRefs[i], cpp_acquire); } } Reference 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 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() }; case TypeClass_BYTE: return Any{ rObject.as() }; case TypeClass_SHORT: return Any{ rObject.as() }; case TypeClass_UNSIGNED_SHORT: return Any{ rObject.as() }; case TypeClass_LONG: return Any{ rObject.as() }; case TypeClass_UNSIGNED_LONG: return Any{ rObject.as() }; case TypeClass_HYPER: return Any{ rObject.as() }; case TypeClass_UNSIGNED_HYPER: return Any{ rObject.as() }; case TypeClass_FLOAT: return Any{ rObject.as() }; case TypeClass_DOUBLE: return Any{ rObject.as() }; case TypeClass_CHAR: return Any{ rObject.as() }; case TypeClass_STRING: return Any{ OUString(rObject.as()) }; case TypeClass_TYPE: return css::uno::Any(rObject.as()); 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(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(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_("uno_Sequence") .value("FromSize", unoembindhelpers::uno_Sequence::FromSize); emscripten::class_("uno_Type") .smart_ptr("uno_Type$") .class_function("Void", +[]() { return cppu::UnoType::get(); }) .class_function("Boolean", +[]() { return cppu::UnoType::get(); }) .class_function("Byte", +[]() { return cppu::UnoType::get(); }) .class_function("Short", +[]() { return cppu::UnoType::get(); }) .class_function("UnsignedShort", +[]() { return cppu::UnoType::get(); }) .class_function("Long", +[]() { return cppu::UnoType::get(); }) .class_function("UnsignedLong", +[]() { return cppu::UnoType::get(); }) .class_function("Hyper", +[]() { return cppu::UnoType::get(); }) .class_function("UnsignedHyper", +[]() { return cppu::UnoType::get(); }) .class_function("Float", +[]() { return cppu::UnoType::get(); }) .class_function("Double", +[]() { return cppu::UnoType::get(); }) .class_function("Char", +[]() { return cppu::UnoType::get(); }) .class_function("String", +[]() { return cppu::UnoType::get(); }) .class_function("Type", +[]() { return cppu::UnoType::get(); }) .class_function("Any", +[]() { return cppu::UnoType::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(&desc)); if (!desc.is()) { throw std::invalid_argument("bad sequence type"); } assert(desc.get()->eTypeClass == typelib_TypeClass_SEQUENCE); return css::uno::Type( reinterpret_cast(desc.get())->pType); }) .function("toString", +[](css::uno::Type const& self) { auto const name = self.getTypeName(); return std::u16string(name.getStr(), name.getLength()); }); // Any class_("uno_Any") .constructor(&constructAny) .function("getType", &css::uno::Any::getValueType) .function("get", +[](css::uno::Any const& self) { switch (self.getValueType().getTypeClass()) { case css::uno::TypeClass_VOID: return emscripten::val::undefined(); case css::uno::TypeClass_BOOLEAN: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_BYTE: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_SHORT: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_UNSIGNED_SHORT: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_LONG: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_UNSIGNED_LONG: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_HYPER: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_UNSIGNED_HYPER: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_FLOAT: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_DOUBLE: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_CHAR: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_STRING: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_TYPE: return emscripten::val(*o3tl::forceAccess(self)); case css::uno::TypeClass_SEQUENCE: { auto const seq = *static_cast(self.getValue()); auto const copy = std::malloc(sizeof(uno_Sequence*)); *static_cast(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(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(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(self.getValue()); auto const copy = std::malloc(sizeof(css::uno::XInterface*)); *static_cast(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) { cppu::throwException(constructAny(type, value)); }); function("sameUnoObject", +[](css::uno::Reference const& ref1, css::uno::Reference const& ref2) { return ref1 == ref2; }); function("rtl_uString_release", +[](std::uintptr_t ptr) { rtl_uString_release(reinterpret_cast(ptr)); }); function("getUnoExceptionFromCxaException", +[](std::uintptr_t ptr) { // Cf. __get_exception_message in , // 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::get().getTypeLibType(), cpp_acquire); } else { uno_any_construct(&exc, reinterpret_cast(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: */