Coverage Report

Created: 2025-07-07 10:01

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