Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/cppuhelper/source/component_context.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <unordered_map>
21
22
#include <osl/diagnose.h>
23
24
#include <sal/log.hxx>
25
26
#include <uno/lbnames.h>
27
#include <uno/mapping.hxx>
28
29
#include <cppuhelper/component_context.hxx>
30
#include <cppuhelper/implbase.hxx>
31
#include <compbase2.hxx>
32
33
#include <com/sun/star/container/XNameContainer.hpp>
34
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
35
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
36
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
37
#include <com/sun/star/lang/XComponent.hpp>
38
#include <com/sun/star/beans/XPropertySet.hpp>
39
#include <com/sun/star/uno/DeploymentException.hpp>
40
#include <com/sun/star/uno/RuntimeException.hpp>
41
42
#include <comphelper/sequence.hxx>
43
44
#include <memory>
45
#include <utility>
46
47
constexpr OUString SMGR_SINGLETON = u"/singletons/com.sun.star.lang.theServiceManager"_ustr;
48
constexpr OUStringLiteral TDMGR_SINGLETON = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager";
49
constexpr OUStringLiteral AC_SINGLETON = u"/singletons/com.sun.star.security.theAccessController";
50
51
using namespace ::com::sun::star::uno;
52
using namespace ::com::sun::star;
53
54
namespace cppu
55
{
56
57
static void try_dispose( std::unique_lock<std::mutex>& rGuard, Reference< XInterface > const & xInstance )
58
0
{
59
0
    Reference< lang::XComponent > xComp( xInstance, UNO_QUERY );
60
0
    if (xComp.is())
61
0
    {
62
0
        rGuard.unlock();
63
0
        xComp->dispose();
64
0
        rGuard.lock();
65
0
    }
66
0
}
67
68
static void try_dispose( std::unique_lock<std::mutex>& rGuard, Reference< lang::XComponent > const & xComp )
69
0
{
70
0
    if (xComp.is())
71
0
    {
72
0
        rGuard.unlock();
73
0
        xComp->dispose();
74
0
        rGuard.lock();
75
0
    }
76
0
}
77
78
namespace {
79
80
class DisposingForwarder
81
    : public WeakImplHelper< lang::XEventListener >
82
{
83
    Reference< lang::XComponent > m_xTarget;
84
85
    explicit DisposingForwarder( Reference< lang::XComponent > const & xTarget )
86
0
        : m_xTarget( xTarget )
87
0
    {
88
0
        OSL_ASSERT( m_xTarget.is() );
89
0
    }
90
public:
91
    // listens at source for disposing, then disposes target
92
    static inline void listen(
93
        Reference< lang::XComponent > const & xSource,
94
        Reference< lang::XComponent > const & xTarget );
95
96
    virtual void SAL_CALL disposing( lang::EventObject const & rSource ) override;
97
};
98
99
}
100
101
inline void DisposingForwarder::listen(
102
    Reference< lang::XComponent > const & xSource,
103
    Reference< lang::XComponent > const & xTarget )
104
108
{
105
108
    if (xSource.is())
106
0
    {
107
0
        xSource->addEventListener( new DisposingForwarder( xTarget ) );
108
0
    }
109
108
}
110
111
void DisposingForwarder::disposing( lang::EventObject const & )
112
0
{
113
0
    m_xTarget->dispose();
114
0
    m_xTarget.clear();
115
0
}
116
117
namespace {
118
119
class ComponentContext
120
    : public cppuhelper::WeakComponentImplHelper2< XComponentContext,
121
                                      container::XNameContainer >
122
{
123
protected:
124
    Reference< XComponentContext > m_xDelegate;
125
126
    struct ContextEntry
127
    {
128
        Any value;
129
        bool lateInit;
130
131
        ContextEntry( Any value_, bool lateInit_ )
132
7.02k
            : value(std::move( value_ ))
133
7.02k
            , lateInit( lateInit_ )
134
7.02k
            {}
135
    };
136
    typedef std::unordered_map< OUString, ContextEntry  > t_map;
137
    t_map m_map;
138
139
    Reference< lang::XMultiComponentFactory > m_xSMgr;
140
141
protected:
142
    Any lookupMap( OUString const & rName );
143
144
    virtual void disposing(std::unique_lock<std::mutex>&) override;
145
public:
146
    ComponentContext(
147
        ContextEntry_Init const * pEntries, sal_Int32 nEntries,
148
        Reference< XComponentContext > const & xDelegate );
149
150
    // XComponentContext
151
    virtual Any SAL_CALL getValueByName( OUString const & rName ) override;
152
    virtual Reference<lang::XMultiComponentFactory> SAL_CALL getServiceManager() override;
153
154
    // XNameContainer
155
    virtual void SAL_CALL insertByName(
156
        OUString const & name, Any const & element ) override;
157
    virtual void SAL_CALL removeByName( OUString const & name ) override;
158
    // XNameReplace
159
    virtual void SAL_CALL replaceByName(
160
        OUString const & name, Any const & element ) override;
161
    // XNameAccess
162
    virtual Any SAL_CALL getByName( OUString const & name ) override;
163
    virtual Sequence<OUString> SAL_CALL getElementNames() override;
164
    virtual sal_Bool SAL_CALL hasByName( OUString const & name ) override;
165
    // XElementAccess
166
    virtual Type SAL_CALL getElementType() override;
167
    virtual sal_Bool SAL_CALL hasElements() override;
168
};
169
170
}
171
172
// XNameContainer
173
174
void ComponentContext::insertByName(
175
    OUString const & name, Any const & element )
176
0
{
177
0
    ContextEntry entry(
178
0
            element,
179
            /* lateInit_: */
180
0
            name.startsWith( "/singletons/" ) &&
181
0
            !element.hasValue() );
182
0
    std::unique_lock guard( m_aMutex );
183
0
    std::pair<t_map::iterator, bool> insertion( m_map.emplace(
184
0
        name, entry ) );
185
0
    if (! insertion.second)
186
0
        throw container::ElementExistException(
187
0
            "element already exists: " + name,
188
0
            static_cast<OWeakObject *>(this) );
189
0
}
190
191
192
void ComponentContext::removeByName( OUString const & name )
193
0
{
194
0
    std::unique_lock guard( m_aMutex );
195
0
    t_map::iterator iFind( m_map.find( name ) );
196
0
    if (iFind == m_map.end())
197
0
        throw container::NoSuchElementException(
198
0
            "no such element: " + name,
199
0
            static_cast<OWeakObject *>(this) );
200
201
0
    m_map.erase(iFind);
202
0
}
203
204
// XNameReplace
205
206
void ComponentContext::replaceByName(
207
    OUString const & name, Any const & element )
208
0
{
209
0
    std::unique_lock guard( m_aMutex );
210
0
    t_map::iterator iFind( m_map.find( name ) );
211
0
    if (iFind == m_map.end())
212
0
        throw container::NoSuchElementException(
213
0
            "no such element: " + name,
214
0
            static_cast<OWeakObject *>(this) );
215
0
    if (name.startsWith( "/singletons/" ) &&
216
0
        !element.hasValue())
217
0
    {
218
0
        iFind->second.value.clear();
219
0
        iFind->second.lateInit = true;
220
0
    }
221
0
    else
222
0
    {
223
0
        iFind->second.value = element;
224
0
        iFind->second.lateInit = false;
225
0
    }
226
0
}
227
228
// XNameAccess
229
230
Any ComponentContext::getByName( OUString const & name )
231
0
{
232
0
    return getValueByName( name );
233
0
}
234
235
236
Sequence<OUString> ComponentContext::getElementNames()
237
0
{
238
0
    std::unique_lock guard( m_aMutex );
239
0
    return comphelper::mapKeysToSequence(m_map);
240
0
}
241
242
243
sal_Bool ComponentContext::hasByName( OUString const & name )
244
0
{
245
0
    std::unique_lock guard( m_aMutex );
246
0
    return m_map.contains( name );
247
0
}
248
249
// XElementAccess
250
251
Type ComponentContext::getElementType()
252
0
{
253
0
    return cppu::UnoType<void>::get();
254
0
}
255
256
257
sal_Bool ComponentContext::hasElements()
258
0
{
259
0
    std::unique_lock guard( m_aMutex );
260
0
    return ! m_map.empty();
261
0
}
262
263
264
Any ComponentContext::lookupMap( OUString const & rName )
265
87.1k
{
266
87.1k
    std::unique_lock guard( m_aMutex );
267
87.1k
    t_map::iterator iFind( m_map.find( rName ) );
268
87.1k
    if (iFind == m_map.end())
269
57
        return Any();
270
271
87.0k
    ContextEntry& rFindEntry = iFind->second;
272
87.0k
    if (! rFindEntry.lateInit)
273
86.9k
        return rFindEntry.value;
274
275
    // late init singleton entry
276
57
    Reference< XInterface > xInstance;
277
57
    guard.unlock();
278
279
57
    try
280
57
    {
281
57
        Any usesService( getValueByName( rName + "/service" ) );
282
57
        Any args_( getValueByName( rName + "/arguments" ) );
283
57
        Sequence<Any> args;
284
57
        if (args_.hasValue() && !(args_ >>= args))
285
0
        {
286
0
            args = { args_ };
287
0
        }
288
289
57
        Reference< lang::XSingleComponentFactory > xFac;
290
57
        if (usesService >>= xFac) // try via factory
291
57
        {
292
57
            xInstance = args.hasElements()
293
57
                ? xFac->createInstanceWithArgumentsAndContext( args, this )
294
57
                : xFac->createInstanceWithContext( this );
295
57
        }
296
0
        else
297
0
        {
298
0
            Reference< lang::XSingleServiceFactory > xFac2;
299
0
            if (usesService >>= xFac2)
300
0
            {
301
                // try via old XSingleServiceFactory
302
0
                xInstance = args.hasElements()
303
0
                    ? xFac2->createInstanceWithArguments( args )
304
0
                    : xFac2->createInstance();
305
0
            }
306
0
            else if (m_xSMgr.is()) // optionally service name
307
0
            {
308
0
                OUString serviceName;
309
0
                if ((usesService >>= serviceName) &&
310
0
                    !serviceName.isEmpty())
311
0
                {
312
0
                    xInstance = args.hasElements()
313
0
                        ? m_xSMgr->createInstanceWithArgumentsAndContext(
314
0
                            serviceName, args, this )
315
0
                        : m_xSMgr->createInstanceWithContext(
316
0
                            serviceName, this );
317
0
                }
318
0
            }
319
0
        }
320
57
    }
321
57
    catch (const RuntimeException &)
322
57
    {
323
0
        throw;
324
0
    }
325
57
    catch (const Exception & exc)
326
57
    {
327
14
        SAL_WARN(
328
14
            "cppuhelper",
329
14
            "exception occurred raising singleton \"" << rName << "\": "
330
14
            << exc);
331
14
    }
332
333
57
    SAL_WARN_IF(!xInstance.is(),
334
57
            "cppuhelper", "no service object raising singleton " << rName);
335
336
57
    Any ret;
337
57
    guard.lock();
338
57
    iFind = m_map.find( rName );
339
57
    if (iFind != m_map.end())
340
57
    {
341
57
        ContextEntry & rEntry = iFind->second;
342
57
        if (rEntry.lateInit)
343
57
        {
344
57
            rEntry.value <<= xInstance;
345
57
            rEntry.lateInit = false;
346
57
            return rEntry.value;
347
57
        }
348
0
        ret = rEntry.value;
349
0
    }
350
0
    if (ret != xInstance) {
351
0
        try_dispose( guard, xInstance );
352
0
    }
353
0
    return ret;
354
57
}
355
356
357
Any ComponentContext::getValueByName( OUString const & rName )
358
87.1k
{
359
    // to determine the root context:
360
87.1k
    if ( rName == "_root" )
361
0
    {
362
0
        if (m_xDelegate.is())
363
0
            return m_xDelegate->getValueByName( rName );
364
0
        return Any( Reference<XComponentContext>(this) );
365
0
    }
366
367
87.1k
    Any ret( lookupMap( rName ) );
368
87.1k
    if (!ret.hasValue() && m_xDelegate.is())
369
0
    {
370
0
        return m_xDelegate->getValueByName( rName );
371
0
    }
372
87.1k
    return ret;
373
87.1k
}
374
375
Reference< lang::XMultiComponentFactory > ComponentContext::getServiceManager()
376
17.5M
{
377
17.5M
    if ( !m_xSMgr.is() )
378
0
    {
379
0
        throw DeploymentException(
380
0
            u"null component context service manager"_ustr,
381
0
            static_cast<OWeakObject *>(this) );
382
0
    }
383
17.5M
    return m_xSMgr;
384
17.5M
}
385
386
void ComponentContext::disposing(std::unique_lock<std::mutex>& rGuard)
387
0
{
388
0
    Reference< lang::XComponent > xTDMgr, xAC; // to be disposed separately
389
390
    // dispose all context objects
391
0
    for ( auto& [rName, rEntry] : m_map )
392
0
    {
393
        // service manager disposed separately
394
0
        if (!m_xSMgr.is() ||
395
0
            !rName.startsWith( SMGR_SINGLETON ))
396
0
        {
397
0
            if (rEntry.lateInit)
398
0
            {
399
0
                rEntry.value.clear(); // release factory
400
0
                rEntry.lateInit = false;
401
0
                continue;
402
0
            }
403
404
0
            Reference< lang::XComponent > xComp;
405
0
            rEntry.value >>= xComp;
406
0
            if (xComp.is())
407
0
            {
408
0
                if ( rName == TDMGR_SINGLETON )
409
0
                {
410
0
                    xTDMgr = std::move(xComp);
411
0
                }
412
0
                else if ( rName == AC_SINGLETON )
413
0
                {
414
0
                    xAC = std::move(xComp);
415
0
                }
416
0
                else // dispose immediately
417
0
                {
418
0
                    rGuard.unlock();
419
0
                    xComp->dispose();
420
0
                    rGuard.lock();
421
0
                }
422
0
            }
423
0
        }
424
0
    }
425
426
    // dispose service manager
427
0
    try_dispose( rGuard, m_xSMgr );
428
0
    m_xSMgr.clear();
429
    // dispose ac
430
0
    try_dispose( rGuard, xAC );
431
    // dispose tdmgr; revokes callback from cppu runtime
432
0
    try_dispose( rGuard, xTDMgr );
433
434
0
    m_map.clear();
435
436
    // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI
437
    // proxies get finalized with arbitrary delay, so the bridge typically does
438
    // not dispose itself early enough before the process exits):
439
0
    uno_Environment ** envs;
440
0
    sal_Int32 envCount;
441
0
    uno_getRegisteredEnvironments(
442
0
        &envs, &envCount, &rtl_allocateMemory, u"java"_ustr.pData);
443
0
    assert(envCount >= 0);
444
0
    assert(envCount == 0 || envs != nullptr);
445
0
    if (envs) {
446
0
        for (sal_Int32 i = 0; i != envCount; ++i) {
447
0
            assert(envs[i] != nullptr);
448
0
            assert(envs[i]->dispose != nullptr);
449
0
            (*envs[i]->dispose)(envs[i]);
450
0
        }
451
0
        std::free(envs);
452
0
    }
453
0
}
454
455
ComponentContext::ComponentContext(
456
    ContextEntry_Init const * pEntries, sal_Int32 nEntries,
457
    Reference< XComponentContext > const & xDelegate )
458
108
    : m_xDelegate( xDelegate )
459
108
{
460
3.78k
    for ( sal_Int32 nPos = 0; nPos < nEntries; ++nPos )
461
3.67k
    {
462
3.67k
        ContextEntry_Init const & rEntry = pEntries[ nPos ];
463
464
3.67k
        if ( rEntry.m_sName == SMGR_SINGLETON )
465
108
        {
466
108
            rEntry.m_aValue >>= m_xSMgr;
467
108
        }
468
469
3.67k
        if (rEntry.m_bLateInitService)
470
3.34k
        {
471
            // singleton entry
472
3.34k
            m_map.emplace( rEntry.m_sName, ContextEntry( Any(), true ) );
473
            // service
474
3.34k
            m_map.emplace( rEntry.m_sName + "/service", ContextEntry( rEntry.m_aValue, false ) );
475
            // initial-arguments are provided as optional context entry
476
3.34k
        }
477
324
        else
478
324
        {
479
            // only value, no late init factory nor string
480
324
            m_map.emplace( rEntry.m_sName, ContextEntry( rEntry.m_aValue, false ) );
481
324
        }
482
3.67k
    }
483
484
108
    if (m_xSMgr.is() || !m_xDelegate.is())
485
108
        return;
486
487
    // wrap delegate's smgr XPropertySet into new smgr
488
0
    Reference< lang::XMultiComponentFactory > xMgr( m_xDelegate->getServiceManager() );
489
0
    if (!xMgr.is())
490
0
        return;
491
492
0
    osl_atomic_increment( &m_refCount );
493
0
    try
494
0
    {
495
        // create new smgr based on delegate's one
496
0
        m_xSMgr.set(
497
0
            xMgr->createInstanceWithContext(
498
0
                u"com.sun.star.comp.stoc.OServiceManagerWrapper"_ustr, xDelegate ),
499
0
            UNO_QUERY );
500
        // patch DefaultContext property of new one
501
0
        Reference< beans::XPropertySet > xProps( m_xSMgr, UNO_QUERY );
502
0
        OSL_ASSERT( xProps.is() );
503
0
        if (xProps.is())
504
0
        {
505
0
            Reference< XComponentContext > xThis( this );
506
0
            xProps->setPropertyValue( u"DefaultContext"_ustr, Any( xThis ) );
507
0
        }
508
0
    }
509
0
    catch (...)
510
0
    {
511
0
        osl_atomic_decrement( &m_refCount );
512
0
        throw;
513
0
    }
514
0
    osl_atomic_decrement( &m_refCount );
515
0
    OSL_ASSERT( m_xSMgr.is() );
516
0
}
517
518
519
extern "C" { static void s_createComponentContext_v(va_list * pParam)
520
108
{
521
108
    ContextEntry_Init const  * pEntries     = va_arg(*pParam, ContextEntry_Init const *);
522
108
    sal_Int32                  nEntries     = va_arg(*pParam, sal_Int32);
523
108
    XComponentContext        * pDelegatee   = va_arg(*pParam, XComponentContext *);
524
108
    void                    ** ppContext    = va_arg(*pParam, void **);
525
108
    uno::Mapping             * pTarget2curr = va_arg(*pParam, uno::Mapping *);
526
527
108
    Reference<XComponentContext> xDelegate(pDelegatee, SAL_NO_ACQUIRE);
528
108
    Reference<XComponentContext> xContext;
529
530
108
    if (nEntries > 0)
531
108
    {
532
108
        try
533
108
        {
534
108
            ComponentContext * p = new ComponentContext( pEntries, nEntries, xDelegate );
535
108
            xContext.set(p);
536
            // listen delegate for disposing, to dispose this (wrapping) context first.
537
108
            DisposingForwarder::listen( Reference< lang::XComponent >::query( xDelegate ), p );
538
108
        }
539
108
        catch (Exception & exc)
540
108
        {
541
0
            SAL_WARN( "cppuhelper", exc );
542
0
            xContext.clear();
543
0
        }
544
108
    }
545
0
    else
546
0
    {
547
0
        xContext = std::move(xDelegate);
548
0
    }
549
550
108
    *ppContext = pTarget2curr->mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get());
551
108
}}
552
553
Reference< XComponentContext > SAL_CALL createComponentContext(
554
    ContextEntry_Init const * pEntries, sal_Int32 nEntries,
555
    Reference< XComponentContext > const & xDelegate )
556
108
{
557
108
    uno::Environment curr_env(Environment::getCurrent());
558
108
    uno::Environment source_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME);
559
560
108
    uno::Mapping curr2source(curr_env, source_env);
561
108
    uno::Mapping source2curr(source_env, curr_env);
562
563
108
    std::unique_ptr<ContextEntry_Init[]> mapped_entries(new ContextEntry_Init[nEntries]);
564
3.78k
    for (sal_Int32 nPos = 0; nPos < nEntries; ++ nPos)
565
3.67k
    {
566
3.67k
        mapped_entries[nPos].m_bLateInitService = pEntries[nPos].m_bLateInitService;
567
3.67k
        mapped_entries[nPos].m_sName            = pEntries[nPos].m_sName;
568
569
3.67k
        uno_type_any_constructAndConvert(&mapped_entries[nPos].m_aValue,
570
3.67k
                                         const_cast<void *>(pEntries[nPos].m_aValue.getValue()),
571
3.67k
                                         pEntries[nPos].m_aValue.getValueTypeRef(),
572
3.67k
                                         curr2source.get());
573
3.67k
    }
574
575
108
    void * mapped_delegate = curr2source.mapInterface(xDelegate.get(), cppu::UnoType<decltype(xDelegate)>::get());
576
108
    XComponentContext * pXComponentContext = nullptr;
577
108
    source_env.invoke(s_createComponentContext_v, mapped_entries.get(), nEntries, mapped_delegate, &pXComponentContext, &source2curr);
578
108
    mapped_entries.reset();
579
580
108
    return Reference<XComponentContext>(pXComponentContext, SAL_NO_ACQUIRE);
581
108
}
582
583
}
584
585
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */