/
VersioningHelper.cs
172 lines (146 loc) · 6.89 KB
/
VersioningHelper.cs
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace System.Runtime.Versioning
{
[Flags]
internal enum SxSRequirements
{
None = 0,
AppDomainID = 0x1,
ProcessID = 0x2,
CLRInstanceID = 0x4, // for multiple CLR's within the process
AssemblyName = 0x8,
TypeName = 0x10
}
public static partial class VersioningHelper
{
// These depend on the exact values given to members of the ResourceScope enum.
private const ResourceScope ResTypeMask = ResourceScope.Machine | ResourceScope.Process | ResourceScope.AppDomain | ResourceScope.Library;
private const ResourceScope VisibilityMask = ResourceScope.Private | ResourceScope.Assembly;
public static string MakeVersionSafeName(string? name, ResourceScope from, ResourceScope to)
{
return MakeVersionSafeName(name, from, to, type: null);
}
public static string MakeVersionSafeName(string? name, ResourceScope from, ResourceScope to, Type? type)
{
ResourceScope fromResType = from & ResTypeMask;
ResourceScope toResType = to & ResTypeMask;
if (fromResType > toResType)
throw new ArgumentException(SR.Format(SR.Argument_ResourceScopeWrongDirection, fromResType, toResType), nameof(from));
SxSRequirements requires = GetRequirements(to, from);
if ((requires & (SxSRequirements.AssemblyName | SxSRequirements.TypeName)) != 0 && type == null)
throw new ArgumentNullException(nameof(type), SR.ArgumentNull_TypeRequiredByResourceScope);
// Add in process ID, CLR base address, and appdomain ID's. Also, use a character identifier
// to ensure that these can never accidentally overlap (ie, you create enough appdomains and your
// appdomain ID might equal your process ID).
StringBuilder safeName = new StringBuilder(name);
char separator = '_';
if ((requires & SxSRequirements.ProcessID) != 0)
{
safeName.Append(separator);
safeName.Append('p');
safeName.Append(Environment.ProcessId);
}
if ((requires & SxSRequirements.CLRInstanceID) != 0)
{
string clrID = GetCLRInstanceString();
safeName.Append(separator);
safeName.Append('r');
safeName.Append(clrID);
}
if ((requires & SxSRequirements.AppDomainID) != 0)
{
safeName.Append(separator);
safeName.Append("ad");
safeName.Append(AppDomain.CurrentDomain.Id);
}
if ((requires & SxSRequirements.TypeName) != 0)
{
safeName.Append(separator);
safeName.Append(type!.Name);
}
if ((requires & SxSRequirements.AssemblyName) != 0)
{
safeName.Append(separator);
safeName.Append(type!.Assembly.FullName);
}
return safeName.ToString();
}
private static string GetCLRInstanceString()
{
// We are going to hardcode the value here to 3 (a random number) so that we don't have to
// actually call GetRuntimeId() which is an ecall method and cannot be
// directly called from outside of the corelib.
// In CoreCLR, GetRuntimeId() gets the TLS index for the thread and adds 3 to that number.
return "3";
}
private static SxSRequirements GetRequirements(ResourceScope consumeAsScope, ResourceScope calleeScope)
{
SxSRequirements requires = SxSRequirements.None;
switch (calleeScope & ResTypeMask)
{
case ResourceScope.Machine:
switch (consumeAsScope & ResTypeMask)
{
case ResourceScope.Machine:
// No work
break;
case ResourceScope.Process:
requires |= SxSRequirements.ProcessID;
break;
case ResourceScope.AppDomain:
requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID | SxSRequirements.ProcessID;
break;
default:
throw new ArgumentException(SR.Format(SR.Argument_BadResourceScopeTypeBits, consumeAsScope), nameof(consumeAsScope));
}
break;
case ResourceScope.Process:
if ((consumeAsScope & ResourceScope.AppDomain) != 0)
requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID;
break;
case ResourceScope.AppDomain:
// No work
break;
default:
throw new ArgumentException(SR.Format(SR.Argument_BadResourceScopeTypeBits, calleeScope), nameof(calleeScope));
}
switch (calleeScope & VisibilityMask)
{
case ResourceScope.None: // Public - implied
switch (consumeAsScope & VisibilityMask)
{
case ResourceScope.None: // Public - implied
// No work
break;
case ResourceScope.Assembly:
requires |= SxSRequirements.AssemblyName;
break;
case ResourceScope.Private:
requires |= SxSRequirements.TypeName | SxSRequirements.AssemblyName;
break;
default:
throw new ArgumentException(SR.Format(SR.Argument_BadResourceScopeVisibilityBits, consumeAsScope), nameof(consumeAsScope));
}
break;
case ResourceScope.Assembly:
if ((consumeAsScope & ResourceScope.Private) != 0)
requires |= SxSRequirements.TypeName;
break;
case ResourceScope.Private:
// No work
break;
default:
throw new ArgumentException(SR.Format(SR.Argument_BadResourceScopeVisibilityBits, calleeScope), nameof(calleeScope));
}
if (consumeAsScope == calleeScope)
{
Debug.Assert(requires == SxSRequirements.None, "Computed a strange set of required resource scoping. It's probably wrong.");
}
return requires;
}
}
}