diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2018-08-11 17:26:36 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-11 17:26:36 +0300 |
commit | b55fbd3fde12e56a5be81979ebed31ae780c5666 (patch) | |
tree | 15adbe8665459d8c1e13050f7edd01c80a18e49c /src | |
parent | 6048b4d2e6bcb6c3195876bc2c74f883d23e4a36 (diff) | |
parent | 0fd0ca289196d2e5d5308bbb3e8e51a4a9cef31a (diff) |
Merge pull request #6213 from dotnet/master
Merge master to nmirror
Diffstat (limited to 'src')
41 files changed, 974 insertions, 637 deletions
diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props index c5b397b3f..a8c8bafdb 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props +++ b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props @@ -81,6 +81,7 @@ See the LICENSE file in the project root for more information. <LinkerArg Include="@(NativeLibrary)" /> <LinkerArg Include="-g" /> <LinkerArg Include="-Wl,-rpath,'$ORIGIN'" /> + <LinkerArg Include="-Wl,--as-needed" Condition="'$(TargetOS)' != 'OSX'" /> <LinkerArg Include="-pthread" /> <LinkerArg Include="-lstdc++" /> <LinkerArg Include="-ldl" /> @@ -94,7 +95,7 @@ See the LICENSE file in the project root for more information. <LinkerArg Include="-shared" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" /> <LinkerArg Include="@(NativeFramework->'-framework %(Identity)')" Condition="'$(TargetOS)' == 'OSX'" /> </ItemGroup> - + <Exec Command="command -v $(CppLinker)" IgnoreExitCode="true"> <Output TaskParameter="ExitCode" PropertyName="_WhereLinker"/> </Exec> diff --git a/src/Common/src/TypeSystem/Common/MethodDesc.cs b/src/Common/src/TypeSystem/Common/MethodDesc.cs index 74c48d8eb..7103433b0 100644 --- a/src/Common/src/TypeSystem/Common/MethodDesc.cs +++ b/src/Common/src/TypeSystem/Common/MethodDesc.cs @@ -133,7 +133,32 @@ namespace Internal.TypeSystem return TypeHashingAlgorithms.ComputeMethodSignatureHashCode(_returnType.GetHashCode(), _parameters); } + public SignatureEnumerator GetEnumerator() + { + return new SignatureEnumerator(this); + } + public override TypeSystemContext Context => _returnType.Context; + + public struct SignatureEnumerator + { + private int _index; + private MethodSignature _signature; + + public SignatureEnumerator(MethodSignature signature) + { + _signature = signature; + _index = -1; + } + + public TypeDesc Current => _signature[_index]; + + public bool MoveNext() + { + _index++; + return _index < _signature.Length; + } + } } /// <summary> diff --git a/src/Framework/Framework.depproj b/src/Framework/Framework.depproj index 395b213e6..d72b2e1a0 100644 --- a/src/Framework/Framework.depproj +++ b/src/Framework/Framework.depproj @@ -46,6 +46,11 @@ <FileToExclude Include="System.Data.Common" /> <FileToExclude Include="System.Diagnostics.StackTrace" /> + <!-- TODO: WinRT --> + <FileToExclude Include="System.Runtime.InteropServices.WindowsRuntime" /> + <FileToExclude Include="System.Runtime.WindowsRuntime" /> + <FileToExclude Include="System.Runtime.WindowsRuntime.UI.Xaml" /> + <!-- TODO: https://github.com/dotnet/corert/issues/5496 --> <!-- <FileToExclude Include="clrcompression" /> --> </ItemGroup> diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Mangling.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Mangling.cs index 720f06114..ace374f04 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Mangling.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Mangling.cs @@ -47,5 +47,24 @@ namespace ILCompiler } } } + + partial class UnboxingThunk : IPrefixMangledMethod + { + MethodDesc IPrefixMangledMethod.BaseMethod + { + get + { + return _targetMethod; + } + } + + string IPrefixMangledMethod.Prefix + { + get + { + return "unbox"; + } + } + } } } diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index a4567950c..19960e6a9 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -45,6 +45,7 @@ namespace Internal.IL private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; private readonly LLVMValueRef[] _localSlots; + private readonly LLVMValueRef[] _argSlots; private List<SpilledExpressionEntry> _spilledExpressions = new List<SpilledExpressionEntry>(); private int _pointerSize; private readonly byte[] _ilBytes; @@ -53,9 +54,7 @@ namespace Internal.IL /// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ... /// </summary> private EvaluationStack<StackEntry> _stack = new EvaluationStack<StackEntry>(0); - - LLVMTypeRef _universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); - + private class BasicBlock { // Common fields @@ -94,6 +93,7 @@ namespace Internal.IL _ilBytes = methodIL.GetILBytes(); _locals = methodIL.GetLocals(); _localSlots = new LLVMValueRef[_locals.Length]; + _argSlots = new LLVMValueRef[method.Signature.Length]; _signature = method.Signature; _thisType = method.OwningType; @@ -103,7 +103,7 @@ namespace Internal.IL { _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; } - _llvmFunction = GetOrCreateLLVMFunction(mangledName); + _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature); _builder = LLVM.CreateBuilder(); _pointerSize = compilation.NodeFactory.Target.PointerSize; } @@ -158,9 +158,36 @@ namespace Internal.IL LLVMBasicBlockRef prologBlock = LLVM.AppendBasicBlock(_llvmFunction, "Prolog"); LLVM.PositionBuilderAtEnd(_builder, prologBlock); + // Copy arguments onto the stack to allow + // them to be referenced by address + int thisOffset = 0; + if (!_signature.IsStatic) + { + thisOffset = 1; + } + + // Keep track of where we are in the llvm signature, starting after the + // shadow stack pointer and return adress + int signatureIndex = 1; + if (NeedsReturnStackSlot(_signature)) + { + signatureIndex++; + } + + for (int i = 0; i < _signature.Length; i++) + { + if (CanStoreTypeOnStack(_signature[i])) + { + LLVMValueRef argStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), $"arg{i + thisOffset}_"); + LLVM.BuildStore(_builder, LLVM.GetParam(_llvmFunction, (uint)signatureIndex), argStackSlot); + _argSlots[i] = argStackSlot; + signatureIndex++; + } + } + for (int i = 0; i < _locals.Length; i++) { - if (CanStoreTypeOnStack(_locals[i].Type)) + if (CanStoreLocalOnStack(_locals[i].Type)) { LLVMValueRef localStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_locals[i].Type), $"local{i}_"); _localSlots[i] = localStackSlot; @@ -172,7 +199,7 @@ namespace Internal.IL for(int i = 0; i < _locals.Length; i++) { LLVMValueRef localAddr = LoadVarAddress(i, LocalVarKind.Local, out TypeDesc localType); - if(CanStoreTypeOnStack(localType)) + if(CanStoreLocalOnStack(localType)) { LLVMTypeRef llvmType = GetLLVMTypeForTypeDesc(localType); LLVMTypeKind typeKind = LLVM.GetTypeKind(llvmType); @@ -236,18 +263,18 @@ namespace Internal.IL LLVM.BuildBr(_builder, block0); } - private LLVMValueRef CreateLLVMFunction(string mangledName) + private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature) { - return LLVM.AddFunction(Module, mangledName , _universalSignature); + return LLVM.AddFunction(Module, mangledName, GetLLVMSignatureForMethod(signature)); } - private LLVMValueRef GetOrCreateLLVMFunction(string mangledName) + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature) { LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName); if(llvmFunction.Pointer == IntPtr.Zero) { - return CreateLLVMFunction(mangledName); + return CreateLLVMFunction(mangledName, signature); } return llvmFunction; } @@ -484,7 +511,7 @@ namespace Internal.IL varCountBase = 1; } - GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset); + GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset, out int realArgIndex); if (!_signature.IsStatic && index == 0) { @@ -499,6 +526,13 @@ namespace Internal.IL type = _signature[index - varCountBase]; } valueType = GetLLVMTypeForTypeDesc(type); + + // If the argument can be passed as a real argument rather than on the shadow stack, + // get its address here + if(realArgIndex != -1) + { + return _argSlots[realArgIndex]; + } } else if (kind == LocalVarKind.Local) { @@ -785,7 +819,7 @@ namespace Internal.IL for (int i = 0; i < _locals.Length; i++) { TypeDesc localType = _locals[i].Type; - if (!CanStoreTypeOnStack(localType)) + if (!CanStoreLocalOnStack(localType)) { offset = PadNextOffset(localType, offset); } @@ -793,38 +827,56 @@ namespace Internal.IL return offset.AlignUp(_pointerSize); } + private bool CanStoreLocalOnStack(TypeDesc localType) + { + // Keep all locals on the shadow stack if there is exception + // handling so funclets can access them + if (_exceptionRegions.Length == 0) + { + return CanStoreTypeOnStack(localType); + } + return false; + } + /// <summary> /// Returns true if the type can be stored on the local stack /// instead of the shadow stack in this method. /// </summary> - private bool CanStoreTypeOnStack(TypeDesc localType) + private static bool CanStoreTypeOnStack(TypeDesc type) { - // Keep all locals on the shadow stack if there is exception - // handling so funclets can access them - if (_exceptionRegions.Length == 0) + if (type is DefType defType) { - if (localType is DefType) - { - if (!((DefType)localType).ContainsGCPointers) - { - return true; - } - } - else if (localType is PointerType) + if (!defType.IsGCPointer && !defType.ContainsGCPointers) { return true; } } + else if (type is PointerType) + { + return true; + } return false; } + /// <summary> + /// Returns true if the method returns a type that must be kept + /// on the shadow stack + /// </summary> + private static bool NeedsReturnStackSlot(MethodSignature signature) + { + return !signature.ReturnType.IsVoid && !CanStoreTypeOnStack(signature.ReturnType); + } + private int GetTotalParameterOffset() { int offset = 0; for (int i = 0; i < _signature.Length; i++) { - offset = PadNextOffset(_signature[i], offset); + if (!CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } } if (!_signature.IsStatic) { @@ -842,8 +894,10 @@ namespace Internal.IL return offset.AlignUp(_pointerSize); } - private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset) + private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset, out int realArgIndex) { + realArgIndex = -1; + int thisSize = 0; if (!_signature.IsStatic) { @@ -863,12 +917,29 @@ namespace Internal.IL var argType = _signature[index]; size = argType.GetElementSize().AsInt; + int potentialRealArgIndex = 0; + offset = thisSize; for (int i = 0; i < index; i++) { - offset = PadNextOffset(_signature[i], offset); + // We could compact the set of argSlots to only those that we'd keep on the stack, but currently don't + potentialRealArgIndex++; + + if (!CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + + if (CanStoreTypeOnStack(argType)) + { + realArgIndex = potentialRealArgIndex; + offset = -1; + } + else + { + offset = PadOffset(argType, offset); } - offset = PadOffset(argType, offset); } private void GetLocalSizeAndOffsetAtIndex(int index, out int size, out int offset) @@ -876,7 +947,7 @@ namespace Internal.IL LocalVariableDefinition local = _locals[index]; size = local.Type.GetElementSize().AsInt; - if (CanStoreTypeOnStack(local.Type)) + if (CanStoreLocalOnStack(local.Type)) { offset = -1; } @@ -885,7 +956,7 @@ namespace Internal.IL offset = 0; for (int i = 0; i < index; i++) { - if (!CanStoreTypeOnStack(_locals[i].Type)) + if (!CanStoreLocalOnStack(_locals[i].Type)) { offset = PadNextOffset(_locals[i].Type, offset); } @@ -992,14 +1063,25 @@ namespace Internal.IL private void ImportReturn() { - if (_signature.ReturnType != GetWellKnownType(WellKnownType.Void)) + if (_signature.ReturnType.IsVoid) { - StackEntry retVal = _stack.Pop(); - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); - ImportStoreHelper(retVal.ValueAsType(valueType, _builder), valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + LLVM.BuildRetVoid(_builder); + return; } - LLVM.BuildRetVoid(_builder); + StackEntry retVal = _stack.Pop(); + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); + LLVMValueRef castValue = retVal.ValueAsType(valueType, _builder); + + if (NeedsReturnStackSlot(_signature)) + { + ImportStoreHelper(castValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + LLVM.BuildRetVoid(_builder); + } + else + { + LLVM.BuildRet(_builder, castValue); + } } private void ImportCall(ILOpcode opcode, int token) @@ -1022,7 +1104,28 @@ namespace Internal.IL if (opcode == ILOpcode.newobj) { TypeDesc newType = callee.OwningType; - if (newType.IsString) + if (newType.IsArray) + { + var paramCnt = callee.Signature.Length; + var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType(); + LLVMValueRef dimensions = LLVM.BuildArrayAlloca(_builder, LLVMTypeRef.Int32Type(), BuildConstInt32(paramCnt), "newobj_array_pdims_" + _currentOffset); + for (int i = paramCnt - 1; i >= 0; --i) + { + LLVM.BuildStore(_builder, _stack.Pop().ValueAsInt32(_builder, true), + LLVM.BuildGEP(_builder, dimensions, new LLVMValueRef[] { BuildConstInt32(i) }, "pdims_ptr")); + } + var arguments = new StackEntry[] + { + new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(newType, true), eeTypeDesc), + new Int32ConstantEntry(paramCnt), + new AddressExpressionEntry(StackValueKind.ValueType, "newobj_array_pdims", dimensions) + }; + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", "ArrayHelpers"); + MethodDesc helperMethod = helperType.GetKnownMethod("NewObjArray", null); + PushNonNull(HandleCall(helperMethod, helperMethod.Signature, arguments, forcedReturnType: newType)); + return; + } + else if (newType.IsString) { // String constructors actually look like regular method calls IMethodNode node = _compilation.NodeFactory.StringAllocator(callee); @@ -1074,7 +1177,7 @@ namespace Internal.IL callee = delegateInfo.Constructor.Method; if (callee.Signature.Length == 3) { - PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString())); + PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString(), delegateInfo.Thunk.Method.Signature)); } } @@ -1084,6 +1187,14 @@ namespace Internal.IL private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt) { string calleeName = _compilation.NameMangler.GetMangledMethodName(callee).ToString(); + + // Sealed methods must not be called virtually due to sealed vTables, so call them directly + if(callee.IsFinal || callee.OwningType.IsSealed()) + { + AddMethodReference(callee); + return GetOrCreateLLVMFunction(calleeName, callee.Signature); + } + if (thisPointer != null && callee.IsVirtual && isCallVirt) { // TODO: Full resolution of virtual methods @@ -1126,7 +1237,7 @@ namespace Internal.IL if (targetMethod != null) { AddMethodReference(targetMethod); - return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString()); + return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString(), callee.Signature); } return GetCallableVirtualMethod(thisPointer, callee); @@ -1134,7 +1245,7 @@ namespace Internal.IL } else { - return GetOrCreateLLVMFunction(calleeName); + return GetOrCreateLLVMFunction(calleeName, callee.Signature); } } @@ -1151,7 +1262,7 @@ namespace Internal.IL Debug.Assert(method.IsVirtual); LLVMValueRef slot = GetOrCreateMethodSlot(method); var pointerSize = method.Context.Target.PointerSize; - LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(method.Signature); LLVMValueRef functionPtr; if (method.OwningType.IsInterface) { @@ -1159,11 +1270,11 @@ namespace Internal.IL var interfaceEEType = new LoadExpressionEntry(StackValueKind.ValueType, "interfaceEEType", GetEETypePointerForTypeDesc(method.OwningType, true), eeTypeDesc); var eeTypeExpression = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), eeTypeDesc); var targetEntry = CallRuntime(_compilation.TypeSystemContext, DispatchResolve, "FindInterfaceMethodImplementationTarget", new StackEntry[] { eeTypeExpression, interfaceEEType, new ExpressionEntry(StackValueKind.Int32, "slot", slot, GetWellKnownType(WellKnownType.UInt16)) }); - functionPtr = targetEntry.ValueAsType(LLVM.PointerType(universalSignature, 0), _builder); + functionPtr = targetEntry.ValueAsType(LLVM.PointerType(llvmSignature, 0), _builder); } else { - var rawObjectPtr = CastIfNecessary(objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(universalSignature, 0), 0), 0), objectPtr.Name()); + var rawObjectPtr = CastIfNecessary(objectPtr.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(llvmSignature, 0), 0), 0), objectPtr.Name()); var eeType = LLVM.BuildLoad(_builder, rawObjectPtr, "ldEEType"); var slotPtr = LLVM.BuildGEP(_builder, eeType, new LLVMValueRef[] { slot }, "__getslot__"); functionPtr = LLVM.BuildLoad(_builder, slotPtr, "ld__getslot__"); @@ -1172,6 +1283,42 @@ namespace Internal.IL return functionPtr; } + private LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature) + { + TypeDesc returnType = signature.ReturnType; + LLVMTypeRef llvmReturnType; + bool returnOnStack = false; + if (!NeedsReturnStackSlot(signature)) + { + returnOnStack = true; + llvmReturnType = GetLLVMTypeForTypeDesc(returnType); + } + else + { + llvmReturnType = LLVM.VoidType(); + } + + List<LLVMTypeRef> signatureTypes = new List<LLVMTypeRef>(); + signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0)); // Shadow stack pointer + + if (!returnOnStack && returnType != GetWellKnownType(WellKnownType.Void)) + { + signatureTypes.Add(LLVM.PointerType(LLVM.Int8Type(), 0)); + } + + // Intentionally skipping the 'this' pointer since it could always be a GC reference + // and thus must be on the shadow stack + foreach (TypeDesc type in signature) + { + if (CanStoreTypeOnStack(type)) + { + signatureTypes.Add(GetLLVMTypeForTypeDesc(type)); + } + } + + return LLVM.FunctionType(llvmReturnType, signatureTypes.ToArray(), false); + } + private ExpressionEntry AllocateObject(TypeDesc type) { MetadataType metadataType = (MetadataType)type; @@ -1342,10 +1489,12 @@ namespace Internal.IL var pointerSize = _compilation.NodeFactory.Target.PointerSize; LLVMValueRef returnAddress; - LLVMValueRef castReturnAddress; + LLVMValueRef castReturnAddress = default; TypeDesc returnType = signature.ReturnType; + + bool needsReturnSlot = NeedsReturnStackSlot(signature); SpilledExpressionEntry returnSlot = null; - if (!returnType.IsVoid) + if (needsReturnSlot) { int returnIndex = _spilledExpressions.Count; returnSlot = new SpilledExpressionEntry(GetStackValueKind(returnType), callee?.Name + "_return", returnType, returnIndex, this); @@ -1353,11 +1502,6 @@ namespace Internal.IL returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused); castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), callee?.Name + "_castreturn"); } - else - { - returnAddress = LLVM.ConstNull(LLVM.PointerType(LLVM.Int8Type(), 0)); - castReturnAddress = returnAddress; - } int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), @@ -1365,16 +1509,25 @@ namespace Internal.IL String.Empty); var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack"); - // argument offset + List<LLVMValueRef> llvmArgs = new List<LLVMValueRef>(); + llvmArgs.Add(castShadowStack); + if (needsReturnSlot) + { + llvmArgs.Add(castReturnAddress); + } + + // argument offset on the shadow stack int argOffset = 0; var instanceAdjustment = signature.IsStatic ? 0 : 1; for (int index = 0; index < argumentValues.Length; index++) { StackEntry toStore = argumentValues[index]; + bool isThisParameter = false; TypeDesc argType; if (index == 0 && !signature.IsStatic) { + isThisParameter = true; if (opcode == ILOpcode.calli) argType = toStore.Type; else if (callee.OwningType.IsValueType) @@ -1387,14 +1540,24 @@ namespace Internal.IL argType = signature[index - instanceAdjustment]; } - // The previous argument might have left this type unaligned, so pad if necessary - argOffset = PadOffset(argType, argOffset); - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); + LLVMValueRef argValue = toStore.ValueAsType(valueType, _builder); + + // Pass arguments as parameters if possible + if (!isThisParameter && CanStoreTypeOnStack(argType)) + { + llvmArgs.Add(argValue); + } + // Otherwise store them on the shadow stack + else + { + // The previous argument might have left this type unaligned, so pad if necessary + argOffset = PadOffset(argType, argOffset); - ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, (uint)argOffset); + ImportStoreHelper(argValue, valueType, castShadowStack, (uint)argOffset); - argOffset += argType.GetElementSize().AsInt; + argOffset += argType.GetElementSize().AsInt; + } } LLVMValueRef fn; @@ -1407,14 +1570,18 @@ namespace Internal.IL fn = LLVMFunctionForMethod(callee, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt); } - LLVM.BuildCall(_builder, fn, new LLVMValueRef[] { - castShadowStack, - castReturnAddress}, string.Empty); - + LLVMValueRef llvmReturn = LLVM.BuildCall(_builder, fn, llvmArgs.ToArray(), string.Empty); if (!returnType.IsVoid) { - return returnSlot; + if (needsReturnSlot) + { + return returnSlot; + } + else + { + return new ExpressionEntry(GetStackValueKind(returnType), callee?.Name + "_return", llvmReturn, returnType); + } } else { @@ -1612,15 +1779,35 @@ namespace Internal.IL curOffset = PadNextOffset(method.Signature.ReturnType, curOffset); LLVMValueRef calleeFrame = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { BuildConstInt32(curOffset) }, "calleeFrame"); + List<LLVMValueRef> llvmArgs = new List<LLVMValueRef>(); + llvmArgs.Add(calleeFrame); + + bool needsReturnSlot = NeedsReturnStackSlot(method.Signature); + + if (needsReturnSlot) + { + // Slot for return value if necessary + llvmArgs.Add(shadowStack); + } + for (int i = 0; i < llvmParams.Length; i++) { - curOffset = PadOffset(method.Signature[i], curOffset); - LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i); - LLVM.BuildStore(builder, LLVM.GetParam(thunkFunc, (uint)i), CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0), $"parameter{i}_")); - curOffset = PadNextOffset(method.Signature[i], curOffset); + LLVMValueRef argValue = LLVM.GetParam(thunkFunc, (uint)i); + + if (CanStoreTypeOnStack(method.Signature[i])) + { + llvmArgs.Add(argValue); + } + else + { + curOffset = PadOffset(method.Signature[i], curOffset); + LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i); + LLVM.BuildStore(builder, argValue, CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0), $"parameter{i}_")); + curOffset = PadNextOffset(method.Signature[i], curOffset); + } } - LLVM.BuildCall(builder, managedFunction, new LLVMValueRef[] { calleeFrame, shadowStack }, ""); + LLVMValueRef llvmReturnValue = LLVM.BuildCall(builder, managedFunction, llvmArgs.ToArray(), ""); if (method.IsNativeCallable) { @@ -1628,9 +1815,16 @@ namespace Internal.IL LLVM.BuildCall(builder, RhpReversePInvokeReturn2, new LLVMValueRef[] { reversePInvokeFrame }, ""); } - if (method.Signature.ReturnType != compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Void)) + if (!method.Signature.ReturnType.IsVoid) { - LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, shadowStack, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "returnValue")); + if (needsReturnSlot) + { + LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, shadowStack, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "returnValue")); + } + else + { + LLVM.BuildRet(builder, llvmReturnValue); + } } else { @@ -1641,7 +1835,7 @@ namespace Internal.IL private void ImportCalli(int token) { MethodSignature methodSignature = (MethodSignature)_methodIL.GetObject(token); - HandleCall(null, methodSignature, ILOpcode.calli, ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(_universalSignature, 0), _builder)); + HandleCall(null, methodSignature, ILOpcode.calli, ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVM.PointerType(GetLLVMSignatureForMethod(methodSignature), 0), _builder)); } private void ImportLdFtn(int token, ILOpcode opCode) @@ -1664,7 +1858,7 @@ namespace Internal.IL if (targetLLVMFunction.Pointer.Equals(IntPtr.Zero)) { - targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString()); + targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(method).ToString(), method.Signature); } var entry = new FunctionPointerEntry("ldftn", method, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn); diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs index cec8c0563..5b819d8a6 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs @@ -270,22 +270,18 @@ namespace ILCompiler.DependencyAnalysis LLVM.BuildStore(builder, castShadowStack, shadowStackTop); // Pass on main arguments - var argcSlot = LLVM.BuildPointerCast(builder, shadowStack, LLVM.PointerType(LLVM.Int32Type(), 0), "argcSlot"); - LLVM.BuildStore(builder, LLVM.GetParam(mainFunc, 0), argcSlot); - var argvSlot = LLVM.BuildGEP(builder, castShadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), 4, LLVMMisc.False) }, "argvSlot"); - LLVM.BuildStore(builder, LLVM.GetParam(mainFunc, 1), LLVM.BuildPointerCast(builder, argvSlot, LLVM.PointerType(LLVM.PointerType(LLVM.Int8Type(), 0), 0), "")); + LLVMValueRef argc = LLVM.GetParam(mainFunc, 0); + LLVMValueRef argv = LLVM.GetParam(mainFunc, 1); - // StartupCodeMain will always return a value whether the user's main does or not - LLVMValueRef returnValueSlot = LLVM.BuildAlloca(builder, LLVM.Int32Type(), "returnValue"); - - LLVM.BuildCall(builder, managedMain, new LLVMValueRef[] + LLVMValueRef mainReturn = LLVM.BuildCall(builder, managedMain, new LLVMValueRef[] { castShadowStack, - LLVM.BuildPointerCast(builder, returnValueSlot, LLVM.PointerType(LLVM.Int8Type(), 0), String.Empty) + argc, + argv, }, - String.Empty); + "returnValue"); - LLVM.BuildRet(builder, LLVM.BuildLoad(builder, returnValueSlot, String.Empty)); + LLVM.BuildRet(builder, mainReturn); LLVM.SetLinkage(mainFunc, LLVMLinkage.LLVMExternalLinkage); } diff --git a/src/ILCompiler/repro/repro.csproj b/src/ILCompiler/repro/repro.csproj index 4a81549b0..e8ec9a042 100644 --- a/src/ILCompiler/repro/repro.csproj +++ b/src/ILCompiler/repro/repro.csproj @@ -8,11 +8,17 @@ <SkipSigning>true</SkipSigning> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <CopyNuGetImplementations>false</CopyNuGetImplementations> + + <!-- Supress warnings that often happen in repro code --> + <NoWarn>169;414</NoWarn> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NETCore.App"> <Version>$(MicrosoftNETCoreAppPackageVersion)</Version> </PackageReference> + <PackageReference Include="System.Runtime.CompilerServices.Unsafe"> + <Version>4.5.1</Version> + </PackageReference> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> diff --git a/src/JitInterface/src/ThunkGenerator/corinfo.h b/src/JitInterface/src/ThunkGenerator/corinfo.h index 2270d328d..89cd95f75 100644 --- a/src/JitInterface/src/ThunkGenerator/corinfo.h +++ b/src/JitInterface/src/ThunkGenerator/corinfo.h @@ -1963,9 +1963,6 @@ struct DelegateCtorArgs // use offsetof to get the offset of the fields above #include <stddef.h> // offsetof -#ifndef offsetof -#define offsetof(s,m) ((size_t)&(((s *)0)->m)) -#endif // Guard-stack cookie for preventing against stack buffer overruns typedef SIZE_T GSCookie; diff --git a/src/JitInterface/src/ThunkGenerator/corjithost.h b/src/JitInterface/src/ThunkGenerator/corjithost.h index 8242fab2b..b2ab80646 100644 --- a/src/JitInterface/src/ThunkGenerator/corjithost.h +++ b/src/JitInterface/src/ThunkGenerator/corjithost.h @@ -15,15 +15,11 @@ class ICorJitHost { public: - // Allocate memory of the given size in bytes. All bytes of the returned block - // must be initialized to zero. If `usePageAllocator` is true, the implementation - // should use an allocator that deals in OS pages if one exists. - virtual void* allocateMemory(size_t size, bool usePageAllocator = false) = 0; + // Allocate memory of the given size in bytes. + virtual void* allocateMemory(size_t size) = 0; - // Frees memory previous obtained by a call to `ICorJitHost::allocateMemory`. The - // value of the `usePageAllocator` parameter must match the value that was - // provided to the call to used to allocate the memory. - virtual void freeMemory(void* block, bool usePageAllocator = false) = 0; + // Frees memory previous obtained by a call to `ICorJitHost::allocateMemory`. + virtual void freeMemory(void* block) = 0; // Return an integer config value for the given key, if any exists. virtual int getIntConfigValue( @@ -43,6 +39,20 @@ public: virtual void freeStringConfigValue( const wchar_t* value ) = 0; + + // Allocate memory slab of the given size in bytes. The host is expected to pool + // these for a good performance. + virtual void* allocateSlab(size_t size, size_t* pActualSize) + { + *pActualSize = size; + return allocateMemory(size); + } + + // Free memory slab of the given size in bytes. + virtual void freeSlab(void* slab, size_t actualSize) + { + freeMemory(slab); + } }; #endif diff --git a/src/Native/ObjWriter/objwriter.cpp b/src/Native/ObjWriter/objwriter.cpp index 4470f04be..bdf1016dc 100644 --- a/src/Native/ObjWriter/objwriter.cpp +++ b/src/Native/ObjWriter/objwriter.cpp @@ -921,13 +921,13 @@ void ObjectWriter::EmitARMExIdxCode(int Offset, const char *Blob) ATS.emitPad(CfiCode->Offset); break; case CFI_REL_OFFSET: - RegList.push_back(CfiCode->DwarfReg); + RegList.push_back(CfiCode->DwarfReg + 14); // See ARMRegEncodingTable in ARMGenRegisterInfo.inc by getEncodingValue ATS.emitRegSave(RegList, false); break; case CFI_DEF_CFA_REGISTER: assert(CfiCode->Offset == 0 && "Unexpected Offset Value for OpDefCfaRegister"); - ATS.emitMovSP(CfiCode->DwarfReg, 0); + ATS.emitMovSP(CfiCode->DwarfReg + 14, 0); // See ARMRegEncodingTable in ARMGenRegisterInfo.inc by getEncodingValue break; default: assert(false && "Unrecognized CFI"); diff --git a/src/Native/jitinterface/jithost.cpp b/src/Native/jitinterface/jithost.cpp index 2e2f17bfb..f433cf5b1 100644 --- a/src/Native/jitinterface/jithost.cpp +++ b/src/Native/jitinterface/jithost.cpp @@ -36,12 +36,12 @@ public: JitHost(JitConfigProvider* pConfigProvider) : pConfigProvider(pConfigProvider) { } - virtual void* allocateMemory(size_t size, bool usePageAllocator = false) + virtual void* allocateMemory(size_t size) { return malloc(size); } - virtual void freeMemory(void* block, bool usePageAllocator = false) + virtual void freeMemory(void* block) { free(block); } @@ -76,6 +76,17 @@ public: { free(value); } + + virtual void* allocateSlab(size_t size, size_t* pActualSize) + { + *pActualSize = size; + return allocateMemory(size); + } + + virtual void freeSlab(void* slab, size_t actualSize) + { + freeMemory(slab); + } }; DLL_EXPORT void* GetJitHost(JitConfigProvider* pConfigProvider) diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 743a70361..1133ce595 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -338,6 +338,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Emit\StackBehaviour.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\EventAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\EventInfo.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ExceptionHandlingClause.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ExceptionHandlingClauseOptions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\FieldAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\FieldInfo.cs" /> @@ -349,12 +350,14 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\InvalidFilterCriteriaException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\IReflect.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\IReflectableType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\LocalVariableInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ManifestResourceInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MemberFilter.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MemberInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MemberTypes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodBase.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodBody.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodImplAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.Internal.cs" /> @@ -484,6 +487,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalDirectiveException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.Fast.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeCallableAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" /> @@ -603,8 +607,10 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreFullException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreSlim.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SendOrPostCallback.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinLock.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinWait.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SynchronizationLockException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadLocal.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\ConcurrentExclusiveSchedulerPair.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCanceledException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCompletionSource.cs" /> diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs index e7efa22b2..91b782061 100644 --- a/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -23,7 +23,7 @@ namespace System.Collections.Generic public sealed override bool Equals(string x, string y) => string.Equals(x, y); - public sealed override int GetHashCode(string obj) => obj?.GetLegacyNonRandomizedHashCode() ?? 0; + public sealed override int GetHashCode(string obj) => obj?.GetNonRandomizedHashCode() ?? 0; public void GetObjectData(SerializationInfo info, StreamingContext context) { diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs index ce43ed093..5c05bf831 100644 --- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs @@ -181,6 +181,7 @@ using System.Security.Permissions; using System.Text; using System.Threading; using Microsoft.Win32; +using Internal.Runtime.Augments; #if ES_BUILD_STANDALONE using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor; @@ -196,10 +197,6 @@ using Contract = System.Diagnostics.Contracts.Contract; using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; #endif -#if CORECLR || ES_BUILD_PN -using Internal.Runtime.Augments; -#endif - #if ES_BUILD_STANDALONE namespace Microsoft.Diagnostics.Tracing #else diff --git a/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs b/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs index aa10e8d88..a3a2d2983 100644 --- a/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs +++ b/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs @@ -2,7 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if MS_IO_REDIST +using System; + +namespace Microsoft.IO +#else namespace System.IO +#endif { /// <summary> /// Simple wrapper to safely disable the normal media insertion prompt for diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs index f22a9913e..1c27e0cf1 100644 --- a/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs @@ -5,7 +5,14 @@ using System.Diagnostics; using System.Text; +#if MS_IO_REDIST +using System; +using System.IO; + +namespace Microsoft.IO +#else namespace System.IO +#endif { public static partial class Path { @@ -34,7 +41,7 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); // If the path would normalize to string empty, we'll consider it empty - if (PathInternal.IsEffectivelyEmpty(path)) + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); // Embedded null characters are the only invalid character case we trully care about. @@ -43,7 +50,7 @@ namespace System.IO if (path.IndexOf('\0') != -1) throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); - if (PathInternal.IsExtended(path)) + if (PathInternal.IsExtended(path.AsSpan())) { // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\ // paths and neither should we. Even if we wanted to GetFullPathName does not work @@ -72,7 +79,7 @@ namespace System.IO if (IsPathFullyQualified(path)) return GetFullPath(path); - if (PathInternal.IsEffectivelyEmpty(path)) + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) return basePath; int length = path.Length; @@ -90,23 +97,23 @@ namespace System.IO // Drive relative paths Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2])); - if (GetVolumeName(path).EqualsOrdinal(GetVolumeName(basePath))) + if (GetVolumeName(path.AsSpan()).EqualsOrdinal(GetVolumeName(basePath.AsSpan()))) { // Matching root // "C:Foo" and "C:\Bar" => "C:\Bar\Foo" // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo" - combinedPath = Join(basePath, path.AsSpan(2)); + combinedPath = Join(basePath.AsSpan(), path.AsSpan(2)); } else { // No matching root, root to specified drive // "D:Foo" and "C:\Bar" => "D:Foo" // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo" - combinedPath = !PathInternal.IsDevice(basePath) + combinedPath = !PathInternal.IsDevice(basePath.AsSpan()) ? path.Insert(2, @"\") : length == 2 - ? JoinInternal(basePath.AsSpan(0, 4), path, @"\") - : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\", path.AsSpan(2)); + ? JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(), @"\".AsSpan()) + : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\".AsSpan(), path.AsSpan(2)); } } else @@ -114,15 +121,15 @@ namespace System.IO // "Simple" relative path // "Foo" and "C:\Bar" => "C:\Bar\Foo" // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo" - combinedPath = JoinInternal(basePath, path); + combinedPath = JoinInternal(basePath.AsSpan(), path.AsSpan()); } // Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo) // to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root // them properly. As such we need to manually remove segments and not use GetFullPath(). - return PathInternal.IsDevice(combinedPath) - ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath)) + return PathInternal.IsDevice(combinedPath.AsSpan()) + ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath.AsSpan())) : GetFullPath(combinedPath); } @@ -205,14 +212,14 @@ namespace System.IO // only contains whitespace characters an ArgumentException gets thrown. public static string GetPathRoot(string path) { - if (PathInternal.IsEffectivelyEmpty(path)) + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) return null; ReadOnlySpan<char> result = GetPathRoot(path.AsSpan()); if (path.Length == result.Length) return PathInternal.NormalizeDirectorySeparators(path); - return PathInternal.NormalizeDirectorySeparators(new string(result)); + return PathInternal.NormalizeDirectorySeparators(result.ToString()); } /// <remarks> @@ -273,11 +280,11 @@ namespace System.IO { bool isDevice = PathInternal.IsDevice(path); - if (!isDevice && path.Slice(0, 2).EqualsOrdinal(@"\\") ) + if (!isDevice && path.Slice(0, 2).EqualsOrdinal(@"\\".AsSpan()) ) return 2; else if (isDevice && path.Length >= 8 - && (path.Slice(0, 8).EqualsOrdinal(PathInternal.UncExtendedPathPrefix) - || path.Slice(5, 4).EqualsOrdinal(@"UNC\"))) + && (path.Slice(0, 8).EqualsOrdinal(PathInternal.UncExtendedPathPrefix.AsSpan()) + || path.Slice(5, 4).EqualsOrdinal(@"UNC\".AsSpan()))) return 8; return -1; diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.cs b/src/System.Private.CoreLib/shared/System/IO/Path.cs index e619ecd8c..7b4565a13 100644 --- a/src/System.Private.CoreLib/shared/System/IO/Path.cs +++ b/src/System.Private.CoreLib/shared/System/IO/Path.cs @@ -6,7 +6,14 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; +#if MS_IO_REDIST +using System; +using System.IO; + +namespace Microsoft.IO +#else namespace System.IO +#endif { // Provides methods for processing file system strings in a cross-platform manner. // Most of the methods don't do a complete parsing (such as examining a UNC hostname), @@ -79,10 +86,10 @@ namespace System.IO /// </remarks> public static string GetDirectoryName(string path) { - if (path == null || PathInternal.IsEffectivelyEmpty(path)) + if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan())) return null; - int end = GetDirectoryNameOffset(path); + int end = GetDirectoryNameOffset(path.AsSpan()); return end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null; } @@ -130,7 +137,7 @@ namespace System.IO if (path == null) return null; - return new string(GetExtension(path.AsSpan())); + return GetExtension(path.AsSpan()).ToString(); } /// <summary> @@ -173,7 +180,7 @@ namespace System.IO if (path.Length == result.Length) return path; - return new string(result); + return result.ToString(); } /// <summary> @@ -204,7 +211,7 @@ namespace System.IO if (path.Length == result.Length) return path; - return new string(result); + return result.ToString(); } /// <summary> @@ -388,9 +395,9 @@ namespace System.IO public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2) { if (path1.Length == 0) - return new string(path2); + return path2.ToString(); if (path2.Length == 0) - return new string(path1); + return path1.ToString(); return JoinInternal(path1, path2); } @@ -498,7 +505,7 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return second; - return JoinInternal(first, second); + return JoinInternal(first.AsSpan(), second.AsSpan()); } private static string CombineInternal(string first, string second, string third) @@ -515,7 +522,7 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return CombineInternal(second, third); - return JoinInternal(first, second, third); + return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan()); } private static string CombineInternal(string first, string second, string third, string fourth) @@ -536,7 +543,7 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return CombineInternal(second, third, fourth); - return JoinInternal(first, second, third, fourth); + return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan(), fourth.AsSpan()); } private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second) @@ -548,7 +555,11 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second)) { +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + (hasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator), (destination, state) => @@ -572,7 +583,11 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third)) { +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, Third: (IntPtr)t, ThirdLength: third.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator), @@ -602,7 +617,12 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third), u = &MemoryMarshal.GetReference(fourth)) { + +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength:fourth.Length, @@ -697,7 +717,7 @@ namespace System.IO private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); - if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) throw new ArgumentNullException(nameof(path)); Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); @@ -715,10 +735,10 @@ namespace System.IO // Trailing separators aren't significant for comparison int relativeToLength = relativeTo.Length; - if (PathInternal.EndsInDirectorySeparator(relativeTo)) + if (PathInternal.EndsInDirectorySeparator(relativeTo.AsSpan())) relativeToLength--; - bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path); + bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path.AsSpan()); int pathLength = path.Length; if (pathEndsInSeparator) pathLength--; diff --git a/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs index ed49422c1..bada2f5cd 100644 --- a/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs @@ -243,7 +243,7 @@ namespace System.IO ReadOnlySpan<char> output = builderToUse.AsSpan(rootDifference); string returnValue = ((originalPath != null) && output.Equals(originalPath.AsSpan(), StringComparison.Ordinal)) - ? originalPath : new string(output); + ? originalPath : output.ToString(); inputBuilder.Dispose(); return returnValue; diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs index b01482abd..5f9ee0e02 100644 --- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs @@ -130,7 +130,7 @@ namespace System.IO // In any case, all internal usages should be hitting normalize path (Path.GetFullPath) before they hit this // shimming method. (Or making a change that doesn't impact normalization, such as adding a filename to a // normalized base path.) - if (IsPartiallyQualified(path) || IsDevice(path)) + if (IsPartiallyQualified(path.AsSpan()) || IsDevice(path.AsSpan())) return path; // Given \\server\share in longpath becomes \\?\UNC\server\share diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs index c9defac1f..1b08a2612 100644 --- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs +++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs @@ -22,10 +22,10 @@ namespace System.IO internal static bool StartsWithDirectorySeparator(ReadOnlySpan<char> path) => path.Length > 0 && IsDirectorySeparator(path[0]); internal static string EnsureTrailingSeparator(string path) - => EndsInDirectorySeparator(path) ? path : path + DirectorySeparatorCharAsString; + => EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString; internal static string TrimEndingDirectorySeparator(string path) => - EndsInDirectorySeparator(path) && !IsRoot(path) ? + EndsInDirectorySeparator(path.AsSpan()) && !IsRoot(path.AsSpan()) ? path.Substring(0, path.Length - 1) : path; @@ -97,8 +97,8 @@ namespace System.IO /// </summary> internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType) { - int firstRootLength = GetRootLength(first); - int secondRootLength = GetRootLength(second); + int firstRootLength = GetRootLength(first.AsSpan()); + int secondRootLength = GetRootLength(second.AsSpan()); return firstRootLength == secondRootLength && string.Compare( diff --git a/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs index b510b6b28..8d94ac60b 100644 --- a/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs +++ b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs @@ -476,6 +476,123 @@ namespace System.IO } } + private void WriteFormatHelper(string format, ParamsArray args, bool appendNewLine) + { + StringBuilder sb = + StringBuilderCache.Acquire(format.Length + args.Length * 8) + .AppendFormatHelper(null, format, args); + + StringBuilder.ChunkEnumerator chunks = sb.GetChunks(); + + bool more = chunks.MoveNext(); + while (more) + { + ReadOnlySpan<char> current = chunks.Current.Span; + more = chunks.MoveNext(); + + // If final chunk, include the newline if needed + WriteSpan(current, appendNewLine: more ? false : appendNewLine); + } + + StringBuilderCache.Release(sb); + } + + public override void Write(string format, object arg0) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: false); + } + else + { + base.Write(format, arg0); + } + } + + public override void Write(string format, object arg0, object arg1) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: false); + } + else + { + base.Write(format, arg0, arg1); + } + } + + public override void Write(string format, object arg0, object arg1, object arg2) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: false); + } + else + { + base.Write(format, arg0, arg1, arg2); + } + } + + public override void Write(string format, params object[] arg) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: false); + } + else + { + base.Write(format, arg); + } + } + + public override void WriteLine(string format, object arg0) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: true); + } + else + { + base.WriteLine(format, arg0); + } + } + + public override void WriteLine(string format, object arg0, object arg1) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: true); + } + else + { + base.WriteLine(format, arg0, arg1); + } + } + + public override void WriteLine(string format, object arg0, object arg1, object arg2) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: true); + } + else + { + base.WriteLine(format, arg0, arg1, arg2); + } + } + + public override void WriteLine(string format, params object[] arg) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: true); + } + else + { + base.WriteLine(format, arg); + } + } + #region Task based Async APIs public override Task WriteAsync(char value) { diff --git a/src/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs b/src/System.Private.CoreLib/shared/System/Reflection/ExceptionHandlingClause.cs index 8598256b3..15780f11c 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs +++ b/src/System.Private.CoreLib/shared/System/Reflection/ExceptionHandlingClause.cs @@ -9,17 +9,13 @@ namespace System.Reflection public class ExceptionHandlingClause { protected ExceptionHandlingClause() { } - - // Desktop compat: These default implementations behave strangely because this class was originally - // creatable only from the native runtime, not through subclass inheritance. - - public virtual Type CatchType => null; - public virtual int FilterOffset { get { throw new InvalidOperationException(); } } - public virtual ExceptionHandlingClauseOptions Flags => default(ExceptionHandlingClauseOptions); - public virtual int HandlerLength => 0; - public virtual int HandlerOffset => 0; - public virtual int TryLength => 0; + public virtual ExceptionHandlingClauseOptions Flags => default; public virtual int TryOffset => 0; + public virtual int TryLength => 0; + public virtual int HandlerOffset => 0; + public virtual int HandlerLength => 0; + public virtual int FilterOffset => throw new InvalidOperationException(SR.Arg_EHClauseNotFilter); + public virtual Type CatchType => null; public override string ToString() { @@ -29,3 +25,4 @@ namespace System.Reflection } } } + diff --git a/src/System.Private.CoreLib/shared/System/Reflection/LocalVariableInfo.cs b/src/System.Private.CoreLib/shared/System/Reflection/LocalVariableInfo.cs new file mode 100644 index 000000000..1540bde53 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Reflection/LocalVariableInfo.cs @@ -0,0 +1,26 @@ +// 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. + +using System.Diagnostics; + +namespace System.Reflection +{ + public class LocalVariableInfo + { + public virtual Type LocalType { get { Debug.Fail("type must be set!"); return null; } } + public virtual int LocalIndex => 0; + public virtual bool IsPinned => false; + protected LocalVariableInfo() { } + public override string ToString() + { + string toString = LocalType.ToString() + " (" + LocalIndex + ")"; + + if (IsPinned) + toString += " (pinned)"; + + return toString; + } + } +} + diff --git a/src/System.Private.CoreLib/src/System/Reflection/MethodBody.cs b/src/System.Private.CoreLib/shared/System/Reflection/MethodBody.cs index b87558573..bdf53ad12 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/MethodBody.cs +++ b/src/System.Private.CoreLib/shared/System/Reflection/MethodBody.cs @@ -9,14 +9,12 @@ namespace System.Reflection public class MethodBody { protected MethodBody() { } - - // Desktop compat: These default implementations behave strangely because this class was originally - // creatable only from the native runtime, not through subclass inheritance. public virtual int LocalSignatureMetadataToken => 0; - public virtual IList<LocalVariableInfo> LocalVariables { get { throw new ArgumentNullException("array"); } } + public virtual IList<LocalVariableInfo> LocalVariables => throw new ArgumentNullException("array"); public virtual int MaxStackSize => 0; public virtual bool InitLocals => false; public virtual byte[] GetILAsByteArray() => null; - public virtual IList<ExceptionHandlingClause> ExceptionHandlingClauses { get { throw new ArgumentNullException("array"); } } + public virtual IList<ExceptionHandlingClause> ExceptionHandlingClauses => throw new ArgumentNullException("array"); } } + diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeCallableAttribute.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeCallableAttribute.cs index 32fc29593..bad4d7f6a 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeCallableAttribute.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeCallableAttribute.cs @@ -2,23 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Runtime.CompilerServices; - namespace System.Runtime.InteropServices { - //BARTOK expects + /// <summary> + /// Any method marked with NativeCallableAttribute can be directly called from + /// native code. The function token can be loaded to a local variable using LDFTN + /// and passed as a callback to native method. + /// </summary> [AttributeUsage(AttributeTargets.Method)] public sealed class NativeCallableAttribute : Attribute { - // Optional. If omitted, then the method is native callable, but no EAT is emitted. - public string EntryPoint; - - // Optional. If omitted a default will be chosen by the compiler. - public CallingConvention CallingConvention; - public NativeCallableAttribute() { } + + /// <summary> + /// Optional. If omitted, compiler will choose one for you. + /// </summary> + public CallingConvention CallingConvention; + + /// <summary> + /// Optional. If omitted, then the method is native callable, but no EAT is emitted. + /// </summary> + public string EntryPoint; } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128.cs index f61310e4e..b2e72ddf8 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128.cs @@ -13,12 +13,12 @@ namespace System.Runtime.Intrinsics [DebuggerDisplay("{DisplayString,nq}")] [DebuggerTypeProxy(typeof(Vector128DebugView<>))] [StructLayout(LayoutKind.Sequential, Size = 16)] - public struct Vector128<T> where T : struct + public readonly struct Vector128<T> where T : struct { // These fields exist to ensure the alignment is 8, rather than 1. // This also allows the debug view to work https://github.com/dotnet/coreclr/issues/15694) - private ulong _00; - private ulong _01; + private readonly ulong _00; + private readonly ulong _01; private unsafe string DisplayString { diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128DebugView.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128DebugView.cs index 7119757b7..ccdc65594 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128DebugView.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector128DebugView.cs @@ -6,9 +6,9 @@ using Internal.Runtime.CompilerServices; namespace System.Runtime.Intrinsics { - internal struct Vector128DebugView<T> where T : struct + internal readonly struct Vector128DebugView<T> where T : struct { - private Vector128<T> _value; + private readonly Vector128<T> _value; public Vector128DebugView(Vector128<T> value) { diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256.cs index 88fd9973e..48744bb5e 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256.cs @@ -13,14 +13,14 @@ namespace System.Runtime.Intrinsics [DebuggerDisplay("{DisplayString,nq}")] [DebuggerTypeProxy(typeof(Vector256DebugView<>))] [StructLayout(LayoutKind.Sequential, Size = 32)] - public struct Vector256<T> where T : struct + public readonly struct Vector256<T> where T : struct { // These fields exist to ensure the alignment is 8, rather than 1. // This also allows the debug view to work https://github.com/dotnet/coreclr/issues/15694) - private ulong _00; - private ulong _01; - private ulong _02; - private ulong _03; + private readonly ulong _00; + private readonly ulong _01; + private readonly ulong _02; + private readonly ulong _03; private unsafe string DisplayString { diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256DebugView.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256DebugView.cs index f07277604..5131341ad 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256DebugView.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector256DebugView.cs @@ -6,9 +6,9 @@ using Internal.Runtime.CompilerServices; namespace System.Runtime.Intrinsics { - internal struct Vector256DebugView<T> where T : struct + internal readonly struct Vector256DebugView<T> where T : struct { - private Vector256<T> _value; + private readonly Vector256<T> _value; public Vector256DebugView(Vector256<T> value) { diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64.cs index 776fe2017..8ce90b9d0 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64.cs @@ -13,11 +13,11 @@ namespace System.Runtime.Intrinsics [DebuggerDisplay("{DisplayString,nq}")] [DebuggerTypeProxy(typeof(Vector64DebugView<>))] [StructLayout(LayoutKind.Sequential, Size = 8)] - public struct Vector64<T> where T : struct + public readonly struct Vector64<T> where T : struct { // These fields exist to ensure the alignment is 8, rather than 1. // This also allows the debug view to work https://github.com/dotnet/coreclr/issues/15694) - private ulong _00; + private readonly ulong _00; private unsafe string DisplayString { diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64DebugView.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64DebugView.cs index 129832a3c..878e29949 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64DebugView.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/Vector64DebugView.cs @@ -6,9 +6,9 @@ using Internal.Runtime.CompilerServices; namespace System.Runtime.Intrinsics { - internal struct Vector64DebugView<T> where T : struct + internal readonly struct Vector64DebugView<T> where T : struct { - private Vector64<T> _value; + private readonly Vector64<T> _value; public Vector64DebugView(Vector64<T> value) { diff --git a/src/System.Private.CoreLib/shared/System/String.Comparison.cs b/src/System.Private.CoreLib/shared/System/String.Comparison.cs index 3ddc90a50..8ac31796b 100644 --- a/src/System.Private.CoreLib/shared/System/String.Comparison.cs +++ b/src/System.Private.CoreLib/shared/System/String.Comparison.cs @@ -753,54 +753,37 @@ namespace System // that string.Equals(A, B, C), then they will return the same hash code with this comparison C. public int GetHashCode(StringComparison comparisonType) => StringComparer.FromComparison(comparisonType).GetHashCode(this); - // Use this if and only if you need the hashcode to not change across app domains (e.g. you have an app domain agile - // hash table). - internal int GetLegacyNonRandomizedHashCode() + // Use this if and only if 'Denial of Service' attacks are not a concern (i.e. never used for free-form user input), + // or are otherwise mitigated + internal unsafe int GetNonRandomizedHashCode() { - unsafe + fixed (char* src = &_firstChar) { - fixed (char* src = &_firstChar) - { - Debug.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'"); - Debug.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary"); -#if BIT64 - int hash1 = 5381; -#else // !BIT64 (32) - int hash1 = (5381<<16) + 5381; -#endif - int hash2 = hash1; + Debug.Assert(src[this.Length] == '\0', "src[this.Length] == '\\0'"); + Debug.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary"); -#if BIT64 - int c; - char* s = src; - while ((c = s[0]) != 0) - { - hash1 = ((hash1 << 5) + hash1) ^ c; - c = s[1]; - if (c == 0) - break; - hash2 = ((hash2 << 5) + hash2) ^ c; - s += 2; - } -#else // !BIT64 (32) - // 32 bit machines. - int* pint = (int *)src; - int len = this.Length; - while (len > 2) - { - hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0]; - hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1]; - pint += 2; - len -= 4; - } + uint hash1 = (5381 << 16) + 5381; + uint hash2 = hash1; - if (len > 0) - { - hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0]; - } -#endif - return hash1 + (hash2 * 1566083941); + uint* ptr = (uint*)src; + int length = this.Length; + + while (length > 2) + { + length -= 4; + // Where length is 4n-1 (e.g. 3,7,11,15,19) this additionally consumes the null terminator + hash1 = (((hash1 << 5) | (hash1 >> 27)) + hash1) ^ ptr[0]; + hash2 = (((hash2 << 5) | (hash2 >> 27)) + hash2) ^ ptr[1]; + ptr += 2; } + + if (length > 0) + { + // Where length is 4n-3 (e.g. 1,5,9,13,17) this additionally consumes the null terminator + hash2 = (((hash2 << 5) | (hash2 >> 27)) + hash2) ^ ptr[0]; + } + + return (int)(hash1 + (hash2 * 1566083941)); } } diff --git a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs index 045a40b65..21cc3dc01 100644 --- a/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs @@ -73,7 +73,7 @@ namespace System.Text public override string ToString() { - var s = new string(_chars.Slice(0, _pos)); + var s = _chars.Slice(0, _pos).ToString(); Dispose(); return s; } diff --git a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs b/src/System.Private.CoreLib/shared/System/Threading/SpinLock.cs index 7f3cb120b..2b0610539 100644 --- a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/SpinLock.cs @@ -12,12 +12,8 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Diagnostics; -using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using Internal.Runtime.Augments; -using Internal.Threading.Tracing; - namespace System.Threading { /// <summary> @@ -66,35 +62,28 @@ namespace System.Threading // // There are several masks and constants below for convenience. - private volatile int m_owner; - - // The multiplier factor for the each spinning iteration - // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results - private const int SPINNING_FACTOR = 100; + private volatile int _owner; // After how many yields, call Sleep(1) private const int SLEEP_ONE_FREQUENCY = 40; - // After how many yields, call Sleep(0) - private const int SLEEP_ZERO_FREQUENCY = 10; - // After how many yields, check the timeout private const int TIMEOUT_CHECK_FREQUENCY = 10; // Thr thread tracking disabled mask - private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000); //1000 0000 0000 0000 0000 0000 0000 0000 + private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000); // 1000 0000 0000 0000 0000 0000 0000 0000 //the lock is held by some thread, but we don't know which - private const int LOCK_ANONYMOUS_OWNED = 0x1; //0000 0000 0000 0000 0000 0000 0000 0001 + private const int LOCK_ANONYMOUS_OWNED = 0x1; // 0000 0000 0000 0000 0000 0000 0000 0001 // Waiters mask if the thread tracking is disabled - private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1); //0111 1111 1111 1111 1111 1111 1111 1110 + private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1); // 0111 1111 1111 1111 1111 1111 1111 1110 // The Thread tacking is disabled and the lock bit is set, used in Enter fast path to make sure the id is disabled and lock is available - private const int ID_DISABLED_AND_ANONYMOUS_OWNED = unchecked((int)0x80000001); //1000 0000 0000 0000 0000 0000 0000 0001 + private const int ID_DISABLED_AND_ANONYMOUS_OWNED = unchecked((int)0x80000001); // 1000 0000 0000 0000 0000 0000 0000 0001 // If the thread is unowned if: - // m_owner zero and the threa tracking is enabled + // m_owner zero and the thread tracking is enabled // m_owner & LOCK_ANONYMOUS_OWNED = zero and the thread tracking is disabled private const int LOCK_UNOWNED = 0; @@ -104,7 +93,7 @@ namespace System.Threading private static int MAXIMUM_WAITERS = WAITERS_MASK; [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int CompareExchange(ref int location, int value, int comparand, ref bool success) + private static int CompareExchange(ref int location, int value, int comparand, ref bool success) { int result = Interlocked.CompareExchange(ref location, value, comparand); success = (result == comparand); @@ -122,10 +111,10 @@ namespace System.Threading /// purposes.</param> public SpinLock(bool enableThreadOwnerTracking) { - m_owner = LOCK_UNOWNED; + _owner = LOCK_UNOWNED; if (!enableThreadOwnerTracking) { - m_owner |= LOCK_ID_DISABLE_MASK; + _owner |= LOCK_ID_DISABLE_MASK; Debug.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now"); } } @@ -159,11 +148,11 @@ namespace System.Threading /// </exception> public void Enter(ref bool lockTaken) { - //Try to keep the code and branching in this method as small as possible in order to inline the method - int observedOwner = m_owner; - if (lockTaken || //invalid parameter - (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) //acquiring the lock failed + // Try to keep the code and branching in this method as small as possible in order to inline the method + int observedOwner = _owner; + if (lockTaken || // invalid parameter + (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || // thread tracking is enabled or the lock is already acquired + CompareExchange(ref _owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) //acquiring the lock failed ContinueTryEnter(Timeout.Infinite, ref lockTaken); // Then try the slow path if any of the above conditions is met } @@ -187,7 +176,7 @@ namespace System.Threading /// </exception> public void TryEnter(ref bool lockTaken) { - int observedOwner = m_owner; + int observedOwner = _owner; if (((observedOwner & LOCK_ID_DISABLE_MASK) == 0) | lockTaken) { // Thread tracking enabled or invalid arg. Take slow path. @@ -201,7 +190,7 @@ namespace System.Threading else { // Lock wasn't held; try to acquire it. - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken); + CompareExchange(ref _owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken); } } @@ -269,18 +258,18 @@ namespace System.Threading /// a negative number other than -1, which represents an infinite time-out.</exception> public void TryEnter(int millisecondsTimeout, ref bool lockTaken) { - int observedOwner = m_owner; + int observedOwner = _owner; if (millisecondsTimeout < -1 || //invalid parameter lockTaken || //invalid parameter (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) // acquiring the lock failed + CompareExchange(ref _owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) // acquiring the lock failed ContinueTryEnter(millisecondsTimeout, ref lockTaken); // The call the slow pth } /// <summary> /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the - /// performance. This method assumed that the parameter are validated in Enter ir TryENter method + /// performance. This method assumed that the parameter are validated in Enter or TryEnter method. /// </summary> /// <param name="millisecondsTimeout">The timeout milliseconds</param> /// <param name="lockTaken">The lockTaken param</param> @@ -290,7 +279,7 @@ namespace System.Threading if (lockTaken) { lockTaken = false; - throw new System.ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); + throw new ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) @@ -305,11 +294,6 @@ namespace System.Threading startTime = TimeoutHelper.GetTime(); } - if (SpinLockTrace.Enabled) - { - SpinLockTrace.SpinLock_FastPathFailed(m_owner); - } - if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode @@ -332,10 +316,10 @@ namespace System.Threading //***Step 1, take the lock or update the waiters // try to acquire the lock directly if possible or update the waiters count - observedOwner = m_owner; + observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { - if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) + if (CompareExchange(ref _owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Acquired lock return; @@ -352,89 +336,50 @@ namespace System.Threading // Did not acquire lock as owned and timeout is 0 so fail fast return; } - else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow + else //failed to acquire the lock, then try to update the waiters. If the waiters count reached the maximum, just break the loop to avoid overflow { if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS) - turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1; - } - - //***Step 2. Spinning - //lock acquired failed and waiters updated - int processorCount = PlatformHelper.ProcessorCount; - if (turn < processorCount) - { - int processFactor = 1; - for (int i = 1; i <= turn * SPINNING_FACTOR; i++) - { - RuntimeThread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); - if (processFactor < processorCount) - processFactor++; - observedOwner = m_owner; - if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) - { - int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero - observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters - : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit - Debug.Assert((newOwner & WAITERS_MASK) >= 0); - - if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) - { - return; - } - } - } - - // Check the timeout. - if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { - DecrementWaiters(); - return; + // This can still overflow, but maybe there will never be that many waiters + turn = (Interlocked.Add(ref _owner, 2) & WAITERS_MASK) >> 1; } } - //*** Step 3, Yielding - //Sleep(1) every 50 yields - int yieldsoFar = 0; + // lock acquired failed and waiters updated + + //*** Step 2, Spinning and Yielding + var spinner = new SpinWait(); + if (turn > PlatformHelper.ProcessorCount) + { + spinner.Count = SpinWait.YieldThreshold; + } while (true) { - observedOwner = m_owner; + spinner.SpinOnce(SLEEP_ONE_FREQUENCY); + + observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero - observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters + observedOwner | 1 // don't decrement it. just set the lock bit, it is zero because a previous call of Exit(false) which corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); - if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) + if (CompareExchange(ref _owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } - if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) - { - RuntimeThread.Sleep(1); - } - else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) - { - RuntimeThread.Sleep(0); - } - else - { - RuntimeThread.Yield(); - } - - if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) + if (spinner.Count % TIMEOUT_CHECK_FREQUENCY == 0) { - //Check the timeout. + // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } - - yieldsoFar++; } } @@ -446,9 +391,9 @@ namespace System.Threading SpinWait spinner = new SpinWait(); while (true) { - int observedOwner = m_owner; + int observedOwner = _owner; if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false) - if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner) + if (Interlocked.CompareExchange(ref _owner, observedOwner - 2, observedOwner) == observedOwner) { Debug.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped break; @@ -467,8 +412,8 @@ namespace System.Threading int lockUnowned = 0; // We are using thread IDs to mark ownership. Snap the thread ID and check for recursion. // We also must or the ID enablement bit, to ensure we propagate when we CAS it in. - int m_newOwner = Environment.CurrentManagedThreadId; - if (m_owner == m_newOwner) + int newOwner = Environment.CurrentManagedThreadId; + if (_owner == newOwner) { // We don't allow lock recursion. throw new LockRecursionException(SR.SpinLock_TryEnter_LockRecursionException); @@ -486,9 +431,9 @@ namespace System.Threading // Test before trying to CAS, to avoid acquiring the line exclusively unnecessarily. - if (m_owner == lockUnowned) + if (_owner == lockUnowned) { - if (CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) + if (CompareExchange(ref _owner, newOwner, lockUnowned, ref lockTaken) == lockUnowned) { return; } @@ -516,10 +461,10 @@ namespace System.Threading public void Exit() { //This is the fast path for the thread tracking is disabled, otherwise go to the slow path - if ((m_owner & LOCK_ID_DISABLE_MASK) == 0) + if ((_owner & LOCK_ID_DISABLE_MASK) == 0) ExitSlowPath(true); else - Interlocked.Decrement(ref m_owner); + Interlocked.Decrement(ref _owner); } /// <summary> @@ -540,16 +485,18 @@ namespace System.Threading /// </exception> public void Exit(bool useMemoryBarrier) { - // This is the fast path for the thread tracking is diabled and not to use memory barrier, otherwise go to the slow path + // This is the fast path for the thread tracking is disabled and not to use memory barrier, otherwise go to the slow path // The reason not to add else statement if the usememorybarrier is that it will add more branching in the code and will prevent // method inlining, so this is optimized for useMemoryBarrier=false and Exit() overload optimized for useMemoryBarrier=true. - int tmpOwner = m_owner; + int tmpOwner = _owner; if ((tmpOwner & LOCK_ID_DISABLE_MASK) != 0 & !useMemoryBarrier) { - m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); + _owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); } else + { ExitSlowPath(useMemoryBarrier); + } } /// <summary> @@ -561,28 +508,33 @@ namespace System.Threading /// </param> private void ExitSlowPath(bool useMemoryBarrier) { - bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0; + bool threadTrackingEnabled = (_owner & LOCK_ID_DISABLE_MASK) == 0; if (threadTrackingEnabled && !IsHeldByCurrentThread) { - throw new System.Threading.SynchronizationLockException( - SR.SpinLock_Exit_SynchronizationLockException); + throw new SynchronizationLockException(SR.SpinLock_Exit_SynchronizationLockException); } if (useMemoryBarrier) { if (threadTrackingEnabled) - Interlocked.Exchange(ref m_owner, LOCK_UNOWNED); + { + Interlocked.Exchange(ref _owner, LOCK_UNOWNED); + } else - Interlocked.Decrement(ref m_owner); + { + Interlocked.Decrement(ref _owner); + } } else { if (threadTrackingEnabled) - m_owner = LOCK_UNOWNED; + { + _owner = LOCK_UNOWNED; + } else { - int tmpOwner = m_owner; - m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); + int tmpOwner = _owner; + _owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); } } } @@ -595,9 +547,9 @@ namespace System.Threading get { if (IsThreadOwnerTrackingEnabled) - return m_owner != LOCK_UNOWNED; + return _owner != LOCK_UNOWNED; - return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED; + return (_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED; } } @@ -623,15 +575,12 @@ namespace System.Threading { throw new InvalidOperationException(SR.SpinLock_IsHeldByCurrentThread); } - return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Environment.CurrentManagedThreadId); + return ((_owner & (~LOCK_ID_DISABLE_MASK)) == Environment.CurrentManagedThreadId); } } /// <summary>Gets whether thread ownership tracking is enabled for this instance.</summary> - public bool IsThreadOwnerTrackingEnabled - { - get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; } - } + public bool IsThreadOwnerTrackingEnabled => (_owner & LOCK_ID_DISABLE_MASK) == 0; #region Debugger proxy class /// <summary> @@ -640,7 +589,7 @@ namespace System.Threading internal class SystemThreading_SpinLockDebugView { // SpinLock object - private SpinLock m_spinLock; + private SpinLock _spinLock; /// <summary> /// SystemThreading_SpinLockDebugView constructor @@ -649,7 +598,7 @@ namespace System.Threading public SystemThreading_SpinLockDebugView(SpinLock spinLock) { // Note that this makes a copy of the SpinLock (struct). It doesn't hold a reference to it. - m_spinLock = spinLock; + _spinLock = spinLock; } /// <summary> @@ -661,7 +610,7 @@ namespace System.Threading { try { - return m_spinLock.IsHeldByCurrentThread; + return _spinLock.IsHeldByCurrentThread; } catch (InvalidOperationException) { @@ -677,9 +626,9 @@ namespace System.Threading { get { - if (m_spinLock.IsThreadOwnerTrackingEnabled) + if (_spinLock.IsThreadOwnerTrackingEnabled) { - return m_spinLock.m_owner; + return _spinLock._owner; } else { @@ -692,10 +641,7 @@ namespace System.Threading /// <summary> /// Gets whether the lock is currently held by any thread or not. /// </summary> - public bool IsHeld - { - get { return m_spinLock.IsHeld; } - } + public bool IsHeld => _spinLock.IsHeld; } #endregion diff --git a/src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs b/src/System.Private.CoreLib/shared/System/Threading/ThreadLocal.cs index ad6da89a9..9f4beae9c 100644 --- a/src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/ThreadLocal.cs @@ -1,23 +1,13 @@ // 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. -#pragma warning disable 0420 -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// +using System.Collections.Generic; +using System.Diagnostics; -// // A class that provides a simple, lightweight implementation of thread-local lazy-initialization, where a value is initialized once per accessing // thread; this provides an alternative to using a ThreadStatic static variable and having // to check the variable prior to every access to see if it's been initialized. -// -// -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System.Diagnostics; -using System.Collections.Generic; namespace System.Threading { @@ -37,15 +27,13 @@ namespace System.Threading public class ThreadLocal<T> : IDisposable { // a delegate that returns the created value, if null the created value will be default(T) - private Func<T> m_valueFactory; + private Func<T> _valueFactory; - // // ts_slotArray is a table of thread-local values for all ThreadLocal<T> instances // // So, when a thread reads ts_slotArray, it gets back an array of *all* ThreadLocal<T> values for this thread and this T. - // The slot relevant to this particular ThreadLocal<T> instance is determined by the m_idComplement instance field stored in + // The slot relevant to this particular ThreadLocal<T> instance is determined by the _idComplement instance field stored in // the ThreadLocal<T> instance. - // [ThreadStatic] private static LinkedSlotVolatile[] ts_slotArray; @@ -55,22 +43,22 @@ namespace System.Threading // Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish // between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or // possibly due to a memory model issue in user code. - private int m_idComplement; + private int _idComplement; // This field is set to true when the constructor completes. That is helpful for recognizing whether a constructor // threw an exception - either due to invalid argument or due to a thread abort. Finally, the field is set to false // when the instance is disposed. - private volatile bool m_initialized; + private volatile bool _initialized; // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock. private static IdManager s_idManager = new IdManager(); // A linked list of all values associated with this ThreadLocal<T> instance. // We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field. - private LinkedSlot m_linkedSlot = new LinkedSlot(null); + private LinkedSlot _linkedSlot = new LinkedSlot(null); // Whether the Values property is supported - private bool m_trackAllValues; + private bool _trackAllValues; /// <summary> /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance. @@ -131,19 +119,19 @@ namespace System.Threading private void Initialize(Func<T> valueFactory, bool trackAllValues) { - m_valueFactory = valueFactory; - m_trackAllValues = trackAllValues; + _valueFactory = valueFactory; + _trackAllValues = trackAllValues; - // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set m_initialized + // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set _initialized // in a finally block, to avoid a thread abort in between the two statements. try { } finally { - m_idComplement = ~s_idManager.GetId(); + _idComplement = ~s_idManager.GetId(); - // As the last step, mark the instance as fully initialized. (Otherwise, if m_initialized=false, we know that an exception + // As the last step, mark the instance as fully initialized. (Otherwise, if _initialized=false, we know that an exception // occurred in the constructor.) - m_initialized = true; + _initialized = true; } } @@ -183,23 +171,23 @@ namespace System.Threading { int id; - using (LockHolder.Hold(s_idManager.m_lock)) + lock (s_idManager) { - id = ~m_idComplement; - m_idComplement = 0; + id = ~_idComplement; + _idComplement = 0; - if (id < 0 || !m_initialized) + if (id < 0 || !_initialized) { - Debug.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized"); + Debug.Assert(id >= 0 || !_initialized, "expected id >= 0 if initialized"); // Handle double Dispose calls or disposal of an instance whose constructor threw an exception. return; } - m_initialized = false; + _initialized = false; - for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next) + for (LinkedSlot linkedSlot = _linkedSlot._next; linkedSlot != null; linkedSlot = linkedSlot._next) { - LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray; + LinkedSlotVolatile[] slotArray = linkedSlot._slotArray; if (slotArray == null) { @@ -208,15 +196,15 @@ namespace System.Threading } // Remove the reference from the LinkedSlot to the slot table. - linkedSlot.SlotArray = null; + linkedSlot._slotArray = null; // And clear the references from the slot table to the linked slot and the value so that // both can get garbage collected. - slotArray[id].Value.Value = default(T); + slotArray[id].Value._value = default; slotArray[id].Value = null; } } - m_linkedSlot = null; + _linkedSlot = null; s_idManager.ReturnId(id); } @@ -265,7 +253,7 @@ namespace System.Threading { LinkedSlotVolatile[] slotArray = ts_slotArray; LinkedSlot slot; - int id = ~m_idComplement; + int id = ~_idComplement; // // Attempt to get the value using the fast path @@ -274,7 +262,7 @@ namespace System.Threading && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)? && id < slotArray.Length // Is the table large enough? && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID? - && m_initialized // Has the instance *still* not been disposed (important for races with Dispose)? + && _initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)? ) { // We verified that the instance has not been disposed *after* we got a reference to the slot. @@ -282,7 +270,7 @@ namespace System.Threading // // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read // will not be reordered before the read of slotArray[id]. - return slot.Value; + return slot._value; } return GetValueSlow(); @@ -291,16 +279,14 @@ namespace System.Threading { LinkedSlotVolatile[] slotArray = ts_slotArray; LinkedSlot slot; - int id = ~m_idComplement; + int id = ~_idComplement; - // // Attempt to set the value using the fast path - // if (slotArray != null // Has the slot array been initialized? && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)? && id < slotArray.Length // Is the table large enough? && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID? - && m_initialized // Has the instance *still* not been disposed (important for races with Dispose)? + && _initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)? ) { // We verified that the instance has not been disposed *after* we got a reference to the slot. @@ -308,7 +294,7 @@ namespace System.Threading // // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read // will not be reordered before the read of slotArray[id]. - slot.Value = value; + slot._value = value; } else { @@ -320,23 +306,23 @@ namespace System.Threading private T GetValueSlow() { // If the object has been disposed, the id will be -1. - int id = ~m_idComplement; + int id = ~_idComplement; if (id < 0) { throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } - //Debugger.NotifyOfCrossThreadDependency(); + Debugger.NotifyOfCrossThreadDependency(); // Determine the initial value T value; - if (m_valueFactory == null) + if (_valueFactory == null) { - value = default(T); + value = default; } else { - value = m_valueFactory(); + value = _valueFactory(); if (IsValueCreated) { @@ -351,7 +337,7 @@ namespace System.Threading private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray) { - int id = ~m_idComplement; + int id = ~_idComplement; // If the object has been disposed, id will be -1. if (id < 0) @@ -363,7 +349,7 @@ namespace System.Threading if (slotArray == null) { slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)]; - ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues); + ts_finalizationHelper = new FinalizationHelper(slotArray, _trackAllValues); ts_slotArray = slotArray; } @@ -392,12 +378,12 @@ namespace System.Threading // if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was // created, we definitely won't assign the value into the wrong instance. - if (!m_initialized) + if (!_initialized) { throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } - slot.Value = value; + slot._value = value; } } @@ -410,32 +396,30 @@ namespace System.Threading var linkedSlot = new LinkedSlot(slotArray); // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array - using (LockHolder.Hold(s_idManager.m_lock)) + lock (s_idManager) { // Check that the instance has not been disposed. It is important to check this under a lock, since // Dispose also executes under a lock. - if (!m_initialized) + if (!_initialized) { throw new ObjectDisposedException(SR.ThreadLocal_Disposed); } - LinkedSlot firstRealNode = m_linkedSlot.Next; + LinkedSlot firstRealNode = _linkedSlot._next; - // // Insert linkedSlot between nodes m_linkedSlot and firstRealNode. - // (m_linkedSlot is the dummy head node that should always be in the front.) - // - linkedSlot.Next = firstRealNode; - linkedSlot.Previous = m_linkedSlot; - linkedSlot.Value = value; + // (_linkedSlot is the dummy head node that should always be in the front.) + linkedSlot._next = firstRealNode; + linkedSlot._previous = _linkedSlot; + linkedSlot._value = value; if (firstRealNode != null) { - firstRealNode.Previous = linkedSlot; + firstRealNode._previous = linkedSlot; } - m_linkedSlot.Next = linkedSlot; + _linkedSlot._next = linkedSlot; - // Assigning the slot under a lock prevents a race with Dispose (dispose also acquires the lock). + // Assigning the slot under a lock prevents a race condition with Dispose (dispose also acquires the lock). // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created // with the same ID, and the write would go to the wrong instance. slotArray[id].Value = linkedSlot; @@ -452,7 +436,7 @@ namespace System.Threading { get { - if (!m_trackAllValues) + if (!_trackAllValues) { throw new InvalidOperationException(SR.ThreadLocal_ValuesNotAvailable); } @@ -464,21 +448,21 @@ namespace System.Threading } /// <summary>Gets all of the threads' values in a list.</summary> - private LowLevelListWithIList<T> GetValuesAsList() + private List<T> GetValuesAsList() { - LowLevelListWithIList<T> valueList = new LowLevelListWithIList<T>(); - int id = ~m_idComplement; + List<T> valueList = new List<T>(); + int id = ~_idComplement; if (id == -1) { return null; } // Walk over the linked list of slots and gather the values associated with this ThreadLocal instance. - for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next) + for (LinkedSlot linkedSlot = _linkedSlot._next; linkedSlot != null; linkedSlot = linkedSlot._next) { // We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot // objects will never be assigned to another ThreadLocal instance. - valueList.Add(linkedSlot.Value); + valueList.Add(linkedSlot._value); } return valueList; @@ -490,7 +474,7 @@ namespace System.Threading get { int count = 0; - for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next) + for (LinkedSlot linkedSlot = _linkedSlot._next; linkedSlot != null; linkedSlot = linkedSlot._next) { count++; } @@ -508,7 +492,7 @@ namespace System.Threading { get { - int id = ~m_idComplement; + int id = ~_idComplement; if (id < 0) { throw new ObjectDisposedException(SR.ThreadLocal_Disposed); @@ -527,17 +511,17 @@ namespace System.Threading get { LinkedSlotVolatile[] slotArray = ts_slotArray; - int id = ~m_idComplement; + int id = ~_idComplement; LinkedSlot slot; - if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !m_initialized) - return default(T); - return slot.Value; + if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !_initialized) + return default; + return slot._value; } } /// <summary>Gets the values of all threads that accessed the ThreadLocal<T>.</summary> - internal IList<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed + internal List<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed { get { return GetValuesAsList(); } } @@ -559,14 +543,14 @@ namespace System.Threading // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while // the value continues to be referenced from the new (larger) array. // - using (LockHolder.Hold(s_idManager.m_lock)) + lock (s_idManager) { for (int i = 0; i < table.Length; i++) { LinkedSlot linkedSlot = table[i].Value; - if (linkedSlot != null && linkedSlot.SlotArray != null) + if (linkedSlot != null && linkedSlot._slotArray != null) { - linkedSlot.SlotArray = newTable; + linkedSlot._slotArray = newTable; newTable[i] = table[i]; } } @@ -575,14 +559,12 @@ namespace System.Threading table = newTable; } - private const int MaxArrayLength = int.MaxValue; - /// <summary> /// Chooses the next larger table size /// </summary> private static int GetNewTableSize(int minSize) { - if ((uint)minSize > MaxArrayLength) + if ((uint)minSize > Array.MaxArrayLength) { // Intentionally return a value that will result in an OutOfMemoryException return int.MaxValue; @@ -593,13 +575,13 @@ namespace System.Threading // Round up the size to the next power of 2 // // The algorithm takes three steps: - // input -> subtract one -> propagate 1-bits to the right -> add one + // input -> subtract one -> propagate 1-bits to the right -> add one // // Let's take a look at the 3 steps in both interesting cases: where the input // is (Example 1) and isn't (Example 2) a power of 2. // - // Example 1: 100000 -> 011111 -> 011111 -> 100000 - // Example 2: 011010 -> 011001 -> 011111 -> 100000 + // Example 1: 100000 -> 011111 -> 011111 -> 100000 + // Example 2: 011010 -> 011001 -> 011111 -> 100000 // int newSize = minSize; @@ -617,9 +599,9 @@ namespace System.Threading newSize++; // Don't set newSize to more than Array.MaxArrayLength - if ((uint)newSize > MaxArrayLength) + if ((uint)newSize > Array.MaxArrayLength) { - newSize = MaxArrayLength; + newSize = Array.MaxArrayLength; } return newSize; @@ -629,7 +611,7 @@ namespace System.Threading /// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics /// on array accesses. /// </summary> - internal struct LinkedSlotVolatile + private struct LinkedSlotVolatile { internal volatile LinkedSlot Value; } @@ -642,24 +624,24 @@ namespace System.Threading /// 1. If SlotArray is not null, the value is in SlotArray.Table[id] /// 2. If SlotArray is null, the value is in FinalValue. /// </summary> - internal sealed class LinkedSlot + private sealed class LinkedSlot { internal LinkedSlot(LinkedSlotVolatile[] slotArray) { - SlotArray = slotArray; + _slotArray = slotArray; } // The next LinkedSlot for this ThreadLocal<> instance - internal volatile LinkedSlot Next; + internal volatile LinkedSlot _next; // The previous LinkedSlot for this ThreadLocal<> instance - internal volatile LinkedSlot Previous; + internal volatile LinkedSlot _previous; // The SlotArray that stores this LinkedSlot at SlotArray.Table[id]. - internal volatile LinkedSlotVolatile[] SlotArray; + internal volatile LinkedSlotVolatile[] _slotArray; // The value for this slot. - internal T Value; + internal T _value; } /// <summary> @@ -668,34 +650,32 @@ namespace System.Threading private class IdManager { // The next ID to try - private int m_nextIdToTry = 0; + private int _nextIdToTry = 0; // Stores whether each ID is free or not. Additionally, the object is also used as a lock for the IdManager. - private LowLevelList<bool> m_freeIds = new LowLevelList<bool>(); - - internal Lock m_lock = new Lock(); + private List<bool> _freeIds = new List<bool>(); internal int GetId() { - using (LockHolder.Hold(m_lock)) + lock (_freeIds) { - int availableId = m_nextIdToTry; - while (availableId < m_freeIds.Count) + int availableId = _nextIdToTry; + while (availableId < _freeIds.Count) { - if (m_freeIds[availableId]) { break; } + if (_freeIds[availableId]) { break; } availableId++; } - if (availableId == m_freeIds.Count) + if (availableId == _freeIds.Count) { - m_freeIds.Add(false); + _freeIds.Add(false); } else { - m_freeIds[availableId] = false; + _freeIds[availableId] = false; } - m_nextIdToTry = availableId + 1; + _nextIdToTry = availableId + 1; return availableId; } @@ -704,10 +684,10 @@ namespace System.Threading // Return an ID to the pool internal void ReturnId(int id) { - using (LockHolder.Hold(m_lock)) + lock (_freeIds) { - m_freeIds[id] = true; - if (id < m_nextIdToTry) m_nextIdToTry = id; + _freeIds[id] = true; + if (id < _nextIdToTry) _nextIdToTry = id; } } } @@ -716,7 +696,7 @@ namespace System.Threading /// A class that facilitates ThreadLocal cleanup after a thread exits. /// /// After a thread with an associated thread-local table has exited, the FinalizationHelper - /// is reponsible for removing back-references to the table. Since an instance of FinalizationHelper + /// is responsible for removing back-references to the table. Since an instance of FinalizationHelper /// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once /// the thread has exited. /// @@ -724,15 +704,15 @@ namespace System.Threading /// (all those LinkedSlot instances can be found by following references from the table slots) and /// releases the table so that it can get GC'd. /// </summary> - internal class FinalizationHelper + private class FinalizationHelper { internal LinkedSlotVolatile[] SlotArray; - private bool m_trackAllValues; + private bool _trackAllValues; internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues) { SlotArray = slotArray; - m_trackAllValues = trackAllValues; + _trackAllValues = trackAllValues; } ~FinalizationHelper() @@ -749,25 +729,25 @@ namespace System.Threading continue; } - if (m_trackAllValues) + if (_trackAllValues) { // Set the SlotArray field to null to release the slot array. - linkedSlot.SlotArray = null; + linkedSlot._slotArray = null; } else { // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to // the table will be have been removed, and so the table can get GC'd. - using (LockHolder.Hold(s_idManager.m_lock)) + lock (s_idManager) { - if (linkedSlot.Next != null) + if (linkedSlot._next != null) { - linkedSlot.Next.Previous = linkedSlot.Previous; + linkedSlot._next._previous = linkedSlot._previous; } // Since the list uses a dummy head node, the Previous reference should never be null. - Debug.Assert(linkedSlot.Previous != null); - linkedSlot.Previous.Next = linkedSlot.Next; + Debug.Assert(linkedSlot._previous != null); + linkedSlot._previous._next = linkedSlot._next; } } } @@ -780,37 +760,22 @@ namespace System.Threading internal sealed class SystemThreading_ThreadLocalDebugView<T> { //The ThreadLocal object being viewed. - private readonly ThreadLocal<T> m_tlocal; + private readonly ThreadLocal<T> _tlocal; /// <summary>Constructs a new debugger view object for the provided ThreadLocal object.</summary> /// <param name="tlocal">A ThreadLocal object to browse in the debugger.</param> public SystemThreading_ThreadLocalDebugView(ThreadLocal<T> tlocal) { - m_tlocal = tlocal; + _tlocal = tlocal; } /// <summary>Returns whether the ThreadLocal object is initialized or not.</summary> - public bool IsValueCreated - { - get { return m_tlocal.IsValueCreated; } - } + public bool IsValueCreated => _tlocal.IsValueCreated; /// <summary>Returns the value of the ThreadLocal object.</summary> - public T Value - { - get - { - return m_tlocal.ValueForDebugDisplay; - } - } + public T Value => _tlocal.ValueForDebugDisplay; /// <summary>Return all values for all threads that have accessed this instance.</summary> - public IList<T> Values - { - get - { - return m_tlocal.ValuesForDebugDisplay; - } - } + public List<T> Values => _tlocal.ValuesForDebugDisplay; } } diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index 0fb811bca..52d9e03c5 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -192,6 +192,9 @@ <data name="Arg_DuplicateWaitObjectException" xml:space="preserve"> <value>Duplicate objects in argument.</value> </data> + <data name="Arg_EHClauseNotFilter" xml:space="preserve"> + <value>This ExceptionHandlingClause is not a filter.</value> + </data> <data name="Arg_EnumAndObjectMustBeSameType" xml:space="preserve"> <value>Object must be the same type as the enum. The type passed in was '{0}'; the enum type was '{1}'.</value> </data> diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index ee7b79f3b..f898a11a2 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -179,12 +179,9 @@ <Compile Include="System\Reflection\Emit\ReflectionEmitThrower.cs" /> <Compile Include="System\Reflection\Emit\SignatureHelper.cs" /> <Compile Include="System\Reflection\Emit\TypeBuilder.cs" /> - <Compile Include="System\Reflection\ExceptionHandlingClause.cs" /> <Compile Include="System\Reflection\FieldInfo.CoreRT.cs" /> <Compile Include="System\Reflection\LegacyCustomAttributeApis.cs" /> - <Compile Include="System\Reflection\LocalVariableInfo.cs" /> <Compile Include="System\Reflection\MethodBase.CoreRT.cs" /> - <Compile Include="System\Reflection\MethodBody.cs" /> <Compile Include="System\Reflection\Runtime\CustomAttributes\RuntimeImplementedCustomAttributeData.cs" /> <Compile Include="System\Resources\FileBasedResourceGroveler.cs" /> <Compile Include="System\Resources\IResourceGroveler.cs" /> @@ -265,7 +262,6 @@ <Compile Include="System\Runtime\InteropServices\GCHandle.cs" /> <Compile Include="System\Runtime\InteropServices\GCHandleType.cs" /> <Compile Include="System\Runtime\InteropServices\InteropExtensions.cs" /> - <Compile Include="System\Runtime\InteropServices\NativeCallableAttribute.cs" /> <Compile Include="System\Runtime\InteropServices\NativeFunctionPointerWrapper.cs" /> <Compile Include="System\Runtime\InteropServices\PInvokeMarshal.cs" /> <Compile Include="System\Runtime\InteropServices\Marshal.cs" /> @@ -306,7 +302,6 @@ <Compile Include="System\Threading\Monitor.cs" /> <Compile Include="System\Threading\ObjectHeader.cs" Condition="'$(UseSyncTable)' == 'true'" /> <Compile Include="System\Threading\Overlapped.cs" /> - <Compile Include="System\Threading\SpinLock.cs" /> <Compile Include="System\Threading\SynchronizationContext.cs" /> <Compile Include="System\Threading\SynchronizationContext.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" /> <Compile Include="System\Threading\SynchronizationContext.Dummy.cs" Condition="'$(EnableWinRT)' != 'true'" /> @@ -323,7 +318,6 @@ <Compile Include="System\Threading\Tasks\TaskFactory.cs" /> <Compile Include="System\Threading\Tasks\TaskScheduler.cs" /> <Compile Include="System\Threading\Tasks\ThreadPoolTaskScheduler.cs" /> - <Compile Include="System\Threading\ThreadLocal.cs" /> <Compile Include="System\Threading\ThreadPool.cs" /> <Compile Include="System\Threading\ThreadPoolCallbackWrapper.cs" /> <Compile Include="System\Threading\Timer.cs" /> @@ -748,4 +742,4 @@ </ItemGroup> <Import Project="..\shared\System.Private.CoreLib.Shared.projitems" Label="Shared" /> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 3dadda396..34b2fbcc3 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -6,16 +6,27 @@ using System.Globalization; using System.Runtime.Serialization; using System.Configuration.Assemblies; -using Internal.Reflection.Augments; - namespace System.Reflection { public sealed class AssemblyName : ICloneable, IDeserializationCallback, ISerializable { + private string _name; + private byte[] _publicKey; + private byte[] _publicKeyToken; + private CultureInfo _cultureInfo; + private string _codeBase; + private Version _version; + + private StrongNameKeyPair _strongNameKeyPair; + private AssemblyHashAlgorithm _hashAlgorithm; + + private AssemblyVersionCompatibility _versionCompatibility; + private AssemblyNameFlags _flags; + public AssemblyName() { - HashAlgorithm = AssemblyHashAlgorithm.None; - VersionCompatibility = AssemblyVersionCompatibility.SameMachine; + _hashAlgorithm = AssemblyHashAlgorithm.None; + _versionCompatibility = AssemblyVersionCompatibility.SameMachine; _flags = AssemblyNameFlags.None; } @@ -28,19 +39,55 @@ namespace System.Reflection runtimeAssemblyName.CopyToAssemblyName(this); } - public object Clone() + // Set and get the name of the assembly. If this is a weak Name + // then it optionally contains a site. For strong assembly names, + // the name partitions up the strong name's namespace + public string Name { - AssemblyName n = new AssemblyName(); - n.Name = Name; - n._publicKey = (byte[])_publicKey?.Clone(); - n._publicKeyToken = (byte[])_publicKeyToken?.Clone(); - n.CultureInfo = CultureInfo; - n.Version = (Version)Version?.Clone(); - n._flags = _flags; - n.CodeBase = CodeBase; - n.HashAlgorithm = HashAlgorithm; - n.VersionCompatibility = VersionCompatibility; - return n; + get { return _name; } + set { _name = value; } + } + + public Version Version + { + get { return _version; } + set { _version = value; } + } + + // Locales, internally the LCID is used for the match. + public CultureInfo CultureInfo + { + get { return _cultureInfo; } + set { _cultureInfo = value; } + } + + public string CultureName + { + get + { + return (_cultureInfo == null) ? null : _cultureInfo.Name; + } + set + { + _cultureInfo = (value == null) ? null : new CultureInfo(value); + } + } + + public string CodeBase + { + get { return _codeBase; } + set { _codeBase = value; } + } + + public string EscapedCodeBase + { + get + { + if (_codeBase == null) + return null; + else + return EscapeCodeBase(_codeBase); + } } public ProcessorArchitecture ProcessorArchitecture @@ -83,85 +130,105 @@ namespace System.Reflection } } - public string CultureName + // Make a copy of this assembly name. + public object Clone() { - get - { - return CultureInfo?.Name; - } - set - { - CultureInfo = (value == null) ? null : new CultureInfo(value); - } + AssemblyName name = new AssemblyName(); + name._name = _name; + name._publicKey = (byte[])_publicKey?.Clone(); + name._publicKeyToken = (byte[])_publicKeyToken?.Clone(); + name._cultureInfo = _cultureInfo; + name._version = (Version)_version?.Clone(); + name._flags = _flags; + name._codeBase = _codeBase; + name._hashAlgorithm = _hashAlgorithm; + name._versionCompatibility = _versionCompatibility; + return name; } - public CultureInfo CultureInfo { get; set; } + public static AssemblyName GetAssemblyName(string assemblyFile) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported_AssemblyName_GetAssemblyName); + } - public AssemblyNameFlags Flags + // The public key that is used to verify an assemblies + // inclusion into the namespace. If the public key associated + // with the namespace cannot verify the assembly the assembly + // will fail to load. + public byte[] GetPublicKey() { - get { return (AssemblyNameFlags)((uint)_flags & 0xFFFFF10F); } - set - { - _flags &= unchecked((AssemblyNameFlags)0x00000EF0); - _flags |= (value & unchecked((AssemblyNameFlags)0xFFFFF10F)); - } + return _publicKey; } - public string FullName + public void SetPublicKey(byte[] publicKey) { - get - { - if (this.Name == null) - return string.Empty; - // Do not call GetPublicKeyToken() here - that latches the result into AssemblyName which isn't a side effect we want. - byte[] pkt = _publicKeyToken ?? AssemblyNameHelpers.ComputePublicKeyToken(_publicKey); - return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, Flags, ContentType); - } + _publicKey = publicKey; + + if (publicKey == null) + _flags &= ~AssemblyNameFlags.PublicKey; + else + _flags |= AssemblyNameFlags.PublicKey; } - public string Name { get; set; } - public Version Version { get; set; } - public string CodeBase { get; set; } - public AssemblyHashAlgorithm HashAlgorithm { get; set; } - public AssemblyVersionCompatibility VersionCompatibility { get; set; } - public StrongNameKeyPair KeyPair { get; set; } + // The compressed version of the public key formed from a truncated hash. + // Will throw a SecurityException if _PublicKey is invalid + public byte[] GetPublicKeyToken() + { + if (_publicKeyToken == null) + _publicKeyToken = AssemblyNameHelpers.ComputePublicKeyToken(_publicKey); + return _publicKeyToken; + } - public string EscapedCodeBase + public void SetPublicKeyToken(byte[] publicKeyToken) { - get + _publicKeyToken = publicKeyToken; + } + + // Flags modifying the name. So far the only flag is PublicKey, which + // indicates that a full public key and not the compressed version is + // present. + // Processor Architecture flags are set only through ProcessorArchitecture + // property and can't be set or retrieved directly + // Content Type flags are set only through ContentType property and can't be + // set or retrieved directly + public AssemblyNameFlags Flags + { + get { return (AssemblyNameFlags)((uint)_flags & 0xFFFFF10F); } + set { - if (CodeBase == null) - return null; - else - return EscapeCodeBase(CodeBase); + _flags &= unchecked((AssemblyNameFlags)0x00000EF0); + _flags |= (value & unchecked((AssemblyNameFlags)0xFFFFF10F)); } } - public byte[] GetPublicKey() + public AssemblyHashAlgorithm HashAlgorithm { - return _publicKey; + get { return _hashAlgorithm; } + set { _hashAlgorithm = value; } } - public byte[] GetPublicKeyToken() + public AssemblyVersionCompatibility VersionCompatibility { - if (_publicKeyToken == null) - _publicKeyToken = AssemblyNameHelpers.ComputePublicKeyToken(_publicKey); - return _publicKeyToken; + get { return _versionCompatibility; } + set { _versionCompatibility = value; } } - public void SetPublicKey(byte[] publicKey) + public StrongNameKeyPair KeyPair { - _publicKey = publicKey; - - if (publicKey == null) - _flags &= ~AssemblyNameFlags.PublicKey; - else - _flags |= AssemblyNameFlags.PublicKey; + get { return _strongNameKeyPair; } + set { _strongNameKeyPair = value; } } - public void SetPublicKeyToken(byte[] publicKeyToken) + public string FullName { - _publicKeyToken = publicKeyToken; + get + { + if (this.Name == null) + return string.Empty; + // Do not call GetPublicKeyToken() here - that latches the result into AssemblyName which isn't a side effect we want. + byte[] pkt = _publicKeyToken ?? AssemblyNameHelpers.ComputePublicKeyToken(_publicKey); + return AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName, pkt, Flags, ContentType); + } } public override string ToString() @@ -183,11 +250,6 @@ namespace System.Reflection throw new PlatformNotSupportedException(); } - public static AssemblyName GetAssemblyName(string assemblyFile) - { - throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported_AssemblyName_GetAssemblyName); - } - /// <summary> /// Compares the simple names disregarding Version, Culture and PKT. While this clearly does not /// match the intent of this api, this api has been broken this way since its debut and we cannot @@ -210,10 +272,6 @@ namespace System.Reflection } internal static string EscapeCodeBase(string codebase) { throw new PlatformNotSupportedException(); } - - private AssemblyNameFlags _flags; - private byte[] _publicKey; - private byte[] _publicKeyToken; } } diff --git a/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs b/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs deleted file mode 100644 index 31e58caeb..000000000 --- a/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -using System.Diagnostics; - -namespace System.Reflection -{ - public class LocalVariableInfo - { - protected LocalVariableInfo() - { - } - - public virtual bool IsPinned - { - get - { - return false; - } - } - - public virtual int LocalIndex - { - get - { - return 0; - } - } - - public virtual Type LocalType - { - get - { - // Don't laugh - this is really how the desktop behaves if you don't override. - Debug.Fail("type must be set!"); - return null; - } - } - - public override string ToString() - { - // Don't laugh - this is really how the desktop behaves if you don't override, including the NullReference when - // it calls ToString() on LocalType's null return. - string toString = LocalType.ToString() + " (" + LocalIndex + ")"; - - if (IsPinned) - toString += " (pinned)"; - - return toString; - } - } -} - diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.NonPortable.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.NonPortable.cs index 4c309ca34..5374478e7 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.NonPortable.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.NonPortable.cs @@ -19,10 +19,9 @@ namespace System.Runtime.CompilerServices { //========================================================================================================= // Intrinsic to call the cctor given a pointer to the code (this method's body is ignored and replaced - // with a calli during compilation). The transform doesn't handle non-generic versions yet (i.e. - // functions that are void). + // with a calli during compilation). //========================================================================================================= - private static T Call<T>(System.IntPtr pfn) + private static void Call(System.IntPtr pfn) { throw NotImplemented.ByDesign; } diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index d8a418e15..a82e434b8 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -94,7 +94,7 @@ namespace System.Runtime.CompilerServices { NoisyLog("Calling cctor, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); - Call<int>(pfnCctor); + Call(pfnCctor); // Insert a memory barrier here to order any writes executed as part of static class // construction above with respect to the initialized flag update we're about to make |