-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
SR.cs
119 lines (106 loc) · 5.29 KB
/
SR.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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Threading;
namespace System
{
internal static partial class SR
{
private static readonly object _lock = new object();
private static List<string>? _currentlyLoading;
private static int _infinitelyRecursingCount;
#if SYSTEM_PRIVATE_CORELIB
private static bool _resourceManagerInited;
#endif
private static string InternalGetResourceString(string key)
{
if (key.Length == 0)
{
Debug.Fail("SR::GetResourceString with empty resourceKey. Bug in caller, or weird recursive loading problem?");
return key;
}
// We have a somewhat common potential for infinite
// loops with mscorlib's ResourceManager. If "potentially dangerous"
// code throws an exception, we will get into an infinite loop
// inside the ResourceManager and this "potentially dangerous" code.
// Potentially dangerous code includes the IO package, CultureInfo,
// parts of the loader, some parts of Reflection, Security (including
// custom user-written permissions that may parse an XML file at
// class load time), assembly load event handlers, etc. Essentially,
// this is not a bounded set of code, and we need to fix the problem.
// Fortunately, this is limited to mscorlib's error lookups and is NOT
// a general problem for all user code using the ResourceManager.
// The solution is to make sure only one thread at a time can call
// GetResourceString. Also, since resource lookups can be
// reentrant, if the same thread comes into GetResourceString
// twice looking for the exact same resource name before
// returning, we're going into an infinite loop and we should
// return a bogus string.
bool lockTaken = false;
try
{
Monitor.Enter(_lock, ref lockTaken);
// Are we recursively looking up the same resource? Note - our backout code will set
// the ResourceHelper's currentlyLoading stack to null if an exception occurs.
if (_currentlyLoading != null && _currentlyLoading.Count > 0 && _currentlyLoading.LastIndexOf(key) >= 0)
{
// We can start infinitely recursing for one resource lookup,
// then during our failure reporting, start infinitely recursing again.
// avoid that.
if (_infinitelyRecursingCount > 0)
{
return key;
}
_infinitelyRecursingCount++;
#if SYSTEM_PRIVATE_CORELIB
// Note: our infrastructure for reporting this exception will again cause resource lookup.
// This is the most direct way of dealing with that problem.
string message = $@"Encountered infinite recursion while looking up resource '{key}' in {CoreLib.Name}. Verify the installation of .NET is complete and does not need repairing, and that the state of the process has not become corrupted.";
Environment.FailFast(message);
#endif
}
_currentlyLoading ??= new List<string>();
#if SYSTEM_PRIVATE_CORELIB
// Call class constructors preemptively, so that we cannot get into an infinite
// loop constructing a TypeInitializationException. If this were omitted,
// we could get the Infinite recursion assert above by failing type initialization
// between the Push and Pop calls below.
if (!_resourceManagerInited)
{
RuntimeHelpers.RunClassConstructor(typeof(ResourceManager).TypeHandle);
RuntimeHelpers.RunClassConstructor(typeof(ResourceReader).TypeHandle);
RuntimeHelpers.RunClassConstructor(typeof(RuntimeResourceSet).TypeHandle);
RuntimeHelpers.RunClassConstructor(typeof(BinaryReader).TypeHandle);
_resourceManagerInited = true;
}
#endif
_currentlyLoading.Add(key); // Push
string? s = ResourceManager.GetString(key, null);
_currentlyLoading.RemoveAt(_currentlyLoading.Count - 1); // Pop
Debug.Assert(s != null, $"Looking up resource '{key}' failed. Was your resource name misspelled? Did you rebuild after adding a resource?");
return s ?? key;
}
catch
{
if (lockTaken)
{
// Backout code - throw away potentially corrupt state
s_resourceManager = null;
_currentlyLoading = null;
}
throw;
}
finally
{
if (lockTaken)
{
Monitor.Exit(_lock);
}
}
}
}
}