diff options
author | Jan Kotas <jkotas@microsoft.com> | 2017-05-17 04:30:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-17 04:30:32 +0300 |
commit | d762c0277e666f85c3cbe60ca73a772ae31cd8b4 (patch) | |
tree | 432203105470f30a993e51ca43caf50998790f6c /src | |
parent | d567d53f42f91e5265639c6f1a14e82e2b63cabf (diff) | |
parent | 553e70def758f3baf28a2b05542f48c18c5b0463 (diff) |
Merge pull request #3631 from dotnet/master
Merge master to nmirror
Diffstat (limited to 'src')
46 files changed, 1949 insertions, 166 deletions
diff --git a/src/Common/src/TypeSystem/Common/CastingHelper.cs b/src/Common/src/TypeSystem/Common/CastingHelper.cs index ebfe39515..b7576923c 100644 --- a/src/Common/src/TypeSystem/Common/CastingHelper.cs +++ b/src/Common/src/TypeSystem/Common/CastingHelper.cs @@ -78,6 +78,14 @@ namespace Internal.TypeSystem // Casting array to something else (between SzArray and Array, for example)? if (thisType.Category != otherType.Category) { + // An SzArray is castable to MdArray rank 1. We follow the same casting rules as SzArray to SzArray. + if (thisType.Category == TypeFlags.SzArray + && otherType.Category == TypeFlags.Array + && ((ArrayType)otherType).Rank == 1) + { + return thisType.CanCastParamTo(((ArrayType)otherType).ParameterType, protect); + } + return false; } diff --git a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs index 3c5278b08..7b82dc361 100644 --- a/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs +++ b/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs @@ -11,6 +11,7 @@ namespace Internal.TypeSystem { private static readonly string[] s_wellKnownTypeNames = new string[] { "Void", + "Boolean", "Char", "SByte", @@ -68,16 +69,27 @@ namespace Internal.TypeSystem // Initialize all well known types - it will save us from checking the name for each loaded type for (int typeIndex = 0; typeIndex < _wellKnownTypes.Length; typeIndex++) { - MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex]); - type.SetWellKnownType((WellKnownType)(typeIndex + 1)); - _wellKnownTypes[typeIndex] = type; + // Require System.Object to be present as a minimal sanity check. + // The set of required well-known types is not strictly defined since different .NET profiles implement different subsets. + MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], typeIndex == (int)WellKnownType.Object); + if (type != null) + { + type.SetWellKnownType((WellKnownType)(typeIndex + 1)); + _wellKnownTypes[typeIndex] = type; + } } } - public override DefType GetWellKnownType(WellKnownType wellKnownType) + public override DefType GetWellKnownType(WellKnownType wellKnownType, bool throwIfNotFound = true) { Debug.Assert(_wellKnownTypes != null, "Forgot to call SetSystemModule?"); - return _wellKnownTypes[(int)wellKnownType - 1]; + + int typeIndex = (int)wellKnownType - 1; + DefType type = _wellKnownTypes[typeIndex]; + if (type == null && throwIfNotFound) + throw new TypeSystemException.TypeLoadException("System", s_wellKnownTypeNames[typeIndex], SystemModule); + + return type; } protected sealed internal override bool ComputeHasStaticConstructor(TypeDesc type) diff --git a/src/Common/src/TypeSystem/Common/TypeSystemContext.cs b/src/Common/src/TypeSystem/Common/TypeSystemContext.cs index ea5e69591..4f9dbb141 100644 --- a/src/Common/src/TypeSystem/Common/TypeSystemContext.cs +++ b/src/Common/src/TypeSystem/Common/TypeSystemContext.cs @@ -57,7 +57,7 @@ namespace Internal.TypeSystem SystemModule = systemModule; } - public abstract DefType GetWellKnownType(WellKnownType wellKnownType); + public abstract DefType GetWellKnownType(WellKnownType wellKnownType, bool throwIfNotFound = true); public virtual ModuleDesc ResolveAssembly(AssemblyName name, bool throwIfNotFound = true) { diff --git a/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs b/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs index cf1d3db16..255eb7cfd 100644 --- a/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs +++ b/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs @@ -12,7 +12,7 @@ namespace Internal.TypeSystem { public static bool IsWellKnownType(this TypeDesc type, WellKnownType wellKnownType) { - return type == type.Context.GetWellKnownType(wellKnownType); + return type == type.Context.GetWellKnownType(wellKnownType, false); } public static InstantiatedType MakeInstantiatedType(this MetadataType typeDef, Instantiation instantiation) diff --git a/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs index c9b4c6ecb..4e8e7a171 100644 --- a/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs +++ b/src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs @@ -142,6 +142,44 @@ namespace Internal.IL.Stubs } } + // Methods on Rank 1 MdArray need to be able to handle `this` that is an SzArray + // because SzArray is castable to Rank 1 MdArray (but not the other way around). + + ILCodeLabel rangeCheckDoneLabel = null; + if (_rank == 1) + { + TypeDesc objectType = context.GetWellKnownType(WellKnownType.Object); + TypeDesc eetypePtrType = context.SystemModule.GetKnownType("System", "EETypePtr"); + ILLocalVariable thisEEType = _emitter.NewLocal(eetypePtrType); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.call, _emitter.NewToken(objectType.GetKnownMethod("get_EETypePtr", null))); + codeStream.EmitStLoc(thisEEType); + codeStream.EmitLdLoca(thisEEType); + codeStream.Emit(ILOpcode.call, + _emitter.NewToken(eetypePtrType.GetKnownMethod("get_IsSzArray", null))); + + ILCodeLabel notSzArrayLabel = _emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.brfalse, notSzArrayLabel); + + // We have an SzArray - do the bounds check differently + EmitLoadInteriorAddress(codeStream, pointerSize); + codeStream.Emit(ILOpcode.dup); + codeStream.Emit(ILOpcode.ldind_i4); + codeStream.EmitLdArg(argStartOffset); + codeStream.EmitStLoc(totalLocalNum); + codeStream.EmitLdLoc(totalLocalNum); + codeStream.Emit(ILOpcode.ble_un, rangeExceptionLabel); + + codeStream.EmitLdc(pointerSize); + codeStream.Emit(ILOpcode.add); + + rangeCheckDoneLabel = _emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.br, rangeCheckDoneLabel); + + codeStream.EmitLabel(notSzArrayLabel); + } + for (int i = 0; i < _rank; i++) { // The first two fields are EEType pointer and total length. Lengths for each dimension follows. @@ -174,6 +212,9 @@ namespace Internal.IL.Stubs int firstElementOffset = (2 * pointerSize + 2 * _rank * sizeof(int)); EmitLoadInteriorAddress(codeStream, firstElementOffset); + if (rangeCheckDoneLabel != null) + codeStream.EmitLabel(rangeCheckDoneLabel); + codeStream.EmitLdLoc(totalLocalNum); int elementSize = _elementType.GetElementSize().AsInt; diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs index 5bdbdc8c2..698d7a46f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs @@ -14,6 +14,7 @@ namespace ILCompiler { protected readonly CompilerTypeSystemContext _context; protected readonly CompilationModuleGroup _compilationGroup; + protected readonly NameMangler _nameMangler; // These need to provide reasonable defaults so that the user can optionally skip // calling the Use/Configure methods and still get something reasonable back. @@ -24,10 +25,11 @@ namespace ILCompiler protected bool _generateDebugInfo = false; private string _metadataLogFile = null; - public CompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup) + public CompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler nameMangler) { _context = context; _compilationGroup = compilationGroup; + _nameMangler = nameMangler; } public CompilationBuilder UseLogger(Logger logger) @@ -70,21 +72,8 @@ namespace ILCompiler protected DependencyAnalyzerBase<NodeFactory> CreateDependencyGraph(NodeFactory factory) { - // Choose which dependency graph implementation to use based on the amount of logging requested. - switch (_dependencyTrackingLevel) - { - case DependencyTrackingLevel.None: - return new DependencyAnalyzer<NoLogStrategy<NodeFactory>, NodeFactory>(factory, null); - - case DependencyTrackingLevel.First: - return new DependencyAnalyzer<FirstMarkLogStrategy<NodeFactory>, NodeFactory>(factory, null); - - case DependencyTrackingLevel.All: - return new DependencyAnalyzer<FullGraphLogStrategy<NodeFactory>, NodeFactory>(factory, null); - - default: - throw new InvalidOperationException(); - } + // TODO: add graph sorter when we go multi-threaded + return _dependencyTrackingLevel.CreateDependencyGraph(factory); } protected MetadataManager CreateMetadataManager() @@ -92,28 +81,12 @@ namespace ILCompiler return new CompilerGeneratedMetadataManager(_compilationGroup, _context, _metadataLogFile); } - public abstract ICompilation ToCompilation(); - } - - /// <summary> - /// Represents the level of dependency tracking within the dependency analysis system. - /// </summary> - public enum DependencyTrackingLevel - { - /// <summary> - /// Tracking disabled. This is the most performant and memory efficient option. - /// </summary> - None, - - /// <summary> - /// The graph keeps track of the first dependency. - /// </summary> - First, + public ILScannerBuilder GetILScannerBuilder(CompilationModuleGroup compilationGroup = null) + { + return new ILScannerBuilder(_context, compilationGroup ?? _compilationGroup, _nameMangler); + } - /// <summary> - /// The graph keeps track of all dependencies. - /// </summary> - All + public abstract ICompilation ToCompilation(); } /// <summary> diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs index 82435d069..3e1a5c368 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs @@ -59,6 +59,21 @@ namespace ILCompiler return _compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType); } + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + return MetadataCategory.RuntimeMapping; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + return MetadataCategory.RuntimeMapping; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + return MetadataCategory.RuntimeMapping; + } + protected override void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List<MetadataMapping<MetadataType>> typeMappings, diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs index 5cc986cd1..3cd077ae3 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs @@ -72,7 +72,7 @@ namespace ILCompiler.DependencyAnalysis public static void AddDependenciesDueToMethodCodePresence(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - AddDependenciesDueToReflectability(ref dependencies, factory, method); + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, method); if (method.HasInstantiation) { diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 4354e46a7..1b585df3d 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -157,6 +157,9 @@ namespace ILCompiler.DependencyAnalysis dependencyList.Add(new DependencyListEntry(factory.TypeNonGCStaticsSymbol((MetadataType)_type), "Class constructor")); } + // Ask the metadata manager if we have any dependencies due to reflectability. + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + return dependencyList; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs new file mode 100644 index 000000000..0a684b8f5 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs @@ -0,0 +1,80 @@ +// 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; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// <summary> + /// Node factory to be used during IL scanning. + /// </summary> + public sealed class ILScanNodeFactory : NodeFactory + { + public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) + : base(context, compilationModuleGroup, metadataManager, nameMangler) + { + } + + protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) + { + if (method.IsInternalCall) + { + // TODO: come up with a scheme where this can be shared between codegen backends and the scanner + if (TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealSpecialUnboxingThunkTargetMethod(method)); + } + else if (method.IsArrayAddressMethod()) + { + return new ScannedMethodNode(((ArrayType)method.OwningType).GetArrayMethod(ArrayMethodKind.AddressWithHiddenArg)); + } + else if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) + { + return new RuntimeImportMethodNode(method); + } + + // On CLR this would throw a SecurityException with "ECall methods must be packaged into a system module." + // This is a corner case that nobody is likely to care about. + throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + } + + if (CompilationModuleGroup.ContainsMethod(method)) + { + return new ScannedMethodNode(method); + } + else + { + return new ExternMethodSymbolNode(this, method); + } + } + + protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) + { + Debug.Assert(!method.Signature.IsStatic); + + if (method.IsCanonicalMethod(CanonicalFormKind.Specific) && !method.HasInstantiation) + { + // Unboxing stubs to canonical instance methods need a special unboxing stub that unboxes + // 'this' and also provides an instantiation argument (we do a calling convention conversion). + // We don't do this for generic instance methods though because they don't use the EEType + // for the generic context anyway. + return new ScannedMethodNode(TypeSystemContext.GetSpecialUnboxingThunk(method, CompilationModuleGroup.GeneratedAssembly)); + } + else + { + // Otherwise we just unbox 'this' and don't touch anything else. + return new UnboxingStubNode(method); + } + } + + protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) + { + return new ReadyToRunHelperNode(this, helperCall.HelperId, helperCall.Target); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs index 04f79eea7..ff4bd731d 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs @@ -30,7 +30,7 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) { DependencyList dependencies = null; - CodeBasedDependencyAlgorithm.AddDependenciesDueToReflectability(ref dependencies, factory, _method); + factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _method); return dependencies; } protected override string GetName(NodeFactory factory) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs index 7182c314f..9e45d1a3e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -12,9 +12,8 @@ namespace ILCompiler.DependencyAnalysis { public sealed class RyuJitNodeFactory : NodeFactory { - public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager) - : base(context, compilationModuleGroup, metadataManager, - new CoreRTNameMangler(context.Target.IsWindows ? (NodeMangler)new WindowsNodeMangler() : (NodeMangler)new UnixNodeMangler(), false)) + public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) + : base(context, compilationModuleGroup, metadataManager, nameMangler) { } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs new file mode 100644 index 000000000..d5fb77f08 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs @@ -0,0 +1,66 @@ +// 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; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// <summary> + /// Represents a method that should be scanned by an IL scanner and its dependencies + /// analyzed. + /// </summary> + public class ScannedMethodNode : DependencyNodeCore<NodeFactory>, IMethodNode + { + private readonly MethodDesc _method; + private DependencyList _dependencies; + + public ScannedMethodNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); + _method = method; + } + + public MethodDesc Method => _method; + + public int Offset => 0; + + public bool RepresentsIndirectionCell => false; + + public override bool StaticDependenciesAreComputed => _dependencies != null; + + public void InitializeDependencies(NodeFactory factory, IEnumerable<DependencyListEntry> dependencies) + { + _dependencies = new DependencyList(dependencies); + CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref _dependencies, factory, _method); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) + { + Debug.Assert(_dependencies != null); + return _dependencies; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs new file mode 100644 index 000000000..6a1079e50 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs @@ -0,0 +1,55 @@ +// 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; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler +{ + /// <summary> + /// Represents the level of dependency tracking within the dependency analysis system. + /// </summary> + public enum DependencyTrackingLevel + { + /// <summary> + /// Tracking disabled. This is the most performant and memory efficient option. + /// </summary> + None, + + /// <summary> + /// The graph keeps track of the first dependency. + /// </summary> + First, + + /// <summary> + /// The graph keeps track of all dependencies. + /// </summary> + All + } + + internal static class DependencyTrackingLevelExtensions + { + public static DependencyAnalyzerBase<NodeFactory> CreateDependencyGraph(this DependencyTrackingLevel trackingLevel, NodeFactory factory, IComparer<DependencyNodeCore<NodeFactory>> comparer = null) + { + // Choose which dependency graph implementation to use based on the amount of logging requested. + switch (trackingLevel) + { + case DependencyTrackingLevel.None: + return new DependencyAnalyzer<NoLogStrategy<NodeFactory>, NodeFactory>(factory, comparer); + + case DependencyTrackingLevel.First: + return new DependencyAnalyzer<FirstMarkLogStrategy<NodeFactory>, NodeFactory>(factory, comparer); + + case DependencyTrackingLevel.All: + return new DependencyAnalyzer<FullGraphLogStrategy<NodeFactory>, NodeFactory>(factory, comparer); + + default: + throw new InvalidOperationException(); + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs index bb6d6fae1..d80103870 100644 --- a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs @@ -28,6 +28,21 @@ namespace ILCompiler return true; } + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + return MetadataCategory.None; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + return MetadataCategory.None; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + return MetadataCategory.None; + } + protected override void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List<MetadataMapping<MetadataType>> typeMappings, out List<MetadataMapping<MethodDesc>> methodMappings, out List<MetadataMapping<FieldDesc>> fieldMappings) { metadataBlob = Array.Empty<byte>(); diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs new file mode 100644 index 000000000..d93aba60c --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs @@ -0,0 +1,110 @@ +// 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; +using System.Collections.Generic; +using System.Collections.Immutable; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; +using Internal.IL.Stubs; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// <summary> + /// IL scan analyzer of programs - this class analyzes what methods, types and other runtime artifact + /// will need to be generated during a compilation. The result of analysis is a conservative superset of + /// what methods will be compiled by the actual codegen backend. + /// </summary> + internal sealed class ILScanner : Compilation, IILScanner + { + internal ILScanner( + DependencyAnalyzerBase<NodeFactory> dependencyGraph, + ILScanNodeFactory nodeFactory, + IEnumerable<ICompilationRootProvider> roots, + Logger logger) + : base(dependencyGraph, nodeFactory, roots, logger) + { + } + + protected override bool GenerateDebugInfo => false; + + protected override void CompileInternal(string outputFile, ObjectDumper dumper) + { + // TODO: We should have a base class for compilation that doesn't implement ICompilation so that + // we don't need this. + throw new NotSupportedException(); + } + + protected override void ComputeDependencyNodeDependencies(List<DependencyNodeCore<NodeFactory>> obj) + { + foreach (DependencyNodeCore<NodeFactory> dependency in obj) + { + var methodCodeNodeNeedingCode = dependency as ScannedMethodNode; + if (methodCodeNodeNeedingCode == null) + { + // To compute dependencies of the shadow method that tracks dictionary + // dependencies we need to ensure there is code for the canonical method body. + var dependencyMethod = (ShadowConcreteMethodNode)dependency; + methodCodeNodeNeedingCode = (ScannedMethodNode)dependencyMethod.CanonicalMethodNode; + } + + // We might have already compiled this method. + if (methodCodeNodeNeedingCode.StaticDependenciesAreComputed) + continue; + + MethodDesc method = methodCodeNodeNeedingCode.Method; + + try + { + var importer = new ILImporter(this, method); + methodCodeNodeNeedingCode.InitializeDependencies(_nodeFactory, importer.Import()); + } + catch (TypeSystemException ex) + { + // Try to compile the method again, but with a throwing method body this time. + MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, ex); + var importer = new ILImporter(this, method, throwingIL); + methodCodeNodeNeedingCode.InitializeDependencies(_nodeFactory, importer.Import()); + } + } + } + + public ILScanResults Scan() + { + return new ILScanResults(_dependencyGraph.MarkedNodeList); + } + } + + public interface IILScanner + { + ILScanResults Scan(); + } + + public class ILScanResults + { + private readonly ImmutableArray<DependencyNodeCore<NodeFactory>> _markedNodes; + + internal ILScanResults(ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes) + { + _markedNodes = markedNodes; + } + + public IEnumerable<MethodDesc> CompiledMethods + { + get + { + foreach (var node in _markedNodes) + { + var methodNode = node as ScannedMethodNode; + if (methodNode != null) + yield return methodNode.Method; + } + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs new file mode 100644 index 000000000..aea5978d2 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs @@ -0,0 +1,55 @@ +// 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; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler +{ + public sealed class ILScannerBuilder + { + private readonly CompilerTypeSystemContext _context; + private readonly CompilationModuleGroup _compilationGroup; + private readonly NameMangler _nameMangler; + + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + private Logger _logger = Logger.Null; + private DependencyTrackingLevel _dependencyTrackingLevel = DependencyTrackingLevel.None; + private IEnumerable<ICompilationRootProvider> _compilationRoots = Array.Empty<ICompilationRootProvider>(); + + internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler) + { + _context = context; + _compilationGroup = compilationGroup; + _nameMangler = mangler; + } + + public ILScannerBuilder UseDependencyTracking(DependencyTrackingLevel trackingLevel) + { + _dependencyTrackingLevel = trackingLevel; + return this; + } + + public ILScannerBuilder UseCompilationRoots(IEnumerable<ICompilationRootProvider> compilationRoots) + { + _compilationRoots = compilationRoots; + return this; + } + + public IILScanner ToILScanner() + { + // TODO: we will want different metadata managers depending on whether we're doing reflection analysis + var metadataManager = new CompilerGeneratedMetadataManager(_compilationGroup, _context, null); + + var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, metadataManager, _nameMangler); + DependencyAnalyzerBase<NodeFactory> graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); + + return new ILScanner(graph, nodeFactory, _compilationRoots, _logger); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs index 882f45efa..3dde1435a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs @@ -17,6 +17,7 @@ using ILCompiler.DependencyAnalysisFramework; using Debug = System.Diagnostics.Debug; using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType; using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList; namespace ILCompiler { @@ -197,7 +198,13 @@ namespace ILCompiler /// <summary> /// Is a method that is reflectable a method which should be placed into the invoke map as invokable? /// </summary> - public bool IsReflectionInvokable (MethodDesc method) + public virtual bool IsReflectionInvokable(MethodDesc method) + { + return IsMethodSignatureSupportedInReflectionInvoke(method) + && IsMethodSupportedInReflectionInvoke(method); + } + + protected bool IsMethodSignatureSupportedInReflectionInvoke(MethodDesc method) { var signature = method.Signature; @@ -244,6 +251,15 @@ namespace ILCompiler return false; } + return true; + } + + protected bool IsMethodSupportedInReflectionInvoke(MethodDesc method) + { + // TODO: also filter out: .cctor, Finalize, string constructors, + // RuntimeHelpers.InitializeArray, methods on Nullable, + // IntPtr/UIntPtr constructors. Maybe more. + // ---------------------------------------------------------------- // Delegate construction is only allowed through specific IL sequences // ---------------------------------------------------------------- @@ -267,6 +283,63 @@ namespace ILCompiler } /// <summary> + /// This method is an extension point that can provide additional metadata-based dependencies to compiled method bodies. + /// </summary> + public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + MetadataCategory category = GetMetadataCategory(method); + + if ((category & MetadataCategory.Description) != 0) + { + GetMetadataDependenciesDueToReflectability(ref dependencies, factory, method); + } + + if ((category & MetadataCategory.RuntimeMapping) != 0) + { + if (IsReflectionInvokable(method)) + { + // We're going to generate a mapping table entry for this. Collect dependencies. + CodeBasedDependencyAlgorithm.AddDependenciesDueToReflectability(ref dependencies, factory, method); + } + } + } + + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata + // (E.g. dependencies caused by the method having custom attributes applied to it: making sure we compile the attribute constructor + // and property setters) + } + + /// <summary> + /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes. + /// </summary> + public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + MetadataCategory category = GetMetadataCategory(type); + + if ((category & MetadataCategory.Description) != 0) + { + GetMetadataDependenciesDueToReflectability(ref dependencies, factory, type); + } + + if ((category & MetadataCategory.RuntimeMapping) != 0) + { + // We're going to generate a mapping table entry for this. Collect dependencies. + + // Nothing for now - the mapping table only refers to the EEType and we already generated one because + // we got the callback. + } + } + + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata + // (E.g. dependencies caused by the type having custom attributes applied to it: making sure we compile the attribute constructor + // and property setters) + } + + /// <summary> /// Given that a method is invokable, does there exist a reflection invoke stub? /// </summary> public abstract bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method); @@ -468,6 +541,10 @@ namespace ILCompiler } public abstract bool IsReflectionBlocked(MetadataType type); + + protected abstract MetadataCategory GetMetadataCategory(MethodDesc method); + protected abstract MetadataCategory GetMetadataCategory(TypeDesc type); + protected abstract MetadataCategory GetMetadataCategory(FieldDesc field); } public struct MetadataMapping<TEntity> @@ -481,4 +558,11 @@ namespace ILCompiler MetadataHandle = metadataHandle; } } + + public enum MetadataCategory + { + None = 0x00, + Description = 0x01, + RuntimeMapping = 0x02, + } } diff --git a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs index 6d59236ca..942e85ba5 100644 --- a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs @@ -202,6 +202,24 @@ namespace ILCompiler return _compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType); } + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + // Backwards compatible behavior. We might want to tweak this. + return MetadataCategory.RuntimeMapping; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + // Backwards compatible behavior. We might want to tweak this. + return MetadataCategory.RuntimeMapping; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + // Backwards compatible behavior. We might want to tweak this. + return MetadataCategory.RuntimeMapping; + } + protected override void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List<MetadataMapping<MetadataType>> typeMappings, out List<MetadataMapping<MethodDesc>> methodMappings, out List<MetadataMapping<FieldDesc>> fieldMappings) { MetadataLoadedInfo loadedMetadata = _loadedMetadata.Value; diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs index 767e29092..58850e22c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs @@ -18,7 +18,8 @@ namespace ILCompiler private KeyValuePair<string, string>[] _ryujitOptions = Array.Empty<KeyValuePair<string, string>>(); public RyuJitCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) - : base(context, group) + : base(context, group, + new CoreRTNameMangler(context.Target.IsWindows ? (NodeMangler)new WindowsNodeMangler() : (NodeMangler)new UnixNodeMangler(), false)) { } @@ -75,7 +76,7 @@ namespace ILCompiler MetadataManager metadataManager = CreateMetadataManager(); - var factory = new RyuJitNodeFactory(_context, _compilationGroup, metadataManager); + var factory = new RyuJitNodeFactory(_context, _compilationGroup, metadataManager, _nameMangler); var jitConfig = new JitConfigProvider(jitFlagBuilder.ToArray(), _ryujitOptions); DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory); diff --git a/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs b/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs new file mode 100644 index 000000000..4011819b1 --- /dev/null +++ b/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs @@ -0,0 +1,929 @@ +// 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; + +using Internal.TypeSystem; + +using ILCompiler; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList; + +namespace Internal.IL +{ + // Implements an IL scanner that scans method bodies to be compiled by the code generation + // backend before the actual compilation happens to gain insights into the code. + partial class ILImporter + { + private readonly MethodIL _methodIL; + private readonly ILScanner _compilation; + private readonly ILScanNodeFactory _factory; + + // True if we're scanning a throwing method body because scanning the real body failed. + private readonly bool _isFallbackBodyCompilation; + + private readonly MethodDesc _canonMethod; + + private readonly DependencyList _dependencies = new DependencyList(); + + private readonly byte[] _ilBytes; + + private class BasicBlock + { + // Common fields + public BasicBlock Next; + + public int StartOffset; + public int EndOffset; + + public bool TryStart; + public bool FilterStart; + public bool HandlerStart; + } + + private TypeDesc _constrained; + + private int _currentInstructionOffset; + private int _previousInstructionOffset; + + private class ExceptionRegion + { + public ILExceptionRegion ILRegion; + } + private ExceptionRegion[] _exceptionRegions; + + public ILImporter(ILScanner compilation, MethodDesc method, MethodIL methodIL = null) + { + if (methodIL == null) + { + methodIL = compilation.GetMethodIL(method); + } + else + { + _isFallbackBodyCompilation = true; + } + + // This is e.g. an "extern" method in C# without a DllImport or InternalCall. + if (methodIL == null) + { + throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + } + + _compilation = compilation; + _factory = (ILScanNodeFactory)compilation.NodeFactory; + + _ilBytes = methodIL.GetILBytes(); + + // Get the runtime determined method IL so that this works right in shared code + // and tokens in shared code resolve to runtime determined types. + MethodIL uninstantiatiedMethodIL = methodIL.GetMethodILDefinition(); + if (methodIL != uninstantiatiedMethodIL) + { + MethodDesc sharedMethod = method.GetSharedRuntimeFormMethodTarget(); + _methodIL = new InstantiatedMethodIL(sharedMethod, uninstantiatiedMethodIL); + } + else + { + _methodIL = methodIL; + } + + _canonMethod = method; + + var ilExceptionRegions = methodIL.GetExceptionRegions(); + _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; + for (int i = 0; i < ilExceptionRegions.Length; i++) + { + _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; + } + } + + public DependencyList Import() + { + FindBasicBlocks(); + ImportBasicBlocks(); + + return _dependencies; + } + + private ISymbolNode GetGenericLookupHelper(ReadyToRunHelperId helperId, object helperArgument) + { + if (_canonMethod.RequiresInstMethodDescArg()) + { + return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArgument, _canonMethod); + } + else + { + Debug.Assert(_canonMethod.RequiresInstArg() || _canonMethod.AcquiresInstMethodTableFromThis()); + return _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(helperId, helperArgument, _canonMethod.OwningType); + } + } + + private ISymbolNode GetHelperEntrypoint(ReadyToRunHelper helper) + { + string mangledName; + MethodDesc methodDesc; + JitHelper.GetEntryPoint(_compilation.TypeSystemContext, helper, out mangledName, out methodDesc); + Debug.Assert(mangledName != null || methodDesc != null); + + ISymbolNode entryPoint; + if (mangledName != null) + entryPoint = _compilation.NodeFactory.ExternSymbol(mangledName); + else + entryPoint = _compilation.NodeFactory.MethodEntrypoint(methodDesc); + + return entryPoint; + } + + private void MarkInstructionBoundary() { } + private void EndImportingBasicBlock(BasicBlock basicBlock) { } + private void EndImportingInstruction() { } + + private void StartImportingBasicBlock(BasicBlock basicBlock) + { + // Import all associated EH regions + foreach (ExceptionRegion ehRegion in _exceptionRegions) + { + ILExceptionRegion region = ehRegion.ILRegion; + if (region.TryOffset == basicBlock.StartOffset) + { + MarkBasicBlock(_basicBlocks[region.HandlerOffset]); + if (region.Kind == ILExceptionRegionKind.Filter) + MarkBasicBlock(_basicBlocks[region.FilterOffset]); + } + } + + _currentInstructionOffset = -1; + _previousInstructionOffset = -1; + } + + private void StartImportingInstruction() + { + _previousInstructionOffset = _currentInstructionOffset; + _currentInstructionOffset = _currentOffset; + } + + private void ImportJmp(int token) + { + // TODO + } + + private void ImportCasting(ILOpcode opcode, int token) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + // Nullable needs to be unwrapped + if (type.IsNullable) + type = type.Instantiation[0]; + + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "IsInst/CastClass"); + } + else + { + ReadyToRunHelperId helperId; + if (opcode == ILOpcode.isinst) + { + helperId = ReadyToRunHelperId.IsInstanceOf; + } + else + { + Debug.Assert(opcode == ILOpcode.castclass); + helperId = ReadyToRunHelperId.CastClass; + } + + _dependencies.Add(_factory.ReadyToRunHelper(helperId, type), "IsInst/CastClass"); + } + } + + private void ImportCall(ILOpcode opcode, int token) + { + // Strip runtime determined characteristics off of the method (because that's how RyuJIT operates) + var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); + MethodDesc method = runtimeDeterminedMethod; + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + method = runtimeDeterminedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (method.IsRawPInvoke()) + { + // Raw P/invokes don't have any dependencies. + return; + } + + string reason = null; + switch (opcode) + { + case ILOpcode.newobj: + reason = "newobj"; break; + case ILOpcode.call: + reason = "call"; break; + case ILOpcode.callvirt: + reason = "callvirt"; break; + case ILOpcode.ldftn: + reason = "ldftn"; break; + case ILOpcode.ldvirtftn: + reason = "ldvirtftn"; break; + default: + Debug.Assert(false); break; + } + + // If we're scanning the fallback body because scanning the real body failed, don't trigger cctor. + // Accessing the cctor could have been a reason why we failed. + if (!_isFallbackBodyCompilation) + { + // Do we need to run the cctor? + TypeDesc owningType = runtimeDeterminedMethod.OwningType; + if (_factory.TypeSystemContext.HasLazyStaticConstructor(owningType)) + { + // For beforefieldinit, we can wait for field access. + if (!((MetadataType)owningType).IsBeforeFieldInit) + { + // Accessing the static base will trigger the cctor. + if (owningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); + } + } + } + } + + if (opcode == ILOpcode.newobj) + { + TypeDesc owningType = runtimeDeterminedMethod.OwningType; + if (owningType.IsString) + { + // String .ctor handled specially below + } + else + { + // Nullable needs to be unwrapped. + if (owningType.IsNullable) + owningType = owningType.Instantiation[0]; + + if (owningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); + } + + if (owningType.IsMdArray) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); + return; + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); + } + } + + if (owningType.IsDelegate) + { + // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn + if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) + { + // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. + + ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); + if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) + { + int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); + var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); + TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); + + if (info.NeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); + } + + return; + } + } + } + } + + if (method.OwningType.IsDelegate && method.Name == "Invoke") + { + // TODO: might not want to do this if scanning for reflection. + // This is expanded as an intrinsic, not a function call. + return; + } + + if (method.IsIntrinsic) + { + if (IsRuntimeHelpersInitializeArray(method)) + { + if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) + return; + } + + if (IsRuntimeTypeHandleGetValueInternal(method)) + { + if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) + return; + } + } + + TypeDesc exactType = method.OwningType; + + bool resolvedConstraint = false; + bool forceUseRuntimeLookup = false; + + MethodDesc methodAfterConstraintResolution = method; + if (_constrained != null) + { + // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this + // will not necessarily resolve the call exactly, since we might be compiling + // shared generic code - it may just resolve it to a candidate suitable for + // JIT compilation, and require a runtime lookup for the actual code pointer + // to call. + + MethodDesc directMethod = _constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); + if (directMethod == null && _constrained.IsEnum) + { + // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference + // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. + directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(_constrained, method); + } + + if (directMethod != null) + { + // Either + // 1. no constraint resolution at compile time (!directMethod) + // OR 2. no code sharing lookup in call + // OR 3. we have have resolved to an instantiating stub + + methodAfterConstraintResolution = directMethod; + + Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); + resolvedConstraint = true; + + exactType = _constrained; + } + else if (_constrained.IsValueType) + { + // We'll need to box `this`. + AddBoxingDependencies(_constrained, reason); + } + + _constrained = null; + } + + MethodDesc targetMethod = methodAfterConstraintResolution; + + bool exactContextNeedsRuntimeLookup; + if (targetMethod.HasInstantiation) + { + exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; + } + else + { + exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); + } + + // + // Determine whether to perform direct call + // + + bool directCall = false; + + if (targetMethod.Signature.IsStatic) + { + // Static methods are always direct calls + directCall = true; + } + else if (targetMethod.OwningType.IsInterface) + { + // Force all interface calls to be interpreted as if they are virtual. + directCall = false; + } + else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) + { + directCall = true; + } + else + { + if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) + { + directCall = true; + } + } + + bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; + + if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) + { + // Needs a single address to call this method but the method needs a hidden argument. + // We need a fat function pointer for this that captures both things. + + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); + } + else + { + _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); + } + } + else if (directCall) + { + bool referencingArrayAddressMethod = false; + + if (targetMethod.IsIntrinsic) + { + // If this is an intrinsic method with a callsite-specific expansion, this will replace + // the method with a method the intrinsic expands into. If it's not the special intrinsic, + // method stays unchanged. + targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); + + // Array address method requires special dependency tracking. + referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); + } + + MethodDesc concreteMethod = targetMethod; + targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) + { + _dependencies.Add(_factory.StringAllocator(targetMethod), reason); + } + else if (exactContextNeedsRuntimeLookup) + { + if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) + { + _dependencies.Add(_factory.RuntimeDeterminedMethod(runtimeDeterminedMethod), reason); + } + else + { + Debug.Assert(!forceUseRuntimeLookup); + _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); + } + } + else + { + ISymbolNode instParam = null; + + if (targetMethod.RequiresInstMethodDescArg()) + { + instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); + } + else if (targetMethod.RequiresInstMethodTableArg() || referencingArrayAddressMethod) + { + // Ask for a constructed type symbol because we need the vtable to get to the dictionary + instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); + } + + if (instParam != null) + { + _dependencies.Add(instParam, reason); + + if (!referencingArrayAddressMethod) + { + _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); + } + else + { + // We don't want array Address method to be modeled in the generic dependency analysis. + // The method doesn't actually have runtime determined dependencies (won't do + // any generic lookups). + _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); + } + } + else if (targetMethod.AcquiresInstMethodTableFromThis()) + { + _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); + } + else + { + _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); + } + } + } + else if (method.HasInstantiation) + { + // Generic virtual method call + + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); + } + else + { + _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); + } + else + { + ReadyToRunHelperId helper; + if (opcode == ILOpcode.ldvirtftn) + { + helper = ReadyToRunHelperId.ResolveVirtualFunction; + } + else + { + Debug.Assert(opcode == ILOpcode.callvirt); + helper = ReadyToRunHelperId.VirtualCall; + } + + if (exactContextNeedsRuntimeLookup && targetMethod.OwningType.IsInterface) + { + _dependencies.Add(GetGenericLookupHelper(helper, runtimeDeterminedMethod), reason); + } + else + { + // Get the slot defining method to make sure our virtual method use tracking gets this right. + // For normal C# code the targetMethod will always be newslot. + MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? + targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); + + _dependencies.Add(_factory.ReadyToRunHelper(helper, slotDefiningMethod), reason); + } + } + } + + private void ImportLdFtn(int token, ILOpcode opCode) + { + // Is this a verifiable delegate creation? If so, we will handle it when we reach the newobj + if (_ilBytes[_currentOffset] == (byte)ILOpcode.newobj) + { + int delegateToken = ReadILTokenAt(_currentOffset + 1); + var delegateType = ((MethodDesc)_methodIL.GetObject(delegateToken)).OwningType; + if (delegateType.IsDelegate) + return; + } + + ImportCall(opCode, token); + } + + private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) + { + ImportFallthrough(target); + + if (fallthrough != null) + ImportFallthrough(fallthrough); + } + + private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) + { + for (int i = 0; i < jmpDelta.Length; i++) + { + BasicBlock target = _basicBlocks[jmpBase + jmpDelta[i]]; + ImportFallthrough(target); + } + + if (fallthrough != null) + ImportFallthrough(fallthrough); + } + + private void ImportUnbox(int token, ILOpcode opCode) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + if (!type.IsValueType) + return; + + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "Unbox"); + } + else + { + _dependencies.Add(_factory.NecessaryTypeSymbol(type), "Unbox"); + } + + ReadyToRunHelper helper; + if (opCode == ILOpcode.unbox) + { + helper = ReadyToRunHelper.Unbox; + } + else + { + Debug.Assert(opCode == ILOpcode.unbox_any); + helper = ReadyToRunHelper.Unbox_Nullable; + } + + _dependencies.Add(GetHelperEntrypoint(helper), "Unbox"); + } + + private void ImportRefAnyVal(int token) + { + // TODO + } + + private void ImportMkRefAny(int token) + { + // TODO + } + + private void ImportLdToken(int token) + { + object obj = _methodIL.GetObject(token); + + if (obj is TypeDesc) + { + var type = (TypeDesc)obj; + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "ldtoken"); + } + else + { + if (ConstructedEETypeNode.CreationAllowed(type)) + _dependencies.Add(_factory.ConstructedTypeSymbol(type), "ldtoken"); + else + _dependencies.Add(_factory.NecessaryTypeSymbol(type), "ldtoken"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeTypeHandle), "ldtoken"); + } + else if (obj is MethodDesc) + { + var method = (MethodDesc)obj; + if (method.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, method), "ldtoken"); + } + else + { + _dependencies.Add(_factory.RuntimeMethodHandle(method), "ldtoken"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeMethodHandle), "ldtoken"); + } + else + { + Debug.Assert(obj is FieldDesc); + + // First check if this is a ldtoken Field / InitializeArray sequence. + BasicBlock nextBasicBlock = _basicBlocks[_currentOffset]; + if (nextBasicBlock == null) + { + if ((ILOpcode)_ilBytes[_currentOffset] == ILOpcode.call) + { + int methodToken = ReadILTokenAt(_currentOffset + 1); + var method = (MethodDesc)_methodIL.GetObject(methodToken); + if (IsRuntimeHelpersInitializeArray(method)) + { + // Codegen expands this and doesn't do the normal ldtoken. + return; + } + } + } + + var field = (FieldDesc)obj; + if (field.OwningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.FieldHandle, field), "ldtoken"); + } + else + { + _dependencies.Add(_factory.RuntimeFieldHandle(field), "ldtoken"); + } + + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeFieldHandle), "ldtoken"); + } + } + + private void ImportRefAnyType() + { + // TODO + } + + private void ImportArgList() + { + } + + private void ImportConstrainedPrefix(int token) + { + // We convert to canon, because that's what ryujit would see. + _constrained = (TypeDesc)_methodIL.GetObject(token); + if (_constrained.IsRuntimeDeterminedSubtype) + _constrained = _constrained.ConvertToCanonForm(CanonicalFormKind.Specific); + } + + private void ImportFieldAccess(int token, bool isStatic, string reason) + { + if (isStatic) + { + var field = (FieldDesc)_methodIL.GetObject(token); + + ReadyToRunHelperId helperId; + if (field.IsThreadStatic) + { + helperId = ReadyToRunHelperId.GetThreadStaticBase; + } + else if (field.HasGCStaticBase) + { + helperId = ReadyToRunHelperId.GetGCStaticBase; + } + else + { + Debug.Assert(field.IsStatic); + helperId = ReadyToRunHelperId.GetNonGCStaticBase; + } + + TypeDesc owningType = field.OwningType; + if (owningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(helperId, owningType), reason); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(helperId, owningType), reason); + } + } + } + + private void ImportLoadField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "ldsfld" : "ldfld"); + } + + private void ImportAddressOfField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "ldsflda" : "ldflda"); + } + + private void ImportStoreField(int token, bool isStatic) + { + ImportFieldAccess(token, isStatic, isStatic ? "stsfld" : "stfld"); + } + + private void ImportLoadString(int token) + { + // If we care, this can include allocating the frozen string node. + } + + private void ImportBox(int token) + { + AddBoxingDependencies((TypeDesc)_methodIL.GetObject(token), "Box"); + } + + private void AddBoxingDependencies(TypeDesc type, string reason) + { + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(type), reason); + } + + if (type.IsNullable) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Box), reason); + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Box_Nullable), reason); + } + } + + private void ImportLeave(BasicBlock target) + { + ImportFallthrough(target); + } + + private void ImportNewArray(int token) + { + var type = ((TypeDesc)_methodIL.GetObject(token)).MakeArrayType(); + if (type.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "newarr"); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewArray), "newarr"); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.NewArr1, type), "newarr"); + } + } + + private void ImportLoadElement(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelem"); + } + + private void ImportLoadElement(TypeDesc elementType) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelem"); + } + + private void ImportStoreElement(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "stelem"); + } + + private void ImportStoreElement(TypeDesc elementType) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "stelem"); + } + + private void ImportAddressOfElement(int token) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelema"); + } + + private void ImportFallthrough(BasicBlock next) + { + MarkBasicBlock(next); + } + + private int ReadILTokenAt(int ilOffset) + { + return (int)(_ilBytes[ilOffset] + + (_ilBytes[ilOffset + 1] << 8) + + (_ilBytes[ilOffset + 2] << 16) + + (_ilBytes[ilOffset + 3] << 24)); + } + + private bool IsRuntimeHelpersInitializeArray(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "InitializeArray") + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "RuntimeHelpers" && owningType.Namespace == "System.Runtime.CompilerServices"; + } + } + + return false; + } + + private bool IsRuntimeTypeHandleGetValueInternal(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "GetValueInternal") + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "RuntimeTypeHandle" && owningType.Namespace == "System"; + } + } + + return false; + } + + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); + } + + private void ImportNop() { } + private void ImportBreak() { } + private void ImportLoadVar(int index, bool argument) { } + private void ImportStoreVar(int index, bool argument) { } + private void ImportAddressOfVar(int index, bool argument) { } + private void ImportDup() { } + private void ImportPop() { } + private void ImportCalli(int token) { } + private void ImportLoadNull() { } + private void ImportReturn() { } + private void ImportLoadInt(long value, StackValueKind kind) { } + private void ImportLoadFloat(double value) { } + private void ImportLoadIndirect(int token) { } + private void ImportLoadIndirect(TypeDesc type) { } + private void ImportStoreIndirect(int token) { } + private void ImportStoreIndirect(TypeDesc type) { } + private void ImportBinaryOperation(ILOpcode opcode) { } + private void ImportShiftOperation(ILOpcode opcode) { } + private void ImportCompareOperation(ILOpcode opcode) { } + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { } + private void ImportUnaryOperation(ILOpcode opCode) { } + private void ImportCpOpj(int token) { } + private void ImportCkFinite() { } + private void ImportLocalAlloc() { } + private void ImportEndFilter() { } + private void ImportCpBlk() { } + private void ImportInitBlk() { } + private void ImportRethrow() { } + private void ImportSizeOf(int token) { } + private void ImportUnalignedPrefix(byte alignment) { } + private void ImportVolatilePrefix() { } + private void ImportTailPrefix() { } + private void ImportNoPrefix(byte mask) { } + private void ImportReadOnlyPrefix() { } + private void ImportThrow() { } + private void ImportInitObj(int token) { } + private void ImportLoadLength() { } + private void ImportEndFinally() { } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 8a6416681..dc0d6d3a9 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -65,6 +65,9 @@ <Compile Include="..\..\Common\src\Internal\Text\Utf8StringBuilder.cs"> <Link>Common\Utf8StringBuilder.cs</Link> </Compile> + <Compile Include="..\..\Common\src\TypeSystem\IL\ILImporter.cs"> + <Link>IL\ILImporter.cs</Link> + </Compile> <Compile Include="..\..\Common\src\TypeSystem\IL\Stubs\AssemblyGetExecutingAssemblyMethodThunk.cs"> <Link>IL\Stubs\AssemblyGetExecutingAssemblyMethodThunk.cs</Link> </Compile> @@ -109,11 +112,16 @@ <Compile Include="Compiler\CompilerTypeSystemContext.BoxedTypes.cs" /> <Compile Include="Compiler\CompilerTypeSystemContext.Mangling.cs" /> <Compile Include="Compiler\CompilerTypeSystemContext.Sorting.cs" /> + <Compile Include="Compiler\DependencyAnalysis\ILScanNodeFactory.cs" /> <Compile Include="Compiler\DependencyAnalysis\ImportedGenericDictionaryNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\DynamicInvokeTemplateDataNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\ReflectableMethodNode.cs" /> + <Compile Include="Compiler\DependencyAnalysis\ScannedMethodNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\ShadowConcreteUnboxingThunkNode.cs" /> + <Compile Include="Compiler\DependencyTrackingLevel.cs" /> <Compile Include="Compiler\EmptyMetadataManager.cs" /> + <Compile Include="Compiler\ILScanner.cs" /> + <Compile Include="Compiler\ILScannerBuilder.cs" /> <Compile Include="Compiler\ILStreamReader.cs" /> <Compile Include="Compiler\INonEmittableType.cs" /> <Compile Include="Compiler\LibraryInitializers.cs" /> @@ -280,6 +288,7 @@ <Compile Include="Compiler\UnixNodeMangler.cs" /> <Compile Include="Compiler\VirtualMethodCallHelper.cs" /> <Compile Include="Compiler\WindowsNodeMangler.cs" /> + <Compile Include="IL\ILImporter.Scanner.cs" /> <Compile Include="IL\Stubs\PInvokeILProvider.cs" /> <Compile Include="IL\Stubs\StartupCode\StartupCodeMainMethod.cs" /> <Compile Include="IL\Stubs\StartupCode\StartupCodeMainMethod.Sorting.cs" /> diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs index 4f9dde54a..0ad6851c0 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs @@ -17,7 +17,7 @@ namespace ILCompiler CppCodegenConfigProvider _config = new CppCodegenConfigProvider(Array.Empty<string>()); public CppCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) - : base(context, group) + : base(context, group, new CoreRTNameMangler(new CppNodeMangler(), true)) { } @@ -30,7 +30,7 @@ namespace ILCompiler public override ICompilation ToCompilation() { MetadataManager metadataManager = CreateMetadataManager(); - CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, metadataManager); + CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, metadataManager, _nameMangler); DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory); return new CppCodegenCompilation(graph, factory, _compilationRoots, _logger, _config); diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs b/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs index bb7ba16a1..aac834c7d 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs @@ -10,8 +10,8 @@ namespace ILCompiler.DependencyAnalysis { public sealed class CppCodegenNodeFactory : NodeFactory { - public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager) - : base(context, compilationModuleGroup, metadataManager, new CoreRTNameMangler(new CppNodeMangler(), true)) + public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) + : base(context, compilationModuleGroup, metadataManager, nameMangler) { } diff --git a/src/ILCompiler.DependencyAnalysisFramework/src/DependencyNodeCore.cs b/src/ILCompiler.DependencyAnalysisFramework/src/DependencyNodeCore.cs index d43c61e57..9b01f5eb3 100644 --- a/src/ILCompiler.DependencyAnalysisFramework/src/DependencyNodeCore.cs +++ b/src/ILCompiler.DependencyAnalysisFramework/src/DependencyNodeCore.cs @@ -34,6 +34,13 @@ namespace ILCompiler.DependencyAnalysisFramework public class DependencyList : List<DependencyListEntry> { + public DependencyList() { } + + public DependencyList(IEnumerable<DependencyListEntry> collection) + : base(collection) + { + } + public void Add(DependencyNodeCore<DependencyContextType> node, string reason) { diff --git a/src/ILCompiler.TypeSystem/tests/CastingTests.cs b/src/ILCompiler.TypeSystem/tests/CastingTests.cs index b2771234c..573bc1822 100644 --- a/src/ILCompiler.TypeSystem/tests/CastingTests.cs +++ b/src/ILCompiler.TypeSystem/tests/CastingTests.cs @@ -73,6 +73,7 @@ namespace TypeSystemTests TypeDesc shortBasedEnumType = _testModule.GetType("Casting", "ShortBasedEnum"); Assert.True(intType.MakeArrayType().CanCastTo(uintType.MakeArrayType())); + Assert.True(intType.MakeArrayType().CanCastTo(uintType.MakeArrayType(1))); Assert.False(intType.CanCastTo(uintType)); Assert.True(byteType.MakeArrayType().CanCastTo(sbyteType.MakeArrayType())); @@ -121,7 +122,9 @@ namespace TypeSystemTests TypeDesc stringSzArrayType = stringType.MakeArrayType(); TypeDesc objectSzArrayType = objectType.MakeArrayType(); - Assert.False(intSzArrayType.CanCastTo(intArray1Type)); + Assert.True(intSzArrayType.CanCastTo(intArray1Type)); + Assert.False(intArray1Type.CanCastTo(intSzArrayType)); + Assert.False(intArray1Type.CanCastTo(intArray2Type)); Assert.True(intSzArrayType.CanCastTo(arrayType)); diff --git a/src/ILVerify/README.md b/src/ILVerify/README.md new file mode 100644 index 000000000..0bc27fc69 --- /dev/null +++ b/src/ILVerify/README.md @@ -0,0 +1,32 @@ +# ILVerify + +### This directory contains the implementation of ILVerify + +## Intention of this project: +The goal is to create a standalone, cross platform, open-source tool that is capable of verifying MSIL code based on [ECMA-335](https://www.ecma-international.org/publications/standards/Ecma-335.htm). + +The main users of this tool are people working on software that emits MSIL code. These are typically compiler and profiler writers. + +## Other tools +Historically on Full Framework IL generators used PEVerify to make sure that they generated correct IL. PEVerify has some major limitations (e.g. it is tied to the Full Framework, it cannot verify mscorlib.dll, etc.), which initiated this project. + +## Main properties of ILVerify: +- No coupling with CoreLib: ILVerify can point to any assembly and verify it. This also includes the full framework base assemblies (especially mscorlib). +- Cross-platform, Open-Source +- It should be easy to add new verification rules +- Fast spin up/tear down. + +## How to contribute +All ILVerify issues are labeled with [area-ILVerification](https://github.com/search?utf8=%E2%9C%93&q=label%3Aarea-ILVerification&type=). + +ILVerify basically runs through the IL commands in an assembly and does all the verification steps that are specified in ECMA-335. + +Currently every IL command falls into one of these categories: + + - Not implemented: the implementation is completely missing. The easiest way is to pick one of them (look for NotImplentedException in the code) and implement it. First you should 100% understand the spec. (see [ECMA-335](https://www.ecma-international.org/publications/standards/Ecma-335.htm)), then try to port an existing implementation (sources below). + - Partially implemented: These are typically methods with TODOs in it. As the first phase we want to make sure that for every command the stack is correctly maintained, therefore for some commands we either have no verification or we have only a not complete verification. You can also pick one of these and finish it. + - Implemented: find and fix bugs ;) . + +Useful sources: + - [PEVerify source code](https://github.com/lewischeng-ms/sscli/blob/master/clr/src/jit64/newverify.cpp) + - [RyuJIT source code](https://github.com/dotnet/coreclr/blob/master/src/jit), specifically: [exception handling specific part](https://github.com/dotnet/coreclr/blob/master/src/jit/jiteh.cpp), [importer.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp) (look for `Compiler::ver`, `Verify`, `VerifyOrReturn`, and `VerifyOrReturnSpeculative`), [_typeinfo.h](https://github.com/dotnet/coreclr/blob/master/src/jit/_typeinfo.h), [typeinfo.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/typeinfo.cpp)
\ No newline at end of file diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerify/src/ILImporter.Verify.cs index c5c4fcfd9..ad7a43c14 100644 --- a/src/ILVerify/src/ILImporter.Verify.cs +++ b/src/ILVerify/src/ILImporter.Verify.cs @@ -92,8 +92,10 @@ namespace Internal.IL public bool FilterStart; public bool HandlerStart; + //these point to the direct enclosing items in _exceptionRegions public int? TryIndex; public int? HandlerIndex; + public int? FilterIndex; }; void Push(StackValue value) @@ -203,6 +205,13 @@ namespace Internal.IL } } } + if(r.FilterOffset != -1 && r.FilterOffset <= offset && r.HandlerOffset - 1 >= offset) + { + if(!basicBlock.FilterIndex.HasValue) + { + basicBlock.FilterIndex = j; + } + } } } } @@ -500,36 +509,33 @@ namespace Internal.IL if (basicBlock.FilterStart || basicBlock.HandlerStart) { Debug.Assert(basicBlock.EntryStack == null); - - for (int i = 0; i < _exceptionRegions.Length; i++) - { - var r = _exceptionRegions[i]; - if (basicBlock.FilterStart) - { - if (basicBlock.StartOffset != r.ILRegion.FilterOffset) - continue; - } - else - { - if (basicBlock.StartOffset != r.ILRegion.HandlerOffset) - continue; - } + ExceptionRegion r; + if (basicBlock.HandlerIndex.HasValue) + { + r = _exceptionRegions[basicBlock.HandlerIndex.Value]; + } + else if (basicBlock.FilterIndex.HasValue) + { + r = _exceptionRegions[basicBlock.FilterIndex.Value]; + } + else + { + return; + } - if (r.ILRegion.Kind == ILExceptionRegionKind.Filter) - { - basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)) }; - } - else - if (r.ILRegion.Kind == ILExceptionRegionKind.Catch) - { - basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(ResolveTypeToken(r.ILRegion.ClassToken)) }; - } - else - { - basicBlock.EntryStack = s_emptyStack; - } - break; + if (r.ILRegion.Kind == ILExceptionRegionKind.Filter) + { + basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)) }; + } + else + if (r.ILRegion.Kind == ILExceptionRegionKind.Catch) + { + basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(ResolveTypeToken(r.ILRegion.ClassToken)) }; + } + else + { + basicBlock.EntryStack = s_emptyStack; } } @@ -1474,7 +1480,22 @@ namespace Internal.IL void ImportUnaryOperation(ILOpcode opCode) { - throw new NotImplementedException($"{nameof(ImportUnaryOperation)} not implemented"); + var operand = Pop(); + + switch (opCode) + { + case ILOpcode.neg: + CheckIsNumeric(operand); + break; + case ILOpcode.not: + CheckIsInteger(operand); + break; + default: + Debug.Assert(false, "Unexpected branch opcode"); + break; + } + + Push(StackValue.CreatePrimitive(operand.Kind)); } void ImportCpOpj(int token) @@ -1562,7 +1583,12 @@ namespace Internal.IL void ImportEndFilter() { - // TODO: + Check(_currentBasicBlock.FilterIndex.HasValue, VerifierError.Endfilter); + Check(_currentOffset == _exceptionRegions[_currentBasicBlock.FilterIndex.Value].ILRegion.HandlerOffset, VerifierError.Endfilter); + + var result = Pop(); + Check(result.Kind == StackValueKind.Int32, VerifierError.StackUnexpected); + Check(_stackTop == 0, VerifierError.EndfilterStack); } void ImportCpBlk() diff --git a/src/ILVerify/src/Resources/Strings.resx b/src/ILVerify/src/Resources/Strings.resx index 0800832e1..76b717caa 100644 --- a/src/ILVerify/src/Resources/Strings.resx +++ b/src/ILVerify/src/Resources/Strings.resx @@ -135,6 +135,12 @@ <data name="ConstrainedCallWithNonByRefThis" xml:space="preserve"> <value>The 'this' argument to a constrained call must have ByRef type.</value> </data> + <data name="Endfilter" xml:space="preserve"> + <value>Endfilter from outside an exception filter block.</value> + </data> + <data name="EndfilterStack" xml:space="preserve"> + <value>Stack not empty when leaving an exception filter.</value> + </data> <data name="ExpectedArray" xml:space="preserve"> <value>Expected single-dimension zero-based array.</value> </data> diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerify/src/VerifierError.cs index b803b1c8d..37524b6f3 100644 --- a/src/ILVerify/src/VerifierError.cs +++ b/src/ILVerify/src/VerifierError.cs @@ -61,7 +61,7 @@ namespace ILVerify //E_LEAVE "Leave from outside a try or catch block." Rethrow, //"Rethrow from outside a catch handler." //E_ENDFINALLY "Endfinally from outside a finally handler." - //E_ENDFILTER "Endfilter from outside an exception filter block." + Endfilter, //"Endfilter from outside an exception filter block." //E_ENDFILTER_MISSING "Missing Endfilter." //E_BR_INTO_TRY "Branch into try block." //E_BR_INTO_HND "Branch into exception handler block." @@ -186,7 +186,7 @@ namespace ILVerify //E_BOX_PTR_TO_STACK "Box operation on TypedReference, ArgHandle, or ArgIterator." //E_SIG_BYREF_TB_AH "ByRef of TypedReference, ArgHandle, or ArgIterator." //E_SIG_ARRAY_TB_AH "Array of TypedReference, ArgHandle, or ArgIterator." - //E_ENDFILTER_STACK "Stack not empty when leaving an exception filter." + EndfilterStack, //"Stack not empty when leaving an exception filter." //E_DLGT_SIG_I "Unrecognized delegate .ctor signature; expected I." //E_DLGT_SIG_O "Unrecognized delegate .ctor signature; expected Object." //E_RA_PTR_TO_STACK "Mkrefany on TypedReference, ArgHandle, or ArgIterator." diff --git a/src/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/Runtime.Base/src/System/Runtime/TypeCast.cs index d7a549a4b..62dea8448 100644 --- a/src/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/src/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -219,8 +219,16 @@ namespace System.Runtime // compare the array types structurally - if (pObjType->ParameterizedTypeShape == pTargetType->ParameterizedTypeShape && - CastCache.AreTypesAssignableInternal(pObjType->RelatedParameterType, pTargetType->RelatedParameterType, + if (pObjType->ParameterizedTypeShape != pTargetType->ParameterizedTypeShape) + { + // If the shapes are different, there's one more case to check for: Casting SzArray to MdArray rank 1. + if (!pObjType->IsSzArray || pTargetType->ArrayRank != 1) + { + return null; + } + } + + if (CastCache.AreTypesAssignableInternal(pObjType->RelatedParameterType, pTargetType->RelatedParameterType, AssignmentVariation.AllowSizeEquivalence)) { return obj; diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SetErrorMode.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs index 276f49c51..123eb75d7 100644 --- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SetErrorMode.cs +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs @@ -8,8 +8,8 @@ internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, SetLastError = false, EntryPoint = "SetErrorMode", ExactSpelling = true)] - internal static extern uint SetErrorMode(uint newMode); + [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true)] + internal static extern bool SetThreadErrorMode(uint dwNewMode, out uint lpOldMode); internal const uint SEM_FAILCRITICALERRORS = 1; } 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 716ed69e7..981d35811 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -491,7 +491,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SecurityOptions.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEndOfFile.cs"/> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetErrorMode.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WideCharToMultiByte.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_IntPtr.cs"/> diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs index c8671db28..529255131 100644 --- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -54,6 +54,7 @@ namespace System.Diagnostics.Tracing // subclasses of EventProvider use when creating efficient (but unsafe) version of // EventWrite. We do make it a nested type because we really don't expect anyone to use // it except subclasses (and then only rarely). + [StructLayout(LayoutKind.Sequential)] public struct EventData { internal unsafe ulong Ptr; @@ -78,7 +79,7 @@ namespace System.Diagnostics.Tracing private static bool m_setInformationMissing; - private IEventProvider m_eventProvider; // The interface that implements the specific logging mechanism functions. + internal IEventProvider m_eventProvider; // The interface that implements the specific logging mechanism functions. UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback; // Trace Callback function private long m_regHandle; // Trace Registration Handle private byte m_level; // Tracing Level @@ -936,7 +937,7 @@ namespace System.Diagnostics.Tracing // </SecurityKernel> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Performance-critical code")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] - internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, params object[] eventPayload) + internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, params object[] eventPayload) { int status = 0; @@ -1064,7 +1065,7 @@ namespace System.Diagnostics.Tracing userDataPtr[refObjPosition[7]].Ptr = (ulong)v7; } - status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData); + status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, eventHandle, activityID, childActivityID, argCount, userData); } } else @@ -1090,7 +1091,7 @@ namespace System.Diagnostics.Tracing } } - status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData); + status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, eventHandle, activityID, childActivityID, argCount, userData); for (int i = 0; i < refObjIndex; ++i) { @@ -1132,7 +1133,7 @@ namespace System.Diagnostics.Tracing // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" /> // </SecurityKernel> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] - internal unsafe protected bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data) + internal unsafe protected bool WriteEvent(ref EventDescriptor eventDescriptor, IntPtr eventHandle, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data) { if (childActivityID != null) { @@ -1143,7 +1144,7 @@ namespace System.Diagnostics.Tracing (EventOpcode)eventDescriptor.Opcode == EventOpcode.Stop); } - int status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, dataCount, (EventData*)data); + int status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, eventHandle, activityID, childActivityID, dataCount, (EventData*)data); if (status != 0) { @@ -1166,6 +1167,7 @@ namespace System.Diagnostics.Tracing status = m_eventProvider.EventWriteTransferWrapper( m_regHandle, ref eventDescriptor, + IntPtr.Zero, activityID, relatedActivityID, dataCount, @@ -1241,6 +1243,7 @@ namespace System.Diagnostics.Tracing unsafe int IEventProvider.EventWriteTransferWrapper( long registrationHandle, ref EventDescriptor eventDescriptor, + IntPtr eventHandle, Guid* activityId, Guid* relatedActivityId, int userDataCount, @@ -1262,6 +1265,12 @@ namespace System.Diagnostics.Tracing ControlCode, ref ActivityId); } + + // Define an EventPipeEvent handle. + unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, Int64 keywords, uint eventVersion, uint level, byte *pMetadata, uint metadataLength) + { + throw new System.NotSupportedException(); + } } #elif !FEATURE_PERFTRACING @@ -1285,6 +1294,7 @@ namespace System.Diagnostics.Tracing unsafe int IEventProvider.EventWriteTransferWrapper( long registrationHandle, ref EventDescriptor eventDescriptor, + IntPtr eventHandle, Guid* activityId, Guid* relatedActivityId, int userDataCount, @@ -1297,6 +1307,12 @@ namespace System.Diagnostics.Tracing { return 0; } + + // Define an EventPipeEvent handle. + unsafe IntPtr IEventProvider.DefineEventHandle(uint eventID, string eventName, Int64 keywords, uint eventVersion, uint level, byte *pMetadata, uint metadataLength) + { + throw new System.NotSupportedException(); + } } #endif 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 ed840a2e0..3349c069c 100644..100755 --- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs @@ -686,6 +686,106 @@ namespace System.Diagnostics.Tracing Initialize(eventSourceGuid, eventSourceName, traits); } +#if FEATURE_PERFTRACING + // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly + // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere. + private unsafe void DefineEventPipeEvents() + { + Debug.Assert(m_eventData != null); + Debug.Assert(m_provider != null); + int cnt = m_eventData.Length; + for (int i = 0; i < cnt; i++) + { + uint eventID = (uint)m_eventData[i].Descriptor.EventId; + if (eventID == 0) + continue; + + string eventName = m_eventData[i].Name; + Int64 keywords = m_eventData[i].Descriptor.Keywords; + uint eventVersion = m_eventData[i].Descriptor.Version; + uint level = m_eventData[i].Descriptor.Level; + + // evnetID : 4 bytes + // eventName : (eventName.Length + 1) * 2 bytes + // keywords : 8 bytes + // eventVersion : 4 bytes + // level : 4 bytes + // parameterCount : 4 bytes + uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2; + + // Increase the metadataLength for the types of all parameters. + metadataLength += (uint)m_eventData[i].Parameters.Length * 4; + + // Increase the metadataLength for the names of all parameters. + foreach (var parameter in m_eventData[i].Parameters) + { + string parameterName = parameter.Name; + metadataLength = metadataLength + ((uint)parameterName.Length + 1) * 2; + } + + byte[] metadata = new byte[metadataLength]; + + // Write metadata: evnetID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name... + fixed (byte *pMetadata = metadata) + { + uint offset = 0; + WriteToBuffer(pMetadata, metadataLength, ref offset, eventID); + fixed(char *pEventName = eventName) + { + WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pEventName, ((uint)eventName.Length + 1) * 2); + } + WriteToBuffer(pMetadata, metadataLength, ref offset, keywords); + WriteToBuffer(pMetadata, metadataLength, ref offset, eventVersion); + WriteToBuffer(pMetadata, metadataLength, ref offset, level); + WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)m_eventData[i].Parameters.Length); + foreach (var parameter in m_eventData[i].Parameters) + { + // Write parameter type. + WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)Type.GetTypeCode(parameter.ParameterType)); + + // Write parameter name. + string parameterName = parameter.Name; + fixed (char *pParameterName = parameterName) + { + WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pParameterName, ((uint)parameterName.Length + 1) * 2); + } + } + Debug.Assert(metadataLength == offset); + IntPtr eventHandle = m_provider.m_eventProvider.DefineEventHandle(eventID, eventName, keywords, eventVersion, level, pMetadata, metadataLength); + m_eventData[i].EventHandle = eventHandle; + } + } + } + + // Copy src to buffer and modify the offset. + // Note: We know the buffer size ahead of time to make sure no buffer overflow. + private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength) + { + Debug.Assert(bufferLength >= (offset + srcLength)); + for (int i = 0; i < srcLength; i++) + { + *(byte *)(buffer + offset + i) = *(byte *)(src + i); + } + offset += srcLength; + } + + // Copy uint value to buffer. + private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, uint value) + { + Debug.Assert(bufferLength >= (offset + 4)); + *(uint *)(buffer + offset) = value; + offset += 4; + } + + // Copy long value to buffer. + private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, long value) + { + Debug.Assert(bufferLength >= (offset + 8)); + *(long *)(buffer + offset) = value; + offset += 8; + } +#endif + internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes) { // @@ -1179,7 +1279,7 @@ namespace System.Diagnostics.Tracing // by default the Descriptor.Keyword will have the perEventSourceSessionId bit // mask set to 0x0f so, when all ETW sessions want the event we don't need to // synthesize a new one - if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) + if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) ThrowEventSourceException(m_eventData[eventId].Name); } else @@ -1199,7 +1299,7 @@ namespace System.Diagnostics.Tracing m_eventData[eventId].Descriptor.Task, unchecked((long)etwSessions.ToEventKeywords() | origKwd)); - if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) + if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) ThrowEventSourceException(m_eventData[eventId].Name); } } @@ -1229,7 +1329,7 @@ namespace System.Diagnostics.Tracing #else if (!SelfDescribingEvents) { - if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) + if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) ThrowEventSourceException(m_eventData[eventId].Name); } else @@ -1322,6 +1422,7 @@ namespace System.Diagnostics.Tracing if (disposing) { #if FEATURE_MANAGED_ETW +#if !FEATURE_PERFTRACING // Send the manifest one more time to ensure circular buffers have a chance to get to this information // even in scenarios with a high volume of ETW events. if (m_eventSourceEnabled) @@ -1334,6 +1435,7 @@ namespace System.Diagnostics.Tracing { } // If it fails, simply give up. m_eventSourceEnabled = false; } +#endif if (m_provider != null) { m_provider.Dispose(); @@ -1468,7 +1570,8 @@ namespace System.Diagnostics.Tracing // Set m_provider, which allows this. m_provider = provider; -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN && !PLATFORM_UNIX) +#if PLATFORM_WINDOWS +#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) // API available on OS >= Win 8 and patched Win 7. // Disable only for FrameworkEventSource to avoid recursion inside exception handling. if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove) @@ -1486,8 +1589,8 @@ namespace System.Diagnostics.Tracing metadataHandle.Free(); } +#endif // PLATFORM_WINDOWS #endif // FEATURE_MANAGED_ETW - Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted. // We are logically completely initialized at this point. m_completelyInited = true; @@ -1939,7 +2042,7 @@ namespace System.Diagnostics.Tracing // by default the Descriptor.Keyword will have the perEventSourceSessionId bit // mask set to 0x0f so, when all ETW sessions want the event we don't need to // synthesize a new one - if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args)) + if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args)) ThrowEventSourceException(m_eventData[eventId].Name); } else @@ -1956,7 +2059,7 @@ namespace System.Diagnostics.Tracing m_eventData[eventId].Descriptor.Task, unchecked((long)etwSessions.ToEventKeywords() | origKwd)); - if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args)) + if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args)) ThrowEventSourceException(m_eventData[eventId].Name); } } @@ -1986,7 +2089,7 @@ namespace System.Diagnostics.Tracing #else if (!SelfDescribingEvents) { - if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args)) + if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args)) ThrowEventSourceException(m_eventData[eventId].Name); } else @@ -2188,7 +2291,7 @@ namespace System.Diagnostics.Tracing [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")] private unsafe void WriteEventString(EventLevel level, long keywords, string msgString) { -#if FEATURE_MANAGED_ETW +#if FEATURE_MANAGED_ETW && !FEATURE_PERFTRACING if (m_provider != null) { string eventName = "EventSourceMessage"; @@ -2224,7 +2327,7 @@ namespace System.Diagnostics.Tracing data.Ptr = (ulong)msgStringPtr; data.Size = (uint)(2 * (msgString.Length + 1)); data.Reserved = 0; - m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data)); + m_provider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data)); } } } @@ -2509,6 +2612,7 @@ namespace System.Diagnostics.Tracing partial struct EventMetadata { public EventDescriptor Descriptor; + public IntPtr EventHandle; // EventPipeEvent handle. public EventTags Tags; public bool EnabledForAnyListener; // true if any dispatcher has this event turned on public bool EnabledForETW; // is this event on for the OS ETW data dispatcher? @@ -2671,11 +2775,13 @@ namespace System.Diagnostics.Tracing { // eventSourceDispatcher == null means this is the ETW manifest +#if !FEATURE_PERFTRACING // Note that we unconditionally send the manifest whenever we are enabled, even if // we were already enabled. This is because there may be multiple sessions active // and we can't know that all the sessions have seen the manifest. if (!SelfDescribingEvents) SendManifest(m_rawManifest); +#endif } #if FEATURE_ACTIVITYSAMPLING @@ -2799,12 +2905,14 @@ namespace System.Diagnostics.Tracing } else { +#if !FEATURE_PERFTRACING if (commandArgs.Command == EventCommand.SendManifest) { // TODO: should we generate the manifest here if we hadn't already? if (m_rawManifest != null) SendManifest(m_rawManifest); } +#endif // These are not used for non-update commands and thus should always be 'default' values // Debug.Assert(enable == true); @@ -3024,6 +3132,7 @@ namespace System.Diagnostics.Tracing { // GetMetadata failed, so we have to set it via reflection. Debug.Assert(m_rawManifest == null); + m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this); Debug.Assert(m_eventData != null); @@ -3057,6 +3166,10 @@ namespace System.Diagnostics.Tracing dispatcher.m_EventEnabled = new bool[m_eventData.Length]; dispatcher = dispatcher.m_Next; } +#if FEATURE_PERFTRACING + // Initialize the EventPipe event handles. + DefineEventPipeEvents(); +#endif } if (s_currentPid == 0) { @@ -3111,7 +3224,7 @@ namespace System.Diagnostics.Tracing dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize); if (m_provider != null) { - if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs)) + if (!m_provider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs)) { // Turns out that if users set the BufferSize to something less than 64K then WriteEvent // can fail. If we get this failure on the first chunk try again with something smaller @@ -3678,6 +3791,7 @@ namespace System.Diagnostics.Tracing eventData[eventAttribute.EventId].Message = eventAttribute.Message; eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions; eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID; + eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero; } // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs index 0b51e52ec..71a2fe4d4 100644 --- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs @@ -27,6 +27,7 @@ namespace System.Diagnostics.Tracing unsafe int EventWriteTransferWrapper( long registrationHandle, ref EventDescriptor eventDescriptor, + IntPtr eventHandle, Guid* activityId, Guid* relatedActivityId, int userDataCount, @@ -34,5 +35,8 @@ namespace System.Diagnostics.Tracing // Get or set the per-thread activity ID. int EventActivityIdControl(UnsafeNativeMethods.ManifestEtw.ActivityControl ControlCode, ref Guid ActivityId); + + // Define an EventPipeEvent handle. + unsafe IntPtr DefineEventHandle(uint eventID, string eventName, Int64 keywords, uint eventVersion, uint level, byte *pMetadata, uint metadataLength); } } diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs index 0045ebeaf..61cd00789 100644 --- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs +++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs @@ -2,13 +2,8 @@ // 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.Buffers; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.IO { @@ -38,7 +33,8 @@ namespace System.IO flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); // Don't pop up a dialog for reading from an empty floppy drive - uint oldMode = Interop.Kernel32.SetErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS); + uint oldMode; + bool success = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); try { SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero); @@ -70,7 +66,8 @@ namespace System.IO } finally { - Interop.Kernel32.SetErrorMode(oldMode); + if (success) + Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 5e29b5907..67c6378c8 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -147,6 +147,14 @@ namespace Internal.Runtime.Augments } } + if (lengths.Length == 1) + { + // We just checked above that all lower bounds are zero. In that case, we should actually allocate + // a new SzArray instead. + RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(typeHandleForArrayType.ToEETypePtr().ArrayElementType); + return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), lengths[0]); + } + // Create a local copy of the lenghts that cannot be motified by the caller int* pLengths = stackalloc int[lengths.Length]; for (int i = 0; i < lengths.Length; i++) diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs index 1f76a8b00..8b27d139f 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs @@ -41,6 +41,13 @@ namespace Internal.Runtime.CompilerHelpers return ret; } + else if (nDimensions == 1) + { + // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated + // as an SzArray. SzArray is castable to MdArray rank 1. + RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(eeType.ArrayElementType); + return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), pDimensions[0]); + } else { // Multidimensional arrays have two ctors, one with and one without lower bounds diff --git a/src/System.Private.CoreLib/src/System/Array.CoreRT.cs b/src/System.Private.CoreLib/src/System/Array.CoreRT.cs index 31168971e..aaa4af523 100644 --- a/src/System.Private.CoreLib/src/System/Array.CoreRT.cs +++ b/src/System.Private.CoreLib/src/System/Array.CoreRT.cs @@ -181,15 +181,7 @@ namespace System elementType = elementType.UnderlyingSystemType; - if (lengths.Length == 1 && lowerBounds[0] == 0) - { - int length = lengths[0]; - return CreateSzArray(elementType, length); - } - else - { - return CreateMultiDimArray(elementType, lengths, lowerBounds); - } + return CreateMultiDimArray(elementType, lengths, lowerBounds); } private static Array CreateSzArray(Type elementType, int length) @@ -990,6 +982,13 @@ namespace System Debug.Assert(eeType.IsArray && !eeType.IsSzArray); Debug.Assert(rank == eeType.ArrayRank); + // Code below assumes 0 lower bounds. MdArray of rank 1 with zero lower bounds should never be allocated. + // The runtime always allocates an SzArray for those: + // * newobj instance void int32[0...]::.ctor(int32)" actually gives you int[] + // * int[] is castable to int[*] to make it mostly transparent + // The callers need to check for this. + Debug.Assert(rank != 1); + ulong totalLength = 1; bool maxArrayDimensionLengthOverflow = false; @@ -1679,11 +1678,9 @@ namespace System } } -#if CORERT public class MDArray { public const int MinRank = 1; public const int MaxRank = 32; } -#endif } diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource_CoreRT.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource_CoreRT.cs index 9e778549c..fc7e17d2a 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource_CoreRT.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource_CoreRT.cs @@ -186,6 +186,7 @@ namespace System.Diagnostics.Tracing this.ActivityOptions = EventActivityOptions.None; this.ParameterTypes = parameterTypes; this.HasRelatedActivityID = false; + this.EventHandle = IntPtr.Zero; } } diff --git a/src/System.Private.CoreLib/src/System/MDArray.cs b/src/System.Private.CoreLib/src/System/MDArray.cs index 5614e9b1f..a7256bdc7 100644 --- a/src/System.Private.CoreLib/src/System/MDArray.cs +++ b/src/System.Private.CoreLib/src/System/MDArray.cs @@ -24,12 +24,6 @@ namespace System // The desktop CLR supports arrays of up to 32 dimensions so that provides // an upper limit on how much this needs to be built out. - public class MDArray - { - public const int MinRank = 2; - public const int MaxRank = 32; - } - [StructLayout(LayoutKind.Sequential)] public class MDArrayRank2<T> { diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 4c0d38f19..aa960c752 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -17,5 +17,28 @@ namespace System.Runtime.Loader public static event ResolveEventHandler TypeResolve; public static event ResolveEventHandler ResourceResolve; public static event ResolveEventHandler AssemblyResolve; + + public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving; + public event Action<AssemblyLoadContext> Unloading; + + public static AssemblyLoadContext Default { get; } = new DefaultAssemblyLoadContext(); + + public static AssemblyLoadContext GetLoadContext(Assembly assembly) + { + return Default; + } + + public Assembly LoadFromAssemblyPath(string assemblyPath) + { + throw new PlatformNotSupportedException(); + } + } + + /// <summary> + /// AssemblyLoadContext is not supported in .NET Native. This is + /// just a dummy class to make applications compile. + /// </summary> + internal class DefaultAssemblyLoadContext : AssemblyLoadContext + { } } diff --git a/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs b/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs index eee73680f..15b7fcc54 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; +using System.Diagnostics; using System.Runtime.InteropServices; namespace System.Threading @@ -12,10 +13,95 @@ namespace System.Threading // internal partial class TimerQueue { + /// <summary> + /// This event is used by the timer thread to wait for timer expiration. It is also + /// used to notify the timer thread that a new timer has been set. + /// </summary> + private static AutoResetEvent s_timerEvent; + + /// <summary> + /// This field stores the value of next timer that the timer thread should install. + /// </summary> + private static volatile int s_nextTimerDuration; + private void SetTimer(uint actualDuration) { - // UNIXTODO: Timer - throw new NotImplementedException(); + // This function is called with the TimerQueue lock acquired + Debug.Assert(Lock.IsAcquired); + + // Note: AutoResetEvent.WaitOne takes an Int32 value as a timeout. + // The TimerQueue code ensures that timer duration is not greater than max Int32 value + Debug.Assert(actualDuration <= (uint)Int32.MaxValue); + s_nextTimerDuration = (int)actualDuration; + + // If this is the first time the timer is set then we need to create a thread that + // will manage and respond to timer requests. Otherwise, simply signal the timer thread + // to notify it that the timer duration has changed. + if (s_timerEvent == null) + { + s_timerEvent = new AutoResetEvent(false); + ThreadPool.QueueLongRunningWork(TimerThread); + } + else + { + s_timerEvent.Set(); + } + } + + + /// <summary> + /// This method is executed on a dedicated a timer thread. Its purpose is + /// to handle timer request and notify the TimerQueue when a timer expires. + /// </summary> + private static void TimerThread() + { + int currentTimerInterval; + + // Get wait time for the next timer + currentTimerInterval = Interlocked.Exchange(ref s_nextTimerDuration, Timeout.Infinite); + + for (;;) + { + // Wait for the current timer to expire. + // We will be woken up because either 1) the wait times out, which will indicate that + // the current timer has expired and/or 2) the TimerQueue installs a new (earlier) timer. + int startWait = TickCount; + bool timerHasExpired = !s_timerEvent.WaitOne(currentTimerInterval); + uint elapsedTime = (uint)(TickCount - startWait); + + // The timer event can be set after this thread reads the new timer interval but before it enters + // the wait state. This can cause a spurious wake up. In addition, expiration of current timer can + // happen almost at the same time as this thread is signaled to install a new timer. To handle + // these cases, we need to update the current interval based on the elapsed time. + if (currentTimerInterval != Timeout.Infinite) + { + if (elapsedTime >= currentTimerInterval) + { + timerHasExpired = true; + } + else + { + currentTimerInterval -= (int)elapsedTime; + } + } + + // Check whether TimerQueue needs to process expired timers. + if (timerHasExpired) + { + Instance.FireNextTimers(); + + // When FireNextTimers() installs a new timer, it also sets the timer event. + // Reset the event so the timer thread is not woken up right away unnecessary. + s_timerEvent.Reset(); + currentTimerInterval = Timeout.Infinite; + } + + int nextTimerInterval = Interlocked.Exchange(ref s_nextTimerDuration, Timeout.Infinite); + if (nextTimerInterval != Timeout.Infinite) + { + currentTimerInterval = nextTimerInterval; + } + } } private static int TickCount diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs index 252e1a05e..feb436d87 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs @@ -65,34 +65,12 @@ namespace System.Reflection.Runtime.TypeInfos InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, delegate (Object _this, Object[] args) { - if (rank == 1) - { - // Legacy: This seems really wrong in the rank1-multidim case (as it's a case of a synthetic constructor that's declared on T[*] returning an instance of T[]) - // This is how the desktop behaves, however. - - int count = (int)(args[0]); - - RuntimeTypeInfo vectorType; - if (multiDim) - { - vectorType = arrayType.InternalRuntimeElementType.GetArrayType(); - } - else - { - vectorType = arrayType; - } - - return ReflectionCoreExecution.ExecutionEnvironment.NewArray(vectorType.TypeHandle, count); - } - else + int[] lengths = new int[rank]; + for (int i = 0; i < rank; i++) { - int[] lengths = new int[rank]; - for (int i = 0; i < rank; i++) - { - lengths[i] = (int)(args[i]); - } - return ReflectionCoreExecution.ExecutionEnvironment.NewMultiDimArray(arrayType.TypeHandle, lengths, null); + lengths[i] = (int)(args[i]); } + return ReflectionCoreExecution.ExecutionEnvironment.NewMultiDimArray(arrayType.TypeHandle, lengths, null); } ); } diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs index 9a90eac5d..537e00bae 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs @@ -107,7 +107,7 @@ namespace Internal.Runtime.TypeLoader return s_nativeLayoutInterfacesAlgorithm; } - public override DefType GetWellKnownType(WellKnownType wellKnownType) + public override DefType GetWellKnownType(WellKnownType wellKnownType, bool throwIfNotFound = true) { switch (wellKnownType) { @@ -190,7 +190,10 @@ namespace Internal.Runtime.TypeLoader return (DefType)ResolveRuntimeTypeHandle(typeof(Exception).TypeHandle); default: - throw new NotImplementedException(); + if (throwIfNotFound) + throw new NotImplementedException(); + else + return null; } } |