summaryrefslogtreecommitdiff
path: root/src/vm/extensibleclassfactory.cpp
blob: 2b51650dcef20763e7a7a7f19244afc36943e06f (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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*============================================================
**
** Header: ExtensibleClassFactory.cpp
**
**
** Purpose: Native methods on System.Runtime.InteropServices.ExtensibleClassFactory
**

** 
===========================================================*/

#include "common.h"

#include "excep.h"
#include "stackwalk.h"
#include "extensibleclassfactory.h"


// Helper function used to walk stack frames looking for a class initializer.
static StackWalkAction FrameCallback(CrawlFrame *pCF, void *pData)
{
    _ASSERTE(NULL != pCF);
    MethodDesc *pMD = pCF->GetFunction();

    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_ANY;
        PRECONDITION(CheckPointer(pMD));
        PRECONDITION(CheckPointer(pData, NULL_OK));
        PRECONDITION(pMD->GetMethodTable() != NULL);
    }
    CONTRACTL_END;
    

    // We use the pData context argument to track the class as we move down the
    // stack and to return the class whose initializer is being called. If
    // *ppMT is NULL we are looking at the caller's initial frame and just
    // record the class that the method belongs to. From that point on the class
    // must remain the same until we hit a class initializer or else we must
    // fail (to prevent other classes called from a class initializer from
    // setting the current classes callback). The very first class we will see
    // belongs to RegisterObjectCreationCallback itself, so skip it (we set
    // *ppMT to an initial value of -1 to detect this).
    MethodTable **ppMT = (MethodTable **)pData;

    if (*ppMT == (MethodTable *)-1)
        *ppMT = NULL;
    
    else if (*ppMT == NULL)
        *ppMT = pMD->GetMethodTable();
    
    else if (pMD->GetMethodTable() != *ppMT)
    {
        *ppMT = NULL;
        return SWA_ABORT;
    }

    if (pMD->IsClassConstructor())
        return SWA_ABORT;

    return SWA_CONTINUE;
}


// Register a delegate that will be called whenever an instance of a
// managed type that extends from an unmanaged type needs to allocate
// the aggregated unmanaged object. This delegate is expected to
// allocate and aggregate the unmanaged object and is called in place
// of a CoCreateInstance. This routine must be called in the context
// of the static initializer for the class for which the callbacks
// will be made.
// It is not legal to register this callback from a class that has any
// parents that have already registered a callback.
FCIMPL1(void, RegisterObjectCreationCallback, Object* pDelegateUNSAFE)
{
    FCALL_CONTRACT;

    OBJECTREF orDelegate = (OBJECTREF) pDelegateUNSAFE;
    HELPER_METHOD_FRAME_BEGIN_1(orDelegate);

    // Validate the delegate argument.
    if (orDelegate == 0)
        COMPlusThrowArgumentNull(W("callback"));

    // We should have been called in the context of a class static initializer.
    // Walk back up the stack to verify this and to determine just what class
    // we're registering a callback for.
    MethodTable *pMT = (MethodTable *)-1;
    if (GetThread()->StackWalkFrames(FrameCallback, &pMT, FUNCTIONSONLY, NULL) == SWA_FAILED)
        COMPlusThrow(kInvalidOperationException, IDS_EE_CALLBACK_NOT_CALLED_FROM_CCTOR);

    // If we didn't find a class initializer, we can't continue.
    if (pMT == NULL)
    {
        COMPlusThrow(kInvalidOperationException, IDS_EE_CALLBACK_NOT_CALLED_FROM_CCTOR);
    }
    
    // The object type must derive at some stage from a COM imported object.
    // Also we must fail the call if some parent class has already registered a
    // callback.
    MethodTable *pParent = pMT;
    do 
    {
        pParent = pParent->GetParentMethodTable();
        if (pParent && !pParent->IsComImport() && (pParent->GetObjCreateDelegate() != NULL))
        {
            COMPlusThrow(kInvalidOperationException, IDS_EE_CALLBACK_ALREADY_REGISTERED);
        }
    } 
    while (pParent && !pParent->IsComImport());

    // If the class does not have a COM imported base class then fail the call.
    if (pParent == NULL || pParent->IsProjectedFromWinRT())
    {
        COMPlusThrow(kInvalidOperationException, IDS_EE_CALLBACK_NOT_CALLED_FROM_CCTOR);
    }

    // Save the delegate in the MethodTable for the class.
    pMT->SetObjCreateDelegate(orDelegate);

    HELPER_METHOD_FRAME_END();
}
FCIMPLEND