summaryrefslogtreecommitdiffstats
path: root/comphelper/source/misc/compbase.cxx
blob: d88a534777f1e2a3cb884900c475f1a2a5cd0e62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/* -*- 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/.
 */

#include <comphelper/compbase.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

namespace comphelper
{
WeakComponentImplHelperBase::~WeakComponentImplHelperBase() {}

// css::lang::XComponent
void SAL_CALL WeakComponentImplHelperBase::dispose()
{
    std::unique_lock aGuard(m_aMutex);
    if (m_bDisposed)
        return;
    m_bDisposed = true;
    disposing(aGuard);
    if (!aGuard.owns_lock())
        aGuard.lock();
    css::lang::EventObject aEvt(static_cast<OWeakObject*>(this));
    maEventListeners.disposeAndClear(aGuard, aEvt);
}

void WeakComponentImplHelperBase::disposing(std::unique_lock<std::mutex>&) {}

void SAL_CALL WeakComponentImplHelperBase::addEventListener(
    css::uno::Reference<css::lang::XEventListener> const& rxListener)
{
    std::unique_lock aGuard(m_aMutex);
    if (m_bDisposed)
        return;
    maEventListeners.addInterface(aGuard, rxListener);
}

void SAL_CALL WeakComponentImplHelperBase::removeEventListener(
    css::uno::Reference<css::lang::XEventListener> const& rxListener)
{
    std::unique_lock aGuard(m_aMutex);
    maEventListeners.removeInterface(aGuard, rxListener);
}

css::uno::Any SAL_CALL WeakComponentImplHelperBase::queryInterface(css::uno::Type const& rType)
{
    css::uno::Any aReturn = ::cppu::queryInterface(rType, static_cast<css::uno::XWeak*>(this),
                                                   static_cast<css::lang::XComponent*>(this));
    if (aReturn.hasValue())
        return aReturn;
    return OWeakObject::queryInterface(rType);
}

static void checkInterface(css::uno::Type const& rType)
{
    if (css::uno::TypeClass_INTERFACE != rType.getTypeClass())
    {
        OUString msg("querying for interface \"" + rType.getTypeName() + "\": no interface type!");
        SAL_WARN("cppuhelper", msg);
        throw css::uno::RuntimeException(msg);
    }
}

static bool isXInterface(rtl_uString* pStr)
{
    return OUString::unacquired(&pStr) == "com.sun.star.uno.XInterface";
}

static bool td_equals(typelib_TypeDescriptionReference const* pTDR1,
                      typelib_TypeDescriptionReference const* pTDR2)
{
    return ((pTDR1 == pTDR2)
            || OUString::unacquired(&pTDR1->pTypeName) == OUString::unacquired(&pTDR2->pTypeName));
}

static cppu::type_entry* getTypeEntries(cppu::class_data* cd)
{
    cppu::type_entry* pEntries = cd->m_typeEntries;
    if (!cd->m_storedTypeRefs) // not inited?
    {
        static std::mutex aMutex;
        std::scoped_lock guard(aMutex);
        if (!cd->m_storedTypeRefs) // not inited?
        {
            // get all types
            for (sal_Int32 n = cd->m_nTypes; n--;)
            {
                cppu::type_entry* pEntry = &pEntries[n];
                css::uno::Type const& rType = (*pEntry->m_type.getCppuType)(nullptr);
                OSL_ENSURE(rType.getTypeClass() == css::uno::TypeClass_INTERFACE,
                           "### wrong helper init: expected interface!");
                OSL_ENSURE(
                    !isXInterface(rType.getTypeLibType()->pTypeName),
                    "### want to implement XInterface: template argument is XInterface?!?!?!");
                if (rType.getTypeClass() != css::uno::TypeClass_INTERFACE)
                {
                    OUString msg("type \"" + rType.getTypeName() + "\" is no interface type!");
                    SAL_WARN("cppuhelper", msg);
                    throw css::uno::RuntimeException(msg);
                }
                // ref is statically held by getCppuType()
                pEntry->m_type.typeRef = rType.getTypeLibType();
            }
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
            cd->m_storedTypeRefs = true;
        }
    }
    else
    {
        OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
    }
    return pEntries;
}

static void* makeInterface(sal_IntPtr nOffset, void* that)
{
    return (static_cast<char*>(that) + nOffset);
}

static bool recursivelyFindType(typelib_TypeDescriptionReference const* demandedType,
                                typelib_InterfaceTypeDescription const* type, sal_IntPtr* offset)
{
    // This code assumes that the vtables of a multiple-inheritance class (the
    // offset amount by which to adjust the this pointer) follow one another in
    // the object layout, and that they contain slots for the inherited classes
    // in a specific order.  In theory, that need not hold for any given
    // platform; in practice, it seems to work well on all supported platforms:
next:
    for (sal_Int32 i = 0; i < type->nBaseTypes; ++i)
    {
        if (i > 0)
        {
            *offset += sizeof(void*);
        }
        typelib_InterfaceTypeDescription const* base = type->ppBaseTypes[i];
        // ignore XInterface:
        if (base->nBaseTypes > 0)
        {
            if (td_equals(reinterpret_cast<typelib_TypeDescriptionReference const*>(base),
                          demandedType))
            {
                return true;
            }
            // Profiling showed that it is important to speed up the common case
            // of only one base:
            if (type->nBaseTypes == 1)
            {
                type = base;
                goto next;
            }
            if (recursivelyFindType(demandedType, base, offset))
            {
                return true;
            }
        }
    }
    return false;
}

static void* queryDeepNoXInterface(typelib_TypeDescriptionReference const* pDemandedTDR,
                                   cppu::class_data* cd, void* that)
{
    cppu::type_entry* pEntries = getTypeEntries(cd);
    sal_Int32 nTypes = cd->m_nTypes;
    sal_Int32 n;

    // try top interfaces without getting td
    for (n = 0; n < nTypes; ++n)
    {
        if (td_equals(pEntries[n].m_type.typeRef, pDemandedTDR))
        {
            return makeInterface(pEntries[n].m_offset, that);
        }
    }
    // query deep getting td
    for (n = 0; n < nTypes; ++n)
    {
        typelib_TypeDescription* pTD = nullptr;
        TYPELIB_DANGER_GET(&pTD, pEntries[n].m_type.typeRef);
        if (pTD)
        {
            // exclude top (already tested) and bottom (XInterface) interface
            OSL_ENSURE(reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD)->nBaseTypes > 0,
                       "### want to implement XInterface:"
                       " template argument is XInterface?!?!?!");
            sal_IntPtr offset = pEntries[n].m_offset;
            bool found = recursivelyFindType(
                pDemandedTDR, reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD), &offset);
            TYPELIB_DANGER_RELEASE(pTD);
            if (found)
            {
                return makeInterface(offset, that);
            }
        }
        else
        {
            OUString msg("cannot get type description for type \""
                         + OUString::unacquired(&pEntries[n].m_type.typeRef->pTypeName) + "\"!");
            SAL_WARN("cppuhelper", msg);
            throw css::uno::RuntimeException(msg);
        }
    }
    return nullptr;
}

css::uno::Any WeakComponentImplHelper_query(css::uno::Type const& rType, cppu::class_data* cd,
                                            WeakComponentImplHelperBase* pBase)
{
    checkInterface(rType);
    typelib_TypeDescriptionReference* pTDR = rType.getTypeLibType();

    // shortcut XInterface to WeakComponentImplHelperBase
    if (!isXInterface(pTDR->pTypeName))
    {
        void* p = queryDeepNoXInterface(pTDR, cd, pBase);
        if (p)
        {
            return css::uno::Any(&p, pTDR);
        }
    }
    return pBase->comphelper::WeakComponentImplHelperBase::queryInterface(rType);
}

} // namespace comphelper

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */