summaryrefslogtreecommitdiff
path: root/src/vm/extensibleclassfactory.cpp
blob: 5ffb5752d668ecc36747d28204c134b7a69da448 (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
// 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;
        SO_TOLERANT;
        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