diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs index 5ecca71fa86..bd3bb233b12 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs @@ -91,7 +91,7 @@ public CompilationBuilder UsePreinitializationManager(PreinitializationManager m protected PreinitializationManager GetPreinitializationManager() { if (_preinitializationManager == null) - return new PreinitializationManager(_context, _compilationGroup, GetILProvider()); + return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), enableInterpreter: false); return _preinitializationManager; } diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs index 48daeab7087..dd4a418314f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs @@ -13,22 +13,27 @@ namespace ILCompiler { internal class CompilerMetadataFieldLayoutAlgorithm : MetadataFieldLayoutAlgorithm { + // GC statics start with a pointer to the "EEType" that signals the size and GCDesc to the GC + public static LayoutInt GetGCStaticFieldOffset(TypeSystemContext context) => context.Target.LayoutPointerSize; + protected override void PrepareRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) { - // GC statics start with a pointer to the "EEType" that signals the size and GCDesc to the GC - layout.GcStatics.Size = context.Target.LayoutPointerSize; - layout.ThreadGcStatics.Size = context.Target.LayoutPointerSize; + LayoutInt offset = GetGCStaticFieldOffset(context); + layout.GcStatics.Size = offset; + layout.ThreadGcStatics.Size = offset; } protected override void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) { + LayoutInt offset = GetGCStaticFieldOffset(context); + // If the size of GCStatics is equal to the size set in PrepareRuntimeSpecificStaticFieldLayout, we // don't have any GC statics - if (layout.GcStatics.Size == context.Target.LayoutPointerSize) + if (layout.GcStatics.Size == offset) { layout.GcStatics.Size = LayoutInt.Zero; } - if (layout.ThreadGcStatics.Size == context.Target.LayoutPointerSize) + if (layout.ThreadGcStatics.Size == offset) { layout.ThreadGcStatics.Size = LayoutInt.Zero; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs similarity index 56% rename from src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs rename to src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs index 8d210f5c19e..5b2fed54786 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs @@ -2,32 +2,32 @@ // 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.Diagnostics; using Internal.Text; using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis { /// - /// Represents a frozen array + /// Represents a frozen object that is statically preallocated within the data section + /// of the executable instead of on the GC heap. /// - public class FrozenArrayNode : EmbeddedObjectNode, ISymbolDefinitionNode + public class FrozenObjectNode : EmbeddedObjectNode, ISymbolDefinitionNode { - private PreInitFieldInfo _preInitFieldInfo; + private readonly FieldDesc _field; + private readonly TypePreinit.ISerializableReference _data; - public FrozenArrayNode(PreInitFieldInfo preInitFieldInfo) + public FrozenObjectNode(FieldDesc field, TypePreinit.ISerializableReference data) { - _preInitFieldInfo = preInitFieldInfo; + _field = field; + _data = data; } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenArr_") - .Append(nameMangler.GetMangledFieldName(_preInitFieldInfo.Field)); + sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenObj_") + .Append(nameMangler.GetMangledFieldName(_field)); } public override bool StaticDependenciesAreComputed => true; @@ -38,41 +38,18 @@ int ISymbolDefinitionNode.Offset { get { - // The frozen array symbol points at the EEType portion of the object, skipping over the sync block - return OffsetFromBeginningOfArray + _preInitFieldInfo.Field.Context.Target.PointerSize; + // The frozen object symbol points at the EEType portion of the object, skipping over the sync block + return OffsetFromBeginningOfArray + _field.Context.Target.PointerSize; } } - private IEETypeNode GetEETypeNode(NodeFactory factory) - { - var fieldType = _preInitFieldInfo.Type; - var node = factory.ConstructedTypeSymbol(fieldType); - Debug.Assert(!node.RepresentsIndirectionCell); // Array are always local - return node; - } - public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) { // Sync Block dataBuilder.EmitZeroPointer(); - // EEType - dataBuilder.EmitPointerReloc(GetEETypeNode(factory)); - - // numComponents - dataBuilder.EmitInt(_preInitFieldInfo.Length); - - int pointerSize = _preInitFieldInfo.Field.Context.Target.PointerSize; - Debug.Assert(pointerSize == 8 || pointerSize == 4); - - if (pointerSize == 8) - { - // padding numComponents in 64-bit - dataBuilder.EmitInt(0); - } - // byte contents - _preInitFieldInfo.WriteData(ref dataBuilder, factory, relocsOnly); + _data.WriteContent(ref dataBuilder, factory); } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -105,7 +82,7 @@ protected override void OnMarked(NodeFactory factory) public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - return _preInitFieldInfo.CompareTo(((FrozenArrayNode)other)._preInitFieldInfo, comparer); + return comparer.Compare(((FrozenObjectNode)other)._field, _field); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs index e4b5d721492..db1023f1e74 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -14,14 +14,16 @@ namespace ILCompiler.DependencyAnalysis { public class GCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode, ISymbolNodeWithDebugInfo { - private MetadataType _type; - private List _preInitFieldInfos; + private readonly MetadataType _type; + private readonly TypePreinit.PreinitializationInfo _preinitializationInfo; - public GCStaticsNode(MetadataType type) + public GCStaticsNode(MetadataType type, PreinitializationManager preinitManager) { Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); _type = type; - _preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: true); + + if (preinitManager.IsPreinitialized(type)) + _preinitializationInfo = preinitManager.GetPreinitializationInfo(_type); } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -51,8 +53,8 @@ private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory) public GCStaticsPreInitDataNode NewPreInitDataNode() { - Debug.Assert(_preInitFieldInfos != null); - return new GCStaticsPreInitDataNode(_type, _preInitFieldInfos); + Debug.Assert(_preinitializationInfo != null && _preinitializationInfo.IsPreinitialized); + return new GCStaticsPreInitDataNode(_preinitializationInfo); } protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) @@ -65,9 +67,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } dependencyList.Add(factory.GCStaticsRegion, "GCStatics Region"); - dependencyList.Add(GetGCStaticEETypeNode(factory), "GCStatic EEType"); - if (_preInitFieldInfos != null) - dependencyList.Add(factory.GCStaticsPreInitDataNode(_type), "PreInitData node"); dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection"); EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); @@ -82,47 +81,25 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { - if (factory.Target.Abi == TargetAbi.CoreRT || factory.Target.Abi == TargetAbi.CppCodegen) - { - ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); - builder.RequireInitialPointerAlignment(); + builder.RequireInitialPointerAlignment(); - int delta = GCStaticRegionConstants.Uninitialized; + int delta = GCStaticRegionConstants.Uninitialized; - // Set the flag that indicates next pointer following EEType is the preinit data - if (_preInitFieldInfos != null) - delta |= GCStaticRegionConstants.HasPreInitializedData; + // Set the flag that indicates next pointer following EEType is the preinit data + bool isPreinitialized = _preinitializationInfo != null && _preinitializationInfo.IsPreinitialized; + if (isPreinitialized) + delta |= GCStaticRegionConstants.HasPreInitializedData; - builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); - - if (_preInitFieldInfos != null) - builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); + builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); - builder.AddSymbol(this); + if (isPreinitialized) + builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); - return builder.ToObjectData(); - } - else - { - if (_preInitFieldInfos == null) - { - ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); - builder.RequireInitialPointerAlignment(); - - builder.EmitZeros(_type.GCStaticFieldSize.AsInt); - - builder.AddSymbol(this); - - return builder.ToObjectData(); - } - else - { - _preInitFieldInfos.Sort(PreInitFieldInfo.FieldDescCompare); - return GCStaticsPreInitDataNode.GetDataForPreInitDataField(this, _type, _preInitFieldInfos, 0, factory, relocsOnly); - } - } + return builder.ToObjectData(); } public override int ClassCode => -522346696; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs index e78801952ee..2c82481af06 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs @@ -2,8 +2,6 @@ // 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.Collections.Generic; - using Internal.Text; using Internal.TypeSystem; @@ -19,28 +17,23 @@ namespace ILCompiler.DependencyAnalysis /// public class GCStaticsPreInitDataNode : ObjectNode, ISymbolDefinitionNode { - private MetadataType _type; - private List _sortedPreInitFields; + private TypePreinit.PreinitializationInfo _preinitializationInfo; - public GCStaticsPreInitDataNode(MetadataType type, List preInitFields) + public GCStaticsPreInitDataNode(TypePreinit.PreinitializationInfo preinitializationInfo) { - Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); - _type = type; - - // sort the PreInitFieldInfo to appear in increasing offset order for easier emitting - _sortedPreInitFields = new List(preInitFields); - _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare); + Debug.Assert(!preinitializationInfo.Type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + _preinitializationInfo = preinitializationInfo; } - protected override string GetName(NodeFactory factory) => GetMangledName(_type, factory.NameMangler); + protected override string GetName(NodeFactory factory) => GetMangledName(_preinitializationInfo.Type, factory.NameMangler); public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append(GetMangledName(_type, nameMangler)); + sb.Append(GetMangledName(_preinitializationInfo.Type, nameMangler)); } public int Offset => 0; - public MetadataType Type => _type; + public MetadataType Type => _preinitializationInfo.Type; public static string GetMangledName(TypeDesc type, NameMangler nameMangler) { @@ -49,66 +42,52 @@ public static string GetMangledName(TypeDesc type, NameMangler nameMangler) public override bool StaticDependenciesAreComputed => true; - public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; - public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type); - - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + public override ObjectNodeSection Section { - // We only need this for CoreRT (at least for now) as we emit static field value directly in GCStaticsNode for N - Debug.Assert(factory.Target.Abi == TargetAbi.CoreRT); - - return GetDataForPreInitDataField( - this, _type, _sortedPreInitFields, - factory.Target.PointerSize, // CoreRT static size calculation includes EEType - skip it - factory, relocsOnly); + get + { + if (Type.Context.Target.IsWindows) + return ObjectNodeSection.ReadOnlyDataSection; + else + return ObjectNodeSection.DataSection; + } } + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_preinitializationInfo.Type); - public static ObjectData GetDataForPreInitDataField( - ISymbolDefinitionNode node, - MetadataType _type, List sortedPreInitFields, - int startOffset, - NodeFactory factory, bool relocsOnly = false) + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); - builder.RequireInitialAlignment(_type.GCStaticFieldAlignment.AsInt); + MetadataType type = _preinitializationInfo.Type; - int staticOffset = startOffset; - int staticOffsetEnd = _type.GCStaticFieldSize.AsInt; - int idx = 0; + builder.RequireInitialAlignment(factory.Target.PointerSize); - while (staticOffset < staticOffsetEnd) + // GC static fields don't begin at offset 0, need to subtract that. + int initialOffset = CompilerMetadataFieldLayoutAlgorithm.GetGCStaticFieldOffset(factory.TypeSystemContext).AsInt; + + foreach (FieldDesc field in type.GetFields()) { - PreInitFieldInfo fieldInfo = idx < sortedPreInitFields.Count ? sortedPreInitFields[idx] : null; - int writeTo = staticOffsetEnd; - if (fieldInfo != null) - writeTo = fieldInfo.Field.Offset.AsInt; - - // Emit the zero before the next preinitField - builder.EmitZeros(writeTo - staticOffset); - staticOffset = writeTo; - - if (fieldInfo != null) - { - int count = builder.CountBytes; - - if (fieldInfo.Field.FieldType.IsValueType) - { - // Emit inlined data for value types - fieldInfo.WriteData(ref builder, factory); - } - else - { - // Emit a pointer reloc to the frozen data for reference types - builder.EmitPointerReloc(factory.SerializedFrozenArray(fieldInfo)); - } - - staticOffset += builder.CountBytes - count; - idx++; - } + if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || !field.HasGCStaticBase) + continue; + + int padding = field.Offset.AsInt - initialOffset - builder.CountBytes; + Debug.Assert(padding >= 0); + builder.EmitZeros(padding); + + TypePreinit.ISerializableValue val = _preinitializationInfo.GetFieldValue(field); + int currentOffset = builder.CountBytes; + if (val != null) + val.WriteFieldData(ref builder, field, factory); + else + builder.EmitZeroPointer(); + Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt); } - builder.AddSymbol(node); + int pad = _preinitializationInfo.Type.GCStaticFieldSize.AsInt - builder.CountBytes - initialOffset; + Debug.Assert(pad >= 0); + builder.EmitZeros(pad); + + builder.AddSymbol(this); return builder.ToObjectData(); } @@ -117,7 +96,7 @@ public static ObjectData GetDataForPreInitDataField( public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - return comparer.Compare(_type, ((GCStaticsPreInitDataNode)other)._type); + return comparer.Compare(_preinitializationInfo.Type, ((GCStaticsPreInitDataNode)other)._preinitializationInfo.Type); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 28f0c005ea9..dbafc0502f4 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -186,7 +186,7 @@ private void CreateNodeCaches() { if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) { - return new NonGCStaticsNode(type, this); + return new NonGCStaticsNode(type, PreinitializationManager); } else { @@ -198,7 +198,7 @@ private void CreateNodeCaches() { if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) { - return new GCStaticsNode(type); + return new GCStaticsNode(type, PreinitializationManager); } else { @@ -334,9 +334,9 @@ private void CreateNodeCaches() return new FrozenStringNode(data, Target); }); - _frozenArrayNodes = new NodeCache((PreInitFieldInfo fieldInfo) => + _frozenObjectNodes = new NodeCache(key => { - return new FrozenArrayNode(fieldInfo); + return new FrozenObjectNode(key.Owner, key.SerializableObject); }); _interfaceDispatchCells = new NodeCache(callSiteCell => @@ -988,11 +988,11 @@ public FrozenStringNode SerializedStringObject(string data) return _frozenStringNodes.GetOrAdd(data); } - private NodeCache _frozenArrayNodes; + private NodeCache _frozenObjectNodes; - public FrozenArrayNode SerializedFrozenArray(PreInitFieldInfo preInitFieldInfo) + public FrozenObjectNode SerializedFrozenObject(FieldDesc owningField, TypePreinit.ISerializableReference data) { - return _frozenArrayNodes.GetOrAdd(preInitFieldInfo); + return _frozenObjectNodes.GetOrAdd(new SerializedFrozenObjectKey(owningField, data)); } private NodeCache _eagerCctorIndirectionNodes; @@ -1231,5 +1231,21 @@ public UninitializedWritableDataBlobKey(Utf8String name, int size, int alignment public override bool Equals(object obj) => obj is UninitializedWritableDataBlobKey && Equals((UninitializedWritableDataBlobKey)obj); public override int GetHashCode() => Name.GetHashCode(); } + + protected struct SerializedFrozenObjectKey : IEquatable + { + public readonly FieldDesc Owner; + public readonly TypePreinit.ISerializableReference SerializableObject; + + public SerializedFrozenObjectKey(FieldDesc owner, TypePreinit.ISerializableReference obj) + { + Owner = owner; + SerializableObject = obj; + } + + public override bool Equals(object obj) => obj is SerializedFrozenObjectKey && Equals((SerializedFrozenObjectKey)obj); + public bool Equals(SerializedFrozenObjectKey other) => Owner == other.Owner; + public override int GetHashCode() => Owner.GetHashCode(); + } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs index f29c5c74983..e237ff48d60 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -20,21 +20,14 @@ namespace ILCompiler.DependencyAnalysis /// public class NonGCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode, ISymbolNodeWithDebugInfo { - private MetadataType _type; - private NodeFactory _factory; - private List _sortedPreInitFields; + private readonly MetadataType _type; + private readonly PreinitializationManager _preinitializationManager; - public NonGCStaticsNode(MetadataType type, NodeFactory factory) + public NonGCStaticsNode(MetadataType type, PreinitializationManager preinitializationManager) { Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); _type = type; - _factory = factory; - var preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: false); - if (preInitFieldInfos != null) - { - _sortedPreInitFields = new List(preInitFieldInfos); - _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare); - } + _preinitializationManager = preinitializationManager; } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -58,9 +51,9 @@ int ISymbolDefinitionNode.Offset get { // Make sure the NonGCStatics symbol always points to the beginning of the data. - if (_factory.PreinitializationManager.HasLazyStaticConstructor(_type)) + if (_preinitializationManager.HasLazyStaticConstructor(_type)) { - return GetClassConstructorContextStorageSize(_factory.Target, _type); + return GetClassConstructorContextStorageSize(_type.Context.Target, _type); } else { @@ -69,7 +62,7 @@ int ISymbolDefinitionNode.Offset } } - public bool HasCCtorContext => _factory.PreinitializationManager.HasLazyStaticConstructor(_type); + public bool HasCCtorContext => _preinitializationManager.HasLazyStaticConstructor(_type); public IDebugInfo DebugInfo => NullTypeIndexDebugInfo.Instance; @@ -146,31 +139,28 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) builder.RequireInitialAlignment(_type.NonGCStaticFieldAlignment.AsInt); } - if (_sortedPreInitFields != null) + if (_preinitializationManager.IsPreinitialized(_type)) { - int staticOffsetBegin = builder.CountBytes; - int staticOffsetEnd = builder.CountBytes + _type.NonGCStaticFieldSize.AsInt; - int staticOffset = staticOffsetBegin; - int idx = 0; - - while (staticOffset < staticOffsetEnd) + TypePreinit.PreinitializationInfo preinitInfo = _preinitializationManager.GetPreinitializationInfo(_type); + int initialOffset = builder.CountBytes; + foreach (FieldDesc field in _type.GetFields()) { - int writeTo = staticOffsetEnd; - if (idx < _sortedPreInitFields.Count) - writeTo = staticOffsetBegin + _sortedPreInitFields[idx].Field.Offset.AsInt; - - // Emit the zeros before the next preinitField - builder.EmitZeros(writeTo - staticOffset); - staticOffset = writeTo; - - // Emit the data - if (idx < _sortedPreInitFields.Count) - { - _sortedPreInitFields[idx].WriteData(ref builder, factory); - idx++; - staticOffset = builder.CountBytes; - } + if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || field.HasGCStaticBase) + continue; + + int padding = field.Offset.AsInt - builder.CountBytes + initialOffset; + Debug.Assert(padding >= 0); + builder.EmitZeros(padding); + + TypePreinit.ISerializableValue val = preinitInfo.GetFieldValue(field); + int currentOffset = builder.CountBytes; + val.WriteFieldData(ref builder, field, factory); + Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt); } + + int pad = _type.NonGCStaticFieldSize.AsInt - builder.CountBytes + initialOffset; + Debug.Assert(pad >= 0); + builder.EmitZeros(pad); } else { diff --git a/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs b/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs deleted file mode 100644 index d8c8fc57065..00000000000 --- a/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs +++ /dev/null @@ -1,392 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Debug = System.Diagnostics.Debug; - -using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; -using ILCompiler.DependencyAnalysis; - -namespace ILCompiler -{ - public abstract class PreInitFixupInfo : IComparable - { - /// - /// Offset into the blob - /// - public int Offset { get; } - - public PreInitFixupInfo(int offset) - { - Offset = offset; - } - - int IComparable.CompareTo(PreInitFixupInfo other) - { - return this.Offset - other.Offset; - } - - /// - /// Writes fixup data into current ObjectDataBuilder. Caller needs to make sure ObjectDataBuilder is - /// at correct offset before writing. - /// - public abstract void WriteData(ref ObjectDataBuilder builder, NodeFactory factory); - } - - public class PreInitTypeFixupInfo : PreInitFixupInfo - { - public TypeDesc TypeFixup { get; } - - public PreInitTypeFixupInfo(int offset, TypeDesc type) - :base(offset) - { - TypeFixup = type; - } - - public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) - { - builder.EmitPointerRelocOrIndirectionReference(factory.MaximallyConstructableType(TypeFixup)); - } - } - - public class PreInitMethodFixupInfo : PreInitFixupInfo - { - public MethodDesc MethodFixup { get; } - - public PreInitMethodFixupInfo(int offset, MethodDesc method) - : base(offset) - { - MethodFixup = method; - } - - public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) - { - builder.EmitPointerReloc(factory.ExactCallableAddress(MethodFixup)); - } - } - - public class PreInitFieldFixupInfo : PreInitFixupInfo - { - public FieldDesc FieldFixup { get; } - - public PreInitFieldFixupInfo(int offset, FieldDesc field) - : base(offset) - { - FieldFixup = field; - } - - public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) - { - MetadataType type = (MetadataType)FieldFixup.OwningType; - - // Do not support fixing up fields from external modules - if (!factory.CompilationModuleGroup.ContainsType(type)) - throw new BadImageFormatException(); - - ISymbolNode staticBase = FieldFixup.HasGCStaticBase ? factory.TypeGCStaticsSymbol(type) : factory.TypeNonGCStaticsSymbol(type); - builder.EmitPointerReloc(staticBase, FieldFixup.Offset.AsInt); - } - } - - public class PreInitFieldInfo - { - public FieldDesc Field { get; } - - /// - /// The type of the real field data. This could be a subtype of the field type. - /// - public TypeDesc Type { get; } - - /// - /// Points to the underlying contents of the data. - /// - public byte[] Data { get; } - - /// - /// Start offset of the real contents in the data. - /// - public int Offset { get; } - - /// - /// Number of elements, if this is a frozen array. - /// - public int Length { get; } - - /// - /// List of fixup to be apply to the data blob - /// This is needed for information that can't be encoded into blob ahead of time before codegen - /// - private List FixupInfos; - - public PreInitFieldInfo(FieldDesc field, TypeDesc type, byte[] data, int offset, int length, List fixups) - { - Field = field; - Type = type; - Data = data; - Offset = offset; - Length = length; - FixupInfos = fixups; - - if (FixupInfos != null) - FixupInfos.Sort(); - } - - public void WriteData(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly = false) - { - int offset = Offset; - - if (FixupInfos != null) - { - int startOffset = builder.CountBytes; - - for (int i = 0; i < FixupInfos.Count; ++i) - { - var fixupInfo = FixupInfos[i]; - - // do we have overlapping fixups? - if (fixupInfo.Offset < offset) - throw new BadImageFormatException(); - - if (!relocsOnly) - { - // emit bytes before fixup - builder.EmitBytes(Data, offset, fixupInfo.Offset - offset); - } - - // write the fixup - FixupInfos[i].WriteData(ref builder, factory); - - // move pointer past the fixup - offset = Offset + builder.CountBytes - startOffset; - } - } - - if (offset > Data.Length) - throw new BadImageFormatException(); - - if (!relocsOnly) - { - // Emit remaining bytes - builder.EmitBytes(Data, offset, Data.Length - offset); - } - } - - public static List GetPreInitFieldInfos(TypeDesc type, bool hasGCStaticBase) - { - List list = null; - - foreach (var field in type.GetFields()) - { - if (!field.IsStatic || field.IsThreadStatic) - continue; - - if (field.HasGCStaticBase != hasGCStaticBase) - continue; - - var dataField = GetPreInitDataField(field); - if (dataField != null) - { - if (list == null) - list = new List(); - list.Add(ConstructPreInitFieldInfo(field, dataField)); - } - } - - return list; - } - - /// - /// Retrieves the corresponding static preinitialized data field by looking at various attributes - /// - private static FieldDesc GetPreInitDataField(FieldDesc thisField) - { - Debug.Assert(thisField.IsStatic); - - var field = thisField as EcmaField; - if (field == null) - return null; - - var decoded = field.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "InitDataBlobAttribute"); - if (decoded == null) - return null; - - var decodedValue = decoded.Value; - if (decodedValue.FixedArguments.Length != 2) - throw new BadImageFormatException(); - - var typeDesc = decodedValue.FixedArguments[0].Value as TypeDesc; - if (typeDesc == null) - throw new BadImageFormatException(); - - if (decodedValue.FixedArguments[1].Type != field.Context.GetWellKnownType(WellKnownType.String)) - throw new BadImageFormatException(); - - var fieldName = (string)decodedValue.FixedArguments[1].Value; - var dataField = typeDesc.GetField(fieldName); - if (dataField== null) - throw new BadImageFormatException(); - - return dataField; - } - - /// - /// Extract preinitialize data as byte[] from a RVA field, and perform necessary validations. - /// - private static PreInitFieldInfo ConstructPreInitFieldInfo(FieldDesc field, FieldDesc dataField) - { - if (!dataField.HasRva) - throw new BadImageFormatException(); - - var ecmaDataField = dataField as EcmaField; - if (ecmaDataField == null) - throw new NotSupportedException(); - - var rvaData = ecmaDataField.GetFieldRvaData(); - var fieldType = field.FieldType; - int elementCount; - int realDataOffset; - TypeDesc realDataType = null; - - // - // Construct fixups - // - List fixups = null; - - var typeFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "TypeHandleFixupAttribute"); - foreach (var typeFixupAttr in typeFixupAttrs) - { - if (typeFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) - throw new BadImageFormatException(); - - int offset = (int)typeFixupAttr.FixedArguments[0].Value; - var typeArg = typeFixupAttr.FixedArguments[1].Value; - var fixupType = typeArg as TypeDesc; - if (fixupType == null) - { - if (typeArg is string fixupTypeName) - { - fixupType = CustomAttributeTypeNameParser.GetTypeByCustomAttributeTypeName(ecmaDataField.Module, fixupTypeName, throwIfNotFound: true); - } - else - { - throw new BadImageFormatException(); - } - } - - fixups = fixups ?? new List(); - - if (offset == 0 && fieldType.IsSzArray) - { - // For array field, offset 0 is the element type handle followed by the element count - realDataType = fixupType; - } - else - { - fixups.Add(new PreInitTypeFixupInfo(offset, fixupType)); - } - } - - var methodFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "MethodAddrFixupAttribute"); - foreach (var methodFixupAttr in methodFixupAttrs) - { - if (methodFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) - throw new BadImageFormatException(); - - int offset = (int)methodFixupAttr.FixedArguments[0].Value; - TypeDesc fixupType = methodFixupAttr.FixedArguments[1].Value as TypeDesc; - if (fixupType == null) - throw new BadImageFormatException(); - - string methodName = methodFixupAttr.FixedArguments[2].Value as string; - if (methodName == null) - throw new BadImageFormatException(); - - var method = fixupType.GetMethod(methodName, signature : null); - if (method == null) - throw new BadImageFormatException(); - - fixups = fixups ?? new List(); - - fixups.Add(new PreInitMethodFixupInfo(offset, method)); - } - - var fieldFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "FieldAddrFixupAttribute"); - foreach (var fieldFixupAttr in fieldFixupAttrs) - { - if (fieldFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) - throw new BadImageFormatException(); - - int offset = (int)fieldFixupAttr.FixedArguments[0].Value; - TypeDesc fixupType = fieldFixupAttr.FixedArguments[1].Value as TypeDesc; - if (fixupType == null) - throw new BadImageFormatException(); - - string fieldName = fieldFixupAttr.FixedArguments[2].Value as string; - if (fieldName == null) - throw new BadImageFormatException(); - - var fixupField = fixupType.GetField(fieldName); - if (fixupField == null) - throw new BadImageFormatException(); - - if (!fixupField.IsStatic) - throw new BadImageFormatException(); - - fixups = fixups ?? new List(); - - fixups.Add(new PreInitFieldFixupInfo(offset, fixupField)); - } - - if (fieldType.IsValueType || fieldType.IsPointer) - { - elementCount = -1; - realDataOffset = 0; - realDataType = fieldType; - } - else if (fieldType.IsSzArray) - { - // Offset 0 is the element type handle fixup followed by the element count - if (realDataType == null) - throw new BadImageFormatException(); - - int ptrSize = fieldType.Context.Target.PointerSize; - elementCount = rvaData[ptrSize] | rvaData[ptrSize + 1] << 8 | rvaData[ptrSize + 2] << 16 | rvaData[ptrSize + 3] << 24; - realDataOffset = ptrSize * 2; - realDataType = realDataType.MakeArrayType(); - } - else - { - throw new NotSupportedException(); - } - - return new PreInitFieldInfo(field, realDataType, rvaData, realDataOffset, elementCount, fixups); - } - - public static int FieldDescCompare(PreInitFieldInfo fieldInfo1, PreInitFieldInfo fieldInfo2) - { - return fieldInfo1.Field.Offset.AsInt - fieldInfo2.Field.Offset.AsInt; - } - - public int CompareTo(PreInitFieldInfo other, TypeSystemComparer comparer) - { - if (Length != other.Length) - return Length - other.Length; - - var compare = comparer.Compare(Field, other.Field); - if (compare != 0) - return compare; - - Debug.Assert(Data.Length == other.Data.Length); - for (int i = 0; i < Data.Length; i++) - { - if (Data[i] != other.Data[i]) - return Data[i] - other.Data[i]; - } - - return 0; - } - } -} diff --git a/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs b/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs index b77b18f69a5..d3a03d8dff2 100644 --- a/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; using Internal.IL; using Internal.TypeSystem; @@ -13,14 +14,13 @@ namespace ILCompiler public class PreinitializationManager { private readonly bool _supportsLazyCctors; - private readonly CompilationModuleGroup _compilationModuleGroup; - private readonly ILProvider _ilprovider; + private readonly bool _enableInterpreter; - public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider) + public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, bool enableInterpreter) { _supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", false) != null; - _compilationModuleGroup = compilationGroup; - _ilprovider = ilprovider; + _preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider); + _enableInterpreter = enableInterpreter; } /// @@ -29,8 +29,28 @@ public PreinitializationManager(TypeSystemContext context, CompilationModuleGrou /// public bool HasLazyStaticConstructor(TypeDesc type) { - return type.HasStaticConstructor && !HasEagerConstructorAttribute(type) && _supportsLazyCctors && - (!(type is MetadataType) || !((MetadataType)type).IsModuleType); + if (!type.HasStaticConstructor) + return false; + + // If the cctor runs eagerly at startup, it's not lazy + if (HasEagerConstructorAttribute(type)) + return false; + + // If the class library doesn't support lazy cctors, everything is preinitialized before Main + // either by interpretting the cctor at compile time, or by running the cctor eagerly at startup. + if (!_supportsLazyCctors) + return false; + + // Would be odd to see a type with a cctor that is not MetadataType + Debug.Assert(type is MetadataType); + var mdType = (MetadataType)type; + + // The cctor on the Module type is the module constructor. That's special. + if (mdType.IsModuleType) + return false; + + // If we can't interpret the cctor at compile time, the cctor is lazy. + return !IsPreinitialized(mdType); } /// @@ -39,7 +59,19 @@ public bool HasLazyStaticConstructor(TypeDesc type) /// public bool HasEagerStaticConstructor(TypeDesc type) { - return type.HasStaticConstructor && (HasEagerConstructorAttribute(type) || !_supportsLazyCctors); + if (!type.HasStaticConstructor) + return false; + + // Would be odd to see a type with a cctor that is not MetadataType + Debug.Assert(type is MetadataType); + var mdType = (MetadataType)type; + + // If the type is preinitialized at compile time, that's not eager. + if (IsPreinitialized(mdType)) + return false; + + // If the type is marked as eager or classlib doesn't have a cctor runner, it's eager. + return HasEagerConstructorAttribute(type) || !_supportsLazyCctors; } private static bool HasEagerConstructorAttribute(TypeDesc type) @@ -48,5 +80,92 @@ private static bool HasEagerConstructorAttribute(TypeDesc type) return mdType != null && mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute"); } + + public bool IsPreinitialized(MetadataType type) + { + // If the cctor interpreter is not enabled, no type is preinitialized. + if (!_enableInterpreter) + return false; + + if (!type.HasStaticConstructor) + return false; + + // The cctor on the Module type is the module constructor. That's special. + if (type.IsModuleType) + return false; + + if (type.HasInstantiation) + { + // Generic definitions cannot be preinitialized + if (type.IsGenericDefinition) + return false; + + // If the type has a canonical form the runtime type loader could create + // a new type sharing code with this one. They need to agree on how + // initialization happens. We can't preinitialize runtime-created + // generic types at compile time. + if (type.ConvertToCanonForm(CanonicalFormKind.Specific) + .IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + } + + return GetPreinitializationInfo(type).IsPreinitialized; + } + + public void LogStatistics(Logger logger) + { + if (!_enableInterpreter) + return; + + int totalEligibleTypes = 0; + int totalPreinitializedTypes = 0; + + if (logger.IsVerbose) + { + foreach (var item in LockFreeReaderHashtable.Enumerator.Get(_preinitHashTable)) + { + totalEligibleTypes++; + if (item.IsPreinitialized) + { + logger.Writer.WriteLine($"Preinitialized type '{item.Type}'"); + totalPreinitializedTypes++; + } + else + { + logger.Writer.WriteLine($"Could not preinitialize '{item.Type}': {item.FailureReason}"); + } + } + + logger.Writer.WriteLine($"Preinitialized {totalPreinitializedTypes} types out of {totalEligibleTypes}."); + } + } + + public TypePreinit.PreinitializationInfo GetPreinitializationInfo(MetadataType type) + { + return _preinitHashTable.GetOrCreateValue(type); + } + + class PreinitializationInfoHashtable : LockFreeReaderHashtable + { + private readonly CompilationModuleGroup _compilationGroup; + private readonly ILProvider _ilProvider; + + public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider) + { + _compilationGroup = compilationGroup; + _ilProvider = ilProvider; + } + + protected override bool CompareKeyToValue(MetadataType key, TypePreinit.PreinitializationInfo value) => key == value.Type; + protected override bool CompareValueToValue(TypePreinit.PreinitializationInfo value1, TypePreinit.PreinitializationInfo value2) => value1.Type == value2.Type; + protected override int GetKeyHashCode(MetadataType key) => key.GetHashCode(); + protected override int GetValueHashCode(TypePreinit.PreinitializationInfo value) => value.Type.GetHashCode(); + + protected override TypePreinit.PreinitializationInfo CreateValueFromKey(MetadataType key) + { + return TypePreinit.ScanType(_compilationGroup, _ilProvider, key); + } + } + private PreinitializationInfoHashtable _preinitHashTable; } } diff --git a/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs b/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs new file mode 100644 index 00000000000..b4e37c67453 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs @@ -0,0 +1,1987 @@ +// 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.Diagnostics; + +using ILCompiler.DependencyAnalysis; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + // Class that computes the initial state of static fields on a type by interpreting the static constructor. + // + // Values are represented by instances of an abstract Value class. Several specialized descendants of + // the Value class exist, representing value types (including e.g. a specialized class representing + // RuntimeFieldHandle), or reference types (including e.g. specialized class representing an array). + // + // For simplicity, non-reference values are represented as byte arrays. This requires many short lived array + // allocations, but makes a lot of things simpler (e.g. byrefs to values are essentially free because they + // only carry a reference to the original array and an optional index). + // + // When dealing with non-reference types (valuetypes and unmanaged pointers) we need to be careful + // about assignment semantics. Some operations need to make a copy of the valuetype bytes while others + // are fine to reuse the original byte array. Whenever storing a value into a location, we need to assign + // a new value to the existing Value instance to keep byrefs working. + public class TypePreinit + { + private readonly MetadataType _type; + private readonly CompilationModuleGroup _compilationGroup; + private readonly ILProvider _ilProvider; + private readonly Dictionary _fieldValues = new Dictionary(); + private readonly Dictionary _internedStrings = new Dictionary(); + + private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationGroup, ILProvider ilProvider) + { + _type = owningType; + _compilationGroup = compilationGroup; + _ilProvider = ilProvider; + + // Zero initialize all fields we model. + foreach (var field in owningType.GetFields()) + { + if (!field.IsStatic || field.IsLiteral || field.IsThreadStatic || field.HasRva) + continue; + + _fieldValues.Add(field, NewUninitializedLocationValue(field.FieldType)); + } + } + + public static PreinitializationInfo ScanType(CompilationModuleGroup compilationGroup, ILProvider ilProvider, MetadataType type) + { + Debug.Assert(type.HasStaticConstructor); + Debug.Assert(!type.IsGenericDefinition); + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + var preinit = new TypePreinit(type, compilationGroup, ilProvider); + + Status status; + try + { + status = preinit.TryScanMethod(type.GetStaticConstructor(), null, null, out _); + } + catch (TypeSystemException ex) + { + status = Status.Fail(type.GetStaticConstructor(), ex.Message); + } + + if (status.IsSuccessful) + { + var values = new List>(); + foreach (var kvp in preinit._fieldValues) + values.Add(new KeyValuePair(kvp.Key, kvp.Value)); + + return new PreinitializationInfo(type, values); + } + + return new PreinitializationInfo(type, status.FailureReason); + } + + private Status TryScanMethod(MethodDesc method, Value[] parameters, Stack recursionProtect, out Value returnValue) + { + MethodIL methodIL = _ilProvider.GetMethodIL(method); + if (methodIL == null) + { + returnValue = null; + return Status.Fail(method, "Extern method"); + } + + return TryScanMethod(methodIL, parameters, recursionProtect, out returnValue); + } + + private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack recursionProtect, out Value returnValue) + { + returnValue = default; + + if (recursionProtect != null && recursionProtect.Contains(methodIL.OwningMethod)) + return Status.Fail(methodIL.OwningMethod, "Recursion"); + + ILExceptionRegion[] ehRegions = methodIL.GetExceptionRegions(); + if (ehRegions != null && ehRegions.Length > 0) + { + // We don't care about catch/filter/fault because those only run when an exception happens + // (exceptions will never happen here). But finally needs to run in non-exceptional paths + // and we don't model that yet. + foreach (ILExceptionRegion ehRegion in ehRegions) + { + if (ehRegion.Kind == ILExceptionRegionKind.Finally) + return Status.Fail(methodIL.OwningMethod, "Finally regions"); + } + } + + var reader = new ILReader(methodIL.GetILBytes()); + + TypeSystemContext context = methodIL.OwningMethod.Context; + + var stack = new Stack(methodIL.MaxStack, context.Target); + + LocalVariableDefinition[] localTypes = methodIL.GetLocals(); + Value[] locals = new Value[localTypes.Length]; + for (int i = 0; i < localTypes.Length; i++) + { + locals[i] = NewUninitializedLocationValue(localTypes[i].Type); + } + + // Read IL opcodes and interpret their semantics. + // + // This is not a full interpreter and we're allowed to not interpret everything. If a semantic is + // not implemented by the interpreter, we simply fail. + // + // We also need to do basic sanity checking for invalid IL to protect us from crashing. These + // all throw the TypeSystem's InvalidProgramException. The exception doesn't need to exactly match + // the runtime exception. We just need something reasonably catchable to abort interpreting. + // + // We throw instead of returning false to aid debuggability of the interpreter (we shouldn't see + // exceptions in normal code so an exception is usually a bug). + + while (reader.HasNext) + { + ILOpcode opcode = reader.ReadILOpcode(); + switch (opcode) + { + case ILOpcode.ldc_i4_m1: + case ILOpcode.ldc_i4_s: + case ILOpcode.ldc_i4: + case ILOpcode.ldc_i4_0: + case ILOpcode.ldc_i4_1: + case ILOpcode.ldc_i4_2: + case ILOpcode.ldc_i4_3: + case ILOpcode.ldc_i4_4: + case ILOpcode.ldc_i4_5: + case ILOpcode.ldc_i4_6: + case ILOpcode.ldc_i4_7: + case ILOpcode.ldc_i4_8: + { + int value = opcode switch + { + ILOpcode.ldc_i4_m1 => -1, + ILOpcode.ldc_i4_s => (sbyte)reader.ReadILByte(), + ILOpcode.ldc_i4 => (int)reader.ReadILUInt32(), + _ => opcode - ILOpcode.ldc_i4_0, + }; + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value)); + } + break; + + case ILOpcode.ldc_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)reader.ReadILUInt64())); + break; + + case ILOpcode.ldc_r4: + case ILOpcode.ldc_r8: + stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble( + opcode == ILOpcode.ldc_r4 ? reader.ReadILFloat() : reader.ReadILDouble())); + break; + + case ILOpcode.sizeof_: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(type.GetElementSize().AsInt)); + } + break; + + case ILOpcode.ldnull: + stack.Push((ReferenceTypeValue)null); + break; + + case ILOpcode.newarr: + { + if (!stack.TryPopIntValue(out int elementCount)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + const int MaximumInterpretedArraySize = 8192; + + TypeDesc elementType = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (elementCount > 0 + && (elementType.IsGCPointer + || (elementType.IsValueType && ((DefType)elementType).ContainsGCPointers))) + { + return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers"); + } + + if (elementCount < 0 + || elementCount > MaximumInterpretedArraySize) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Array out of bounds"); + } + + stack.Push(new ArrayInstance(elementType.MakeArrayType(), elementCount)); + } + break; + + case ILOpcode.dup: + if (stack.Count == 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + stack.Push(stack.Peek()); + break; + + case ILOpcode.ldstr: + { + string s = (string)methodIL.GetObject(reader.ReadILToken()); + if (!_internedStrings.TryGetValue(s, out StringInstance instance)) + { + instance = new StringInstance(context.GetWellKnownType(WellKnownType.String), s); + _internedStrings.Add(s, instance); + } + stack.Push(instance); + } + break; + + case ILOpcode.ret: + { + bool returnsVoid = methodIL.OwningMethod.Signature.ReturnType.IsVoid; + if ((returnsVoid && stack.Count > 0) + || (!returnsVoid && stack.Count != 1)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (!returnsVoid) + { + returnValue = stack.PopIntoLocation(methodIL.OwningMethod.Signature.ReturnType); + } + return Status.Success; + } + + case ILOpcode.nop: + case ILOpcode.volatile_: + break; + + case ILOpcode.stsfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.OwningType != _type) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Store into other static"); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + if (_fieldValues[field] is IAssignableValue assignableField) + assignableField.Assign(stack.PopIntoLocation(field.FieldType)); + else + _fieldValues[field] = stack.PopIntoLocation(field.FieldType); + } + break; + + case ILOpcode.ldsfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.OwningType != _type) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Load from other static" + (field.IsInitOnly ? " initonly " : "")); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + stack.PushFromLocation(field.FieldType, _fieldValues[field]); + } + break; + + case ILOpcode.ldsflda: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (!field.IsStatic || field.IsLiteral) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (field.OwningType != _type) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Address of other static"); + } + + if (field.IsThreadStatic || field.HasRva) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static"); + } + + if (!(_fieldValues[field] is ValueTypeValue vtfield)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported byref"); + } + + stack.Push(vtfield.CreateByRef()); + } + break; + + case ILOpcode.call: + { + MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodSignature methodSig = method.Signature; + int paramOffset = methodSig.IsStatic ? 0 : 1; + int numParams = methodSig.Length + paramOffset; + + TypeDesc owningType = method.OwningType; + if (!_compilationGroup.CanInline(methodIL.OwningMethod, method)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline"); + } + + if (owningType.HasStaticConstructor + && owningType != methodIL.OwningMethod.OwningType + && !((MetadataType)owningType).IsBeforeFieldInit) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor"); + } + + Value[] methodParams = new Value[numParams]; + for (int i = numParams - 1; i >= 0; i--) + { + methodParams[i] = stack.PopIntoLocation(GetArgType(method, i)); + } + + Value retVal; + if (!method.IsIntrinsic || !TryHandleIntrinsicCall(method, methodParams, out retVal)) + { + recursionProtect ??= new Stack(); + recursionProtect.Push(methodIL.OwningMethod); + Status callResult = TryScanMethod(method, methodParams, recursionProtect, out retVal); + if (!callResult.IsSuccessful) + { + recursionProtect.Pop(); + return callResult; + } + recursionProtect.Pop(); + } + + if (!methodSig.ReturnType.IsVoid) + stack.PushFromLocation(methodSig.ReturnType, retVal); + } + break; + + case ILOpcode.newobj: + { + MethodDesc ctor = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodSignature ctorSig = ctor.Signature; + + TypeDesc owningType = ctor.OwningType; + if (!_compilationGroup.CanInline(methodIL.OwningMethod, ctor) + || !_compilationGroup.ContainsType(owningType)) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline"); + } + + if (owningType.HasStaticConstructor + && owningType != methodIL.OwningMethod.OwningType + && !((MetadataType)owningType).IsBeforeFieldInit) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor"); + } + + if (!owningType.IsDefType) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Not a class or struct"); + } + + if (owningType.HasFinalizer) + { + // Finalizer might have observable side effects + return Status.Fail(methodIL.OwningMethod, opcode, "Finalizable class"); + } + + if (((DefType)owningType).ContainsGCPointers) + { + // We don't want to end up with GC pointers in the frozen region + // because write barriers can't handle that. + return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers"); + } + + Value instance; + Value ctorArg0; + if (owningType.IsValueType) + { + instance = new ValueTypeValue(owningType); + ctorArg0 = ((ValueTypeValue)instance).CreateByRef(); + } + else + { + instance = new ObjectInstance((DefType)owningType); + ctorArg0 = instance; + } + + Value[] ctorParameters = new Value[ctorSig.Length + 1]; + ctorParameters[0] = ctorArg0; + for (int i = ctorSig.Length - 1; i >= 0; i--) + { + ctorParameters[i + 1] = stack.PopIntoLocation(GetArgType(ctor, i + 1)); + } + recursionProtect ??= new Stack(); + recursionProtect.Push(methodIL.OwningMethod); + Status ctorCallResult = TryScanMethod(ctor, ctorParameters, recursionProtect, out _); + if (!ctorCallResult.IsSuccessful) + { + recursionProtect.Pop(); + return ctorCallResult; + } + + recursionProtect.Pop(); + + stack.PushFromLocation(owningType, instance); + } + break; + + case ILOpcode.stfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + + if (field.FieldType.IsGCPointer + || field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Reference field"); + } + + Value value = stack.PopIntoLocation(field.FieldType); + StackEntry instance = stack.Pop(); + + var settableInstance = instance.Value as IHasInstanceFields; + if (settableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + settableInstance.SetField(field, value); + } + break; + + case ILOpcode.ldfld: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + + if (field.FieldType.IsGCPointer + || field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + StackEntry instance = stack.Pop(); + + var loadableInstance = instance.Value as IHasInstanceFields; + if (loadableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + Value fieldValue = loadableInstance.GetField(field); + + stack.PushFromLocation(field.FieldType, fieldValue); + } + break; + + case ILOpcode.ldflda: + { + FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken()); + if (field.FieldType.IsGCPointer + || field.IsStatic) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + StackEntry instance = stack.Pop(); + + var loadableInstance = instance.Value as IHasInstanceFields; + if (loadableInstance == null) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + stack.Push(loadableInstance.GetFieldAddress(field)); + } + break; + + case ILOpcode.conv_i: + case ILOpcode.conv_u: + case ILOpcode.conv_i2: + case ILOpcode.conv_i4: + case ILOpcode.conv_i8: + case ILOpcode.conv_u2: + case ILOpcode.conv_u8: + { + StackEntry popped = stack.Pop(); + if (popped.ValueKind == StackValueKind.Int32) + { + int val = popped.Value.AsInt32(); + switch (opcode) + { + case ILOpcode.conv_i: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32(val)); + break; + case ILOpcode.conv_u: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((uint)val) : ValueTypeValue.FromInt32(val)); + break; + case ILOpcode.conv_i2: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((short)val)); + break; + case ILOpcode.conv_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(val)); + break; + case ILOpcode.conv_u2: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)val)); + break; + case ILOpcode.conv_u8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((uint)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else if (popped.ValueKind == StackValueKind.NativeInt) + { + long val = context.Target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32(); + switch (opcode) + { + case ILOpcode.conv_i4: + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else if (popped.ValueKind == StackValueKind.Int64) + { + long val = popped.Value.AsInt64(); + switch (opcode) + { + case ILOpcode.conv_u: + stack.Push(StackValueKind.NativeInt, + context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32((int)val)); + break; + } + } + else if (popped.ValueKind == StackValueKind.Float) + { + double val = popped.Value.AsDouble(); + switch (opcode) + { + case ILOpcode.conv_i8: + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val)); + break; + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.ldarg_0: + case ILOpcode.ldarg_1: + case ILOpcode.ldarg_2: + case ILOpcode.ldarg_3: + case ILOpcode.ldarg_s: + case ILOpcode.ldarg: + { + int index = opcode switch + { + ILOpcode.ldarg_s => reader.ReadILByte(), + ILOpcode.ldarg => reader.ReadILUInt16(), + _ => opcode - ILOpcode.ldarg_0, + }; + stack.PushFromLocation(GetArgType(methodIL.OwningMethod, index), parameters[index]); + } + break; + + case ILOpcode.starg_s: + case ILOpcode.starg: + { + int index = opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte(); + TypeDesc argType = GetArgType(methodIL.OwningMethod, index); + if (parameters[index] is IAssignableValue assignableParam) + assignableParam.Assign(stack.PopIntoLocation(argType)); + else + parameters[index] = stack.PopIntoLocation(argType); + } + break; + + case ILOpcode.ldtoken: + { + var token = methodIL.GetObject(reader.ReadILToken()); + if (!(token is FieldDesc field)) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + stack.Push(new StackEntry(StackValueKind.ValueType, new RuntimeFieldHandleValue(field))); + } + break; + + case ILOpcode.ldloc_0: + case ILOpcode.ldloc_1: + case ILOpcode.ldloc_2: + case ILOpcode.ldloc_3: + case ILOpcode.ldloc_s: + case ILOpcode.ldloc: + { + int index = opcode switch + { + ILOpcode.ldloc_s => reader.ReadILByte(), + ILOpcode.ldloc => reader.ReadILUInt16(), + _ => opcode - ILOpcode.ldloc_0, + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + stack.PushFromLocation(localTypes[index].Type, locals[index]); + } + break; + + case ILOpcode.stloc_0: + case ILOpcode.stloc_1: + case ILOpcode.stloc_2: + case ILOpcode.stloc_3: + case ILOpcode.stloc_s: + case ILOpcode.stloc: + { + int index = opcode switch + { + ILOpcode.stloc_s => reader.ReadILByte(), + ILOpcode.stloc => reader.ReadILUInt16(), + _ => opcode - ILOpcode.stloc_0, + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + TypeDesc localType = localTypes[index].Type; + if (locals[index] is IAssignableValue assignableLocal) + assignableLocal.Assign(stack.PopIntoLocation(localType)); + else + locals[index] = stack.PopIntoLocation(localType); + + } + break; + + case ILOpcode.ldloca_s: + case ILOpcode.ldloca: + { + int index = opcode switch + { + ILOpcode.ldloca_s => reader.ReadILByte(), + ILOpcode.ldloca => reader.ReadILUInt16(), + _ => throw new NotImplementedException(), // Unreachable + }; + + if (index >= locals.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + if (locals[index] is ValueTypeValue vtvalue) + { + stack.Push(vtvalue.CreateByRef()); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.initobj: + { + StackEntry popped = stack.Pop(); + if (popped.ValueKind != StackValueKind.ByRef) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + TypeDesc token = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (token.IsGCPointer) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + ((ByRefValue)popped.Value).Initialize(token.GetElementSize().AsInt); + } + break; + + case ILOpcode.br: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.blt: + case ILOpcode.blt_un: + case ILOpcode.bgt: + case ILOpcode.bgt_un: + case ILOpcode.beq: + case ILOpcode.bne_un: + case ILOpcode.bge: + case ILOpcode.bge_un: + case ILOpcode.ble: + case ILOpcode.ble_un: + case ILOpcode.br_s: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.blt_s: + case ILOpcode.blt_un_s: + case ILOpcode.bgt_s: + case ILOpcode.bgt_un_s: + case ILOpcode.beq_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_s: + case ILOpcode.bge_un_s: + case ILOpcode.ble_s: + case ILOpcode.ble_un_s: + { + int delta = opcode >= ILOpcode.br ? + (int)reader.ReadILUInt32() : + (sbyte)reader.ReadILByte(); + int target = reader.Offset + delta; + if (target < 0 + || target > reader.Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + ILOpcode normalizedOpcode = opcode >= ILOpcode.br ? + opcode - ILOpcode.br + ILOpcode.br_s: + opcode; + + bool branchTaken; + if (normalizedOpcode == ILOpcode.brtrue_s || normalizedOpcode == ILOpcode.brfalse_s) + { + StackEntry condition = stack.Pop(); + if (condition.ValueKind == StackValueKind.Int32 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 4)) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value.AsInt32() == 0 : condition.Value.AsInt32() != 0; + else if (condition.ValueKind == StackValueKind.Int64 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 8)) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value.AsInt64() == 0 : condition.Value.AsInt64() != 0; + else if (condition.ValueKind == StackValueKind.ObjRef) + branchTaken = normalizedOpcode == ILOpcode.brfalse_s + ? condition.Value == null : condition.Value != null; + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (normalizedOpcode == ILOpcode.blt_s || normalizedOpcode == ILOpcode.bgt_s + || normalizedOpcode == ILOpcode.bge_s || normalizedOpcode == ILOpcode.beq_s + || normalizedOpcode == ILOpcode.ble_s || normalizedOpcode == ILOpcode.blt_un_s + || normalizedOpcode == ILOpcode.ble_un_s || normalizedOpcode == ILOpcode.bge_un_s + || normalizedOpcode == ILOpcode.bgt_un_s || normalizedOpcode == ILOpcode.bne_un_s) + { + StackEntry value2 = stack.Pop(); + StackEntry value1 = stack.Pop(); + + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsInt32() < value2.Value.AsInt32(), + ILOpcode.blt_un_s => (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32(), + ILOpcode.bgt_s => value1.Value.AsInt32() > value2.Value.AsInt32(), + ILOpcode.bgt_un_s => (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32(), + ILOpcode.bge_s => value1.Value.AsInt32() >= value2.Value.AsInt32(), + ILOpcode.bge_un_s => (uint)value1.Value.AsInt32() >= (uint)value2.Value.AsInt32(), + ILOpcode.beq_s => value1.Value.AsInt32() == value2.Value.AsInt32(), + ILOpcode.bne_un_s => value1.Value.AsInt32() != value2.Value.AsInt32(), + ILOpcode.ble_s => value1.Value.AsInt32() <= value2.Value.AsInt32(), + ILOpcode.ble_un_s => (uint)value1.Value.AsInt32() <= (uint)value2.Value.AsInt32(), + _ => throw new NotImplementedException() // unreachable + }; + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsInt64() < value2.Value.AsInt64(), + ILOpcode.blt_un_s => (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64(), + ILOpcode.bgt_s => value1.Value.AsInt64() > value2.Value.AsInt64(), + ILOpcode.bgt_un_s => (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64(), + ILOpcode.bge_s => value1.Value.AsInt64() >= value2.Value.AsInt64(), + ILOpcode.bge_un_s => (ulong)value1.Value.AsInt64() >= (ulong)value2.Value.AsInt64(), + ILOpcode.beq_s => value1.Value.AsInt64() == value2.Value.AsInt64(), + ILOpcode.bne_un_s => value1.Value.AsInt64() != value2.Value.AsInt64(), + ILOpcode.ble_s => value1.Value.AsInt64() <= value2.Value.AsInt64(), + ILOpcode.ble_un_s => (ulong)value1.Value.AsInt64() <= (ulong)value2.Value.AsInt64(), + _ => throw new NotImplementedException() // unreachable + }; + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + branchTaken = normalizedOpcode switch + { + ILOpcode.blt_s => value1.Value.AsDouble() < value2.Value.AsDouble(), + ILOpcode.blt_un_s => !(value1.Value.AsDouble() >= value2.Value.AsDouble()), + ILOpcode.bgt_s => value1.Value.AsDouble() > value2.Value.AsDouble(), + ILOpcode.bgt_un_s => !(value1.Value.AsDouble() <= value2.Value.AsDouble()), + ILOpcode.bge_s => value1.Value.AsDouble() >= value2.Value.AsDouble(), + ILOpcode.bge_un_s => !(value1.Value.AsDouble() < value2.Value.AsDouble()), + ILOpcode.beq_s => value1.Value.AsDouble() == value2.Value.AsDouble(), + ILOpcode.bne_un_s => value1.Value.AsDouble() != value2.Value.AsDouble(), + ILOpcode.ble_s => value1.Value.AsDouble() <= value2.Value.AsDouble(), + ILOpcode.ble_un_s => !(value1.Value.AsDouble() > value2.Value.AsDouble()), + _ => throw new NotImplementedException() // unreachable + }; + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + else + { + Debug.Assert(normalizedOpcode == ILOpcode.br_s); + branchTaken = true; + } + + if (branchTaken) + { + // Don't allow backwards branches so that we don't have to worry about infinite loops + if (target < reader.Offset) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Backwards branch"); + } + + reader.Seek(target); + } + } + break; + + case ILOpcode.leave: + case ILOpcode.leave_s: + { + stack.Clear(); + + // We assume no finally regions (would have to run them here) + // This is validated before, but we're being paranoid. + foreach (ILExceptionRegion ehRegion in ehRegions) + { + Debug.Assert(ehRegion.Kind != ILExceptionRegionKind.Finally); + } + + int delta = opcode == ILOpcode.leave ? + (int)reader.ReadILUInt32() : + (sbyte)reader.ReadILByte(); + int target = reader.Offset + delta; + if (target < 0 + || target > reader.Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + reader.Seek(target); + } + break; + + case ILOpcode.clt: + case ILOpcode.clt_un: + case ILOpcode.cgt: + case ILOpcode.cgt_un: + { + StackEntry value1 = stack.Pop(); + StackEntry value2 = stack.Pop(); + + bool condition; + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsInt32() < value2.Value.AsInt32(); + else if (opcode == ILOpcode.cgt_un) + condition = (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32(); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsInt32() > value2.Value.AsInt32(); + else if (opcode == ILOpcode.clt_un) + condition = (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32(); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsInt64() < value2.Value.AsInt64(); + else if (opcode == ILOpcode.cgt_un) + condition = (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64(); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsInt64() > value2.Value.AsInt64(); + else if (opcode == ILOpcode.clt_un) + condition = (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64(); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + if (opcode == ILOpcode.cgt) + condition = value1.Value.AsDouble() < value2.Value.AsDouble(); + else if (opcode == ILOpcode.cgt_un) + condition = !(value1.Value.AsDouble() >= value2.Value.AsDouble()); + else if (opcode == ILOpcode.clt) + condition = value1.Value.AsDouble() > value2.Value.AsDouble(); + else if (opcode == ILOpcode.clt_un) + condition = !(value1.Value.AsDouble() <= value2.Value.AsDouble()); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else if (value1.ValueKind == StackValueKind.ObjRef && value2.ValueKind == StackValueKind.ObjRef) + { + if (opcode == ILOpcode.cgt_un) + condition = value1.Value == null && value2.Value != null; + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + stack.Push(StackValueKind.Int32, condition + ? ValueTypeValue.FromInt32(1) + : ValueTypeValue.FromInt32(0)); + } + break; + + case ILOpcode.ceq: + { + StackEntry value1 = stack.Pop(); + StackEntry value2 = stack.Pop(); + + if (value1.ValueKind == value2.ValueKind) + { + stack.Push(StackValueKind.Int32, + Value.Equals(value1.Value, value2.Value) + ? ValueTypeValue.FromInt32(1) + : ValueTypeValue.FromInt32(0)); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.neg: + { + StackEntry value = stack.Pop(); + if (value.ValueKind == StackValueKind.Int32) + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(-value.Value.AsInt32())); + else + return Status.Fail(methodIL.OwningMethod, opcode); + } + break; + + case ILOpcode.or: + case ILOpcode.shl: + case ILOpcode.add: + case ILOpcode.sub: + case ILOpcode.mul: + case ILOpcode.and: + case ILOpcode.div: + case ILOpcode.rem: + { + bool isDivRem = opcode == ILOpcode.div || opcode == ILOpcode.rem; + + StackEntry value2 = stack.Pop(); + StackEntry value1 = stack.Pop(); + if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32) + { + if (isDivRem && value2.Value.AsInt32() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + int result = opcode switch + { + ILOpcode.or => value1.Value.AsInt32() | value2.Value.AsInt32(), + ILOpcode.shl => value1.Value.AsInt32() << value2.Value.AsInt32(), + ILOpcode.add => value1.Value.AsInt32() + value2.Value.AsInt32(), + ILOpcode.sub => value1.Value.AsInt32() - value2.Value.AsInt32(), + ILOpcode.and => value1.Value.AsInt32() & value2.Value.AsInt32(), + ILOpcode.mul => value1.Value.AsInt32() * value2.Value.AsInt32(), + ILOpcode.div => value1.Value.AsInt32() / value2.Value.AsInt32(), + ILOpcode.rem => value1.Value.AsInt32() % value2.Value.AsInt32(), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(result)); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64) + { + if (isDivRem && value2.Value.AsInt64() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + long result = opcode switch + { + ILOpcode.or => value1.Value.AsInt64() | value2.Value.AsInt64(), + ILOpcode.add => value1.Value.AsInt64() + value2.Value.AsInt64(), + ILOpcode.sub => value1.Value.AsInt64() - value2.Value.AsInt64(), + ILOpcode.and => value1.Value.AsInt64() & value2.Value.AsInt64(), + ILOpcode.mul => value1.Value.AsInt64() * value2.Value.AsInt64(), + ILOpcode.div => value1.Value.AsInt64() / value2.Value.AsInt64(), + ILOpcode.rem => value1.Value.AsInt64() % value2.Value.AsInt64(), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result)); + } + else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float) + { + if (isDivRem && value2.Value.AsDouble() == 0) + return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero"); + + if (opcode == ILOpcode.or || opcode == ILOpcode.shl || opcode == ILOpcode.and) + ThrowHelper.ThrowInvalidProgramException(); + + double result = opcode switch + { + ILOpcode.add => value1.Value.AsDouble() + value2.Value.AsDouble(), + ILOpcode.sub => value1.Value.AsDouble() - value2.Value.AsDouble(), + ILOpcode.mul => value1.Value.AsDouble() * value2.Value.AsDouble(), + ILOpcode.div => value1.Value.AsDouble() / value2.Value.AsDouble(), + ILOpcode.rem => value1.Value.AsDouble() % value2.Value.AsDouble(), + _ => throw new NotImplementedException(), // unreachable + }; + + stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(result)); + } + else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int32 + && opcode == ILOpcode.shl) + { + long result = value1.Value.AsInt64() << value2.Value.AsInt32(); + stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result)); + } + else + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + } + break; + + case ILOpcode.ldlen: + { + StackEntry popped = stack.Pop(); + if (popped.Value is ArrayInstance arrayInstance) + { + stack.Push(StackValueKind.NativeInt, ValueTypeValue.FromInt64(arrayInstance.Length)); + } + else if (popped.Value == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Null array"); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + case ILOpcode.stelem: + case ILOpcode.stelem_i: + case ILOpcode.stelem_i1: + case ILOpcode.stelem_i2: + case ILOpcode.stelem_i4: + case ILOpcode.stelem_i8: + case ILOpcode.stelem_r4: + case ILOpcode.stelem_r8: + { + TypeDesc elementType = opcode switch + { + ILOpcode.stelem_i => context.GetWellKnownType(WellKnownType.IntPtr), + ILOpcode.stelem_i1 => context.GetWellKnownType(WellKnownType.SByte), + ILOpcode.stelem_i2 => context.GetWellKnownType(WellKnownType.Int16), + ILOpcode.stelem_i4 => context.GetWellKnownType(WellKnownType.Int32), + ILOpcode.stelem_i8 => context.GetWellKnownType(WellKnownType.Int64), + ILOpcode.stelem_r4 => context.GetWellKnownType(WellKnownType.Single), + ILOpcode.stelem_r8 => context.GetWellKnownType(WellKnownType.Double), + _ => (TypeDesc)methodIL.GetObject(reader.ReadILToken()), + }; + + if (elementType.IsGCPointer) + { + return Status.Fail(methodIL.OwningMethod, opcode); + } + + Value value = stack.PopIntoLocation(elementType); + if (!stack.TryPopIntValue(out int index)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + StackEntry array = stack.Pop(); + if (array.Value is ArrayInstance arrayInstance) + { + if (!arrayInstance.TryStoreElement(index, value)) + return Status.Fail(methodIL.OwningMethod, opcode, "Out of range access"); + } + else if (array.Value == null) + { + return Status.Fail(methodIL.OwningMethod, opcode, "Null array"); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + case ILOpcode.box: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + if (type.IsValueType) + { + if (type.IsNullable) + return Status.Fail(methodIL.OwningMethod, opcode); + + Value value = stack.PopIntoLocation(type); + stack.Push(StackValueKind.ObjRef, ObjectInstance.Box((DefType)type, ((ValueTypeValue)value).InstanceBytes)); + } + } + break; + + case ILOpcode.unbox_any: + { + TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + StackEntry entry = stack.Pop(); + if (entry.Value is ObjectInstance objInst + && objInst.TryUnboxAny(type, out Value unboxed)) + { + stack.PushFromLocation(type, unboxed); + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + } + } + break; + + default: + return Status.Fail(methodIL.OwningMethod, opcode); + } + + } + + return Status.Fail(methodIL.OwningMethod, "Control fell through"); + } + + private static Value NewUninitializedLocationValue(TypeDesc locationType) + { + if (locationType.IsGCPointer || locationType.IsByRef) + { + return null; + } + else + { + Debug.Assert(locationType.IsValueType || locationType.IsPointer || locationType.IsFunctionPointer); + return new ValueTypeValue(locationType); + } + } + + private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out Value retVal) + { + retVal = default; + + switch (method.Name) + { + case "InitializeArray": + if (method.OwningType is MetadataType mdType + && mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices" + && mdType.Module == mdType.Context.SystemModule + && parameters[0] is ArrayInstance array + && parameters[1] is RuntimeFieldHandleValue fieldHandle + && fieldHandle.Field.IsStatic && fieldHandle.Field.HasRva + && fieldHandle.Field is Internal.TypeSystem.Ecma.EcmaField ecmaField) + { + byte[] rvaData = Internal.TypeSystem.Ecma.EcmaFieldExtensions.GetFieldRvaData(ecmaField); + return array.TryInitialize(rvaData); + } + return false; + } + + return false; + } + + private TypeDesc GetArgType(MethodDesc method, int index) + { + var sig = method.Signature; + int offset = 0; + if (!sig.IsStatic) + { + if (index == 0) + return method.OwningType.IsValueType ? method.OwningType.MakeByRefType() : method.OwningType; + offset = 1; + } + + if ((uint)(index - offset) >= (uint)sig.Length) + ThrowHelper.ThrowInvalidProgramException(); + + return sig[index - offset]; + } + + class Stack : Stack + { + private readonly TargetDetails _target; + + public Stack(int capacity, TargetDetails target) : base(capacity) + { + _target = target; + } + + public new StackEntry Pop() + { + if (Count < 1) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return base.Pop(); + } + + public bool TryPopIntValue(out int value) + { + if (Count == 0) + { + value = 0; + return false; + } + + StackEntry entry = Pop(); + if (entry.ValueKind == StackValueKind.Int32) + { + value = entry.Value.AsInt32(); + return true; + } + else if (entry.ValueKind == StackValueKind.NativeInt) + { + if (_target.PointerSize == 8) + { + long longValue = entry.Value.AsInt64(); + if (longValue < int.MinValue || longValue > int.MaxValue) + { + value = 0; + return false; + } + value = (int)longValue; + return true; + } + + value = entry.Value.AsInt32(); + return true; + } + + value = 0; + return false; + } + + public void Push(StackValueKind kind, Value val) + { + Push(new StackEntry(kind, val)); + } + + public void Push(ReferenceTypeValue value) + { + Push(StackValueKind.ObjRef, value); + } + + public void Push(ByRefValue value) + { + Push(StackValueKind.ByRef, value); + } + + public void PushFromLocation(TypeDesc locationType, Value value) + { + switch (locationType.UnderlyingType.Category) + { + case TypeFlags.Boolean: + case TypeFlags.Byte: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32((byte)value.AsSByte())); break; + case TypeFlags.Char: + case TypeFlags.UInt16: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)value.AsInt16())); break; + case TypeFlags.SByte: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsSByte())); break; + case TypeFlags.Int16: + Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsInt16())); break; + case TypeFlags.Int32: + case TypeFlags.UInt32: + Push(StackValueKind.Int32, value); break; + case TypeFlags.Int64: + case TypeFlags.UInt64: + Push(StackValueKind.Int64, value); break; + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + Push(StackValueKind.NativeInt, value); break; + case TypeFlags.Single: + Push(StackValueKind.Float, ValueTypeValue.FromDouble(value.AsSingle())); break; + case TypeFlags.Double: + Push(StackValueKind.Float, value); break; + case TypeFlags.ValueType: + case TypeFlags.Nullable: + Push(StackValueKind.ValueType, value); break; + case TypeFlags.Class: + case TypeFlags.Interface: + case TypeFlags.Array: + case TypeFlags.SzArray: + Push(StackValueKind.ObjRef, value); break; + case TypeFlags.ByRef: + Push(StackValueKind.ByRef, value); break; + default: + throw new NotImplementedException(); + } + } + + public Value PopIntoLocation(TypeDesc locationType) + { + if (Count == 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + locationType = locationType.UnderlyingType; + + StackEntry popped = Pop(); + + switch (popped.ValueKind) + { + case StackValueKind.Int64: + if (!locationType.IsWellKnownType(WellKnownType.Int64) + && !locationType.IsWellKnownType(WellKnownType.UInt64)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.Int32: + if (!locationType.IsWellKnownType(WellKnownType.Int32) + && !locationType.IsWellKnownType(WellKnownType.UInt32)) + { + int value = popped.Value.AsInt32(); + switch (locationType.Category) + { + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Boolean: + return ValueTypeValue.FromSByte((sbyte)value); + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return ValueTypeValue.FromInt16((short)value); + // case TypeFlags.IntPtr: sign extend + // case TypeFlags.UIntPtr: zero extend + } + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.NativeInt: + // If it's none of the natural pointer types, we might need to truncate. + if (!locationType.IsPointer + && !locationType.IsFunctionPointer + && !locationType.IsWellKnownType(WellKnownType.IntPtr) + && !locationType.IsWellKnownType(WellKnownType.UIntPtr)) + { + long value = _target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32(); + switch (locationType.Category) + { + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Boolean: + return ValueTypeValue.FromSByte((sbyte)value); + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return ValueTypeValue.FromInt16((short)value); + case TypeFlags.Int32: + case TypeFlags.UInt32: + return ValueTypeValue.FromInt32((int)value); + // case TypeFlags.ByRef: start GC tracking + } + + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.Float: + if (locationType.IsWellKnownType(WellKnownType.Double)) + { + return popped.Value; + } + else if (!locationType.IsWellKnownType(WellKnownType.Single)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return ValueTypeValue.FromSingle((float)popped.Value.AsDouble()); + + case StackValueKind.ByRef: + if (!locationType.IsByRef) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.ObjRef: + if (!locationType.IsGCPointer) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + case StackValueKind.ValueType: + if (!locationType.IsValueType + || ((BaseValueTypeValue)popped.Value).Size != ((DefType)locationType).InstanceFieldSize.AsInt) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return popped.Value; + + default: + throw new NotImplementedException(); + } + } + } + + private enum StackValueKind + { + Unknown, + Int32, + Int64, + NativeInt, + Float, + ByRef, + ObjRef, + ValueType, + } + + /// + /// Represents a field value that can be serialized into a preinitialized blob. + /// + public interface ISerializableValue + { + void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory); + } + + /// + /// Represents a frozen object whose contents can be serialized into the executable. + /// + public interface ISerializableReference : ISerializableValue + { + void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory); + } + + /// + /// Represents a value with instance fields. This is either a reference type, or a byref to + /// a valuetype. + /// + private interface IHasInstanceFields + { + void SetField(FieldDesc field, Value value); + Value GetField(FieldDesc field); + ByRefValue GetFieldAddress(FieldDesc field); + } + + /// + /// Represents a value that can be assigned into. + /// + private interface IAssignableValue + { + void Assign(Value value); + } + + private abstract class Value : ISerializableValue + { + public abstract bool Equals(Value value); + + public static bool Equals(Value value1, Value value2) + { + if (value1 == value2) + { + return true; + } + if (value1 == null || value2 == null) + { + return false; + } + return value1.Equals(value2); + } + + public abstract void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory); + + private T ThrowInvalidProgram() + { + ThrowHelper.ThrowInvalidProgramException(); + return default; + } + + public virtual sbyte AsSByte() => ThrowInvalidProgram(); + public virtual short AsInt16() => ThrowInvalidProgram(); + public virtual int AsInt32() => ThrowInvalidProgram(); + public virtual long AsInt64() => ThrowInvalidProgram(); + public virtual float AsSingle() => ThrowInvalidProgram(); + public virtual double AsDouble() => ThrowInvalidProgram(); + } + + private abstract class BaseValueTypeValue : Value + { + public abstract int Size { get; } + } + + // Also represents pointers and function pointer. + private class ValueTypeValue : BaseValueTypeValue, IAssignableValue + { + public readonly byte[] InstanceBytes; + + public override int Size => InstanceBytes.Length; + + public ValueTypeValue(TypeDesc type) + { + Debug.Assert(type.IsValueType || type.IsPointer || type.IsFunctionPointer); + InstanceBytes = new byte[type.GetElementSize().AsInt]; + } + + private ValueTypeValue(byte[] bytes) + { + InstanceBytes = bytes; + } + + public ByRefValue CreateByRef() + { + + return new ByRefValue(InstanceBytes, 0); + } + + void IAssignableValue.Assign(Value value) + { + if (!(value is ValueTypeValue other) + || other.Size != Size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + Array.Copy(((ValueTypeValue)value).InstanceBytes, InstanceBytes, InstanceBytes.Length); + } + + public override bool Equals(Value value) + { + if (!(value is ValueTypeValue vtvalue) + || vtvalue.InstanceBytes.Length != InstanceBytes.Length) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + for (int i = 0; i < InstanceBytes.Length; i++) + { + if (InstanceBytes[i] != ((ValueTypeValue)value).InstanceBytes[i]) + return false; + } + + return true; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + Debug.Assert(field.FieldType.GetElementSize().AsInt == InstanceBytes.Length); + builder.EmitBytes(InstanceBytes); + } + + private byte[] AsExactByteCount(int size) + { + if (InstanceBytes.Length != size) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return InstanceBytes; + } + + public override sbyte AsSByte() => (sbyte)AsExactByteCount(1)[0]; + public override short AsInt16() => BitConverter.ToInt16(AsExactByteCount(2), 0); + public override int AsInt32() => BitConverter.ToInt32(AsExactByteCount(4), 0); + public override long AsInt64() => BitConverter.ToInt64(AsExactByteCount(8), 0); + public override float AsSingle() => BitConverter.ToSingle(AsExactByteCount(4), 0); + public override double AsDouble() => BitConverter.ToDouble(AsExactByteCount(8), 0); + public static ValueTypeValue FromSByte(sbyte value) => new ValueTypeValue(new byte[1] { (byte)value }); + public static ValueTypeValue FromInt16(short value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromInt32(int value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromInt64(long value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromSingle(float value) => new ValueTypeValue(BitConverter.GetBytes(value)); + public static ValueTypeValue FromDouble(double value) => new ValueTypeValue(BitConverter.GetBytes(value)); + } + + private class RuntimeFieldHandleValue : BaseValueTypeValue + { + public FieldDesc Field { get; private set; } + + public RuntimeFieldHandleValue(FieldDesc field) + { + Field = field; + } + + public override int Size => Field.Context.Target.PointerSize; + + public override bool Equals(Value value) + { + if (!(value is RuntimeFieldHandleValue)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return Field == ((RuntimeFieldHandleValue)value).Field; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + Debug.Assert(field.FieldType.IsWellKnownType(WellKnownType.RuntimeFieldHandle)); + builder.EmitPointerReloc(factory.RuntimeFieldHandle(Field)); + } + } + + private class ByRefValue : Value, IHasInstanceFields + { + public readonly byte[] PointedToBytes; + public readonly int PointedToOffset; + + public ByRefValue(byte[] pointedToBytes, int pointedToOffset) + { + PointedToBytes = pointedToBytes; + PointedToOffset = pointedToOffset; + } + + public override bool Equals(Value value) + { + if (!(value is ByRefValue)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return PointedToBytes == ((ByRefValue)value).PointedToBytes + && PointedToOffset == ((ByRefValue)value).PointedToOffset; + } + + Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetField(field); + void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(PointedToBytes, PointedToOffset).SetField(field, value); + ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetFieldAddress(field); + + public void Initialize(int size) + { + if ((uint)size > (uint)(PointedToBytes.Length - PointedToOffset)) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + for (int i = PointedToOffset; i < PointedToOffset + size; i++) + { + PointedToBytes[i] = 0; + } + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + // This would imply we have a byref-typed static field. The layout algorithm should have blocked this. + throw new NotImplementedException(); + } + } + + private abstract class ReferenceTypeValue : Value, ISerializableReference + { + protected readonly TypeDesc _type; + + protected ReferenceTypeValue(TypeDesc type) { _type = type; } + + public override bool Equals(Value value) + { + return this == value; + } + + public abstract void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory); + } + + private class ArrayInstance : ReferenceTypeValue + { + private readonly int _elementCount; + private readonly int _elementSize; + private readonly byte[] _data; + + public ArrayInstance(ArrayType type, int elementCount) + : base(type) + { + _elementCount = elementCount; + _elementSize = type.ElementType.GetElementSize().AsInt; + _data = new byte[elementCount * _elementSize]; + } + + public bool TryInitialize(byte[] bytes) + { + if (bytes.Length != _data.Length) + return false; + + Array.Copy(bytes, _data, bytes.Length); + return true; + } + + public int Length + { + get + { + return _elementCount; + } + } + + public bool TryStoreElement(int index, Value value) + { + Debug.Assert(value is ValueTypeValue); + + if ((uint)index > (uint)Length) + return false; + + var valueToStore = value as ValueTypeValue; + Debug.Assert(valueToStore.InstanceBytes.Length == _elementSize); + Array.Copy(valueToStore.InstanceBytes, 0, _data, index * _elementSize, valueToStore.InstanceBytes.Length); + return true; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this)); + } + + public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory) + { + // EEType + var node = factory.ConstructedTypeSymbol(_type); + Debug.Assert(!node.RepresentsIndirectionCell); // Arrays are always local + builder.EmitPointerReloc(node); + + // numComponents + builder.EmitInt(_elementCount); + + int pointerSize = _type.Context.Target.PointerSize; + Debug.Assert(pointerSize == 8 || pointerSize == 4); + + if (pointerSize == 8) + { + // padding numComponents in 64-bit + builder.EmitInt(0); + } + + builder.EmitBytes(_data); + } + } + + private class StringInstance : ReferenceTypeValue + { + private readonly string _value; + + public StringInstance(TypeDesc stringType, string value) + : base(stringType) + { + _value = value; + } + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedStringObject(_value)); + } + + public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory) + { + // Not actually used by SerializedStringObject. + throw new NotImplementedException(); + } + } + + private class ObjectInstance : ReferenceTypeValue, IHasInstanceFields + { + private readonly byte[] _data; + + public ObjectInstance(DefType type) + : base(type) + { + int size = type.InstanceByteCount.AsInt; + if (type.IsValueType) + size += type.Context.Target.PointerSize; + _data = new byte[size]; + } + + public static ObjectInstance Box(DefType type, byte[] data) + { + var inst = new ObjectInstance(type); + Array.Copy(data, 0, inst._data, type.Context.Target.PointerSize, data.Length); + return inst; + } + + public bool TryUnboxAny(TypeDesc type, out Value value) + { + value = null; + + if (!type.IsValueType || type.IsNullable) + return false; + + if (_type.UnderlyingType != type.UnderlyingType) + return false; + + var result = new ValueTypeValue(type); + Array.Copy(_data, type.Context.Target.PointerSize, result.InstanceBytes, 0, result.InstanceBytes.Length); + value = result; + return true; + } + + Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(_data).GetField(field); + void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(_data).SetField(field, value); + ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(_data).GetFieldAddress(field); + + public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory) + { + builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this)); + } + + public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory) + { + // EEType + var node = factory.ConstructedTypeSymbol(_type); + Debug.Assert(!node.RepresentsIndirectionCell); // Shouldn't have allowed preinitializing this + builder.EmitPointerReloc(node); + + // We skip the first pointer because that's the EEType pointer + // we just initialized above. + int pointerSize = factory.Target.PointerSize; + builder.EmitBytes(_data, pointerSize, _data.Length - pointerSize); + } + } + + private struct FieldAccessor + { + private readonly byte[] _instanceBytes; + private readonly int _offset; + + public FieldAccessor(byte[] bytes, int offset = 0) + { + _instanceBytes = bytes; + _offset = 0; + } + + public Value GetField(FieldDesc field) + { + Debug.Assert(!field.IsStatic); + Debug.Assert(!field.FieldType.IsGCPointer); + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + var result = new ValueTypeValue(field.FieldType); + Array.Copy(_instanceBytes, _offset + fieldOffset, result.InstanceBytes, 0, fieldSize); + return result; + } + + public void SetField(FieldDesc field, Value value) + { + Debug.Assert(!field.IsStatic); + Debug.Assert(!field.FieldType.IsGCPointer); + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + Array.Copy(((ValueTypeValue)value).InstanceBytes, 0, _instanceBytes, _offset + fieldOffset, fieldSize); + } + + public ByRefValue GetFieldAddress(FieldDesc field) + { + Debug.Assert(!field.IsStatic); + Debug.Assert(!field.FieldType.IsGCPointer); + int fieldOffset = field.Offset.AsInt; + int fieldSize = field.FieldType.GetElementSize().AsInt; + if (fieldOffset + fieldSize > _instanceBytes.Length - _offset) + ThrowHelper.ThrowInvalidProgramException(); + + return new ByRefValue(_instanceBytes, _offset + fieldOffset); + } + } + + private struct StackEntry + { + public readonly StackValueKind ValueKind; + public readonly Value Value; + + public StackEntry(StackValueKind valueKind, Value value) + { + ValueKind = valueKind; + Value = value; + } + } + + private struct Status + { + public string FailureReason { get; } + + public static Status Success => default; + + public bool IsSuccessful => FailureReason == null; + + private Status(string message) + { + FailureReason = message; + } + + public static Status Fail(MethodDesc method, ILOpcode opcode, string detail = null) + { + return new Status($"Method '{method}', opcode '{opcode}' {detail ?? ""}"); + } + + public static Status Fail(MethodDesc method, string detail) + { + return new Status($"Method '{method}': {detail}"); + } + } + + public class PreinitializationInfo + { + private readonly Dictionary _fieldValues; + + public MetadataType Type { get; } + + public string FailureReason { get; } + + public bool IsPreinitialized => _fieldValues != null; + + public PreinitializationInfo(MetadataType type, IEnumerable> fieldValues) + { + Type = type; + _fieldValues = new Dictionary(); + foreach (var field in fieldValues) + _fieldValues.Add(field.Key, field.Value); + } + + public PreinitializationInfo(MetadataType type, string failureReason) + { + Type = type; + FailureReason = failureReason; + } + + public ISerializableValue GetFieldValue(FieldDesc field) + { + Debug.Assert(IsPreinitialized); + Debug.Assert(field.OwningType == Type); + Debug.Assert(field.IsStatic && !field.HasRva && !field.IsThreadStatic && !field.IsLiteral); + return _fieldValues[field]; + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 7a753288572..b17fa10ee9f 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -168,8 +168,7 @@ - - + @@ -357,6 +356,7 @@ + diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index d205c313eef..ec4659ce035 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -49,6 +49,8 @@ internal class Program private string _exportsFile; private bool _useScanner; private bool _noScanner; + private bool _preinitStatics; + private bool _noPreinitStatics; private bool _emitStackTraceData; private string _mapFileName; private string _metadataLogFileName; @@ -190,6 +192,8 @@ private ArgumentSyntax ParseCommandLine(string[] args) syntax.DefineOptionList("removefeature", ref _removedFeatures, "Framework features to remove"); syntax.DefineOption("singlethreaded", ref _singleThreaded, "Run compilation on a single thread"); syntax.DefineOption("instructionset", ref _instructionSet, "Instruction set to allow or disallow"); + syntax.DefineOption("preinitstatics", ref _preinitStatics, "Interpret static constructors at compile time if possible (implied by -O)"); + syntax.DefineOption("nopreinitstatics", ref _noPreinitStatics, "Do not interpret static constructors at compile time"); syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation"); syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation"); @@ -648,7 +652,15 @@ private int Run(string[] args) useScanner &= !_noScanner; - builder.UseILProvider(ilProvider); + // Enable static data preinitialization in optimized builds. + bool preinitStatics = _preinitStatics || + (_optimizationMode != OptimizationMode.None && !_isCppCodegen && !_multiFile); + preinitStatics &= !_noPreinitStatics; + + var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); + builder + .UseILProvider(ilProvider) + .UsePreinitializationManager(preinitManager); ILScanResults scanResults = null; if (useScanner) @@ -774,6 +786,8 @@ private int Run(string[] args) if (debugInfoProvider is IDisposable) ((IDisposable)debugInfoProvider).Dispose(); + preinitManager.LogStatistics(logger); + return 0; } diff --git a/tests/src/Simple/PreInitData/PreInitData.cs b/tests/src/Simple/PreInitData/PreInitData.cs deleted file mode 100644 index e1585efe2bf..00000000000 --- a/tests/src/Simple/PreInitData/PreInitData.cs +++ /dev/null @@ -1,264 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using Internal.Runtime.CompilerServices; - -#region Place holder types for internal System.Private.CoreLib types - -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] - class PreInitializedAttribute: Attribute - { - } - - [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] - class InitDataBlobAttribute: Attribute - { - public InitDataBlobAttribute(Type type, string fieldName) - { - - } - } - - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - class TypeHandleFixupAttribute: Attribute - { - public TypeHandleFixupAttribute(int offset, Type fixupType) - { - } - } - - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] - class MethodAddrFixupAttribute: Attribute - { - public MethodAddrFixupAttribute(int offset, Type fixupType, string methodName) - { - } - } -} - - -namespace System.Runtime.InteropServices -{ - [AttributeUsage(AttributeTargets.Method)] - public sealed class UnmanagedCallersOnlyAttribute : Attribute - { - public string EntryPoint; - - public CallingConvention CallingConvention; - - public UnmanagedCallersOnlyAttribute() - { - } - } -} - -#endregion - -namespace System.Runtime.InteropServices -{ - [AttributeUsage((System.AttributeTargets.Method | System.AttributeTargets.Class))] - internal class McgIntrinsicsAttribute : Attribute - { - } - - [McgIntrinsics] - internal static class AddrofIntrinsics - { - // This method is implemented elsewhere in the toolchain - internal static IntPtr AddrOf(T ftn) { throw new PlatformNotSupportedException(); } - } -} - -class Details -{ - private static IntPtr PreInitializedInt32Field_DataBlob; - - [TypeHandleFixupAttribute(0, typeof(IntPtr))] - private static IntPtr PreInitializedIntField_DataBlob; - -#if TARGET_64BIT - [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))] - [TypeHandleFixupAttribute(16, typeof(int))] - [TypeHandleFixupAttribute(24, typeof(short))] - [TypeHandleFixupAttribute(32, typeof(long))] - [TypeHandleFixupAttribute(40, typeof(string))] -#else - [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))] - [TypeHandleFixupAttribute(8, typeof(int))] - [TypeHandleFixupAttribute(12, typeof(short))] - [TypeHandleFixupAttribute(16, typeof(long))] - [TypeHandleFixupAttribute(20, typeof(string))] -#endif - private static IntPtr PreInitializedTypeField_DataBlob; - -#if TARGET_64BIT - [TypeHandleFixupAttribute(0, typeof(IntPtr))] - [MethodAddrFixupAttribute(16, typeof(NativeMethods), "Func1")] - [MethodAddrFixupAttribute(24, typeof(NativeMethods), "Func2")] -#else - [TypeHandleFixupAttribute(0, typeof(IntPtr))] - [MethodAddrFixupAttribute(8, typeof(NativeMethods), "Func1")] - [MethodAddrFixupAttribute(12, typeof(NativeMethods), "Func2")] -#endif - private static IntPtr PreInitializedMethodTypeField_DataBlob; -} - -static class NativeMethods -{ - [UnmanagedCallersOnly] - internal static void Func1(int a) - { - } - - [UnmanagedCallersOnly] - internal static void Func2(float b) - { - } -} - -class PreInitData -{ - internal static string StaticStringFieldBefore = "BEFORE"; - - // - // Reference type fields - // - [PreInitialized] - [InitDataBlob(typeof(Details), "PreInitializedIntField_DataBlob")] - internal static int[] PreInitializedIntField; - - [PreInitialized] - [InitDataBlob(typeof(Details), "PreInitializedTypeField_DataBlob")] - internal static RuntimeTypeHandle[] PreInitializedTypeField; - - [PreInitialized] - [InitDataBlob(typeof(Details), "PreInitializedMethodField_DataBlob")] - internal static IntPtr[] PreInitializedMethodField; - - // - // Primitive type fields - // - [PreInitialized] - [InitDataBlob(typeof(Details), "PreInitializedInt32Field_DataBlob")] - internal static int PreInitializedInt32Field; // = 0x12345678 - - internal static string StaticStringFieldAfter = "AFTER"; -} - -public class PreInitDataTest -{ - const int Pass = 100; - const int Fail = -1; - - public static int Main(string[] args) - { - int result = Pass; - - if (!TestPreInitPrimitiveData()) - { - Console.WriteLine("Failed"); - result = Fail; - } - - if (!TestPreInitIntData()) - { - Console.WriteLine("Failed"); - result = Fail; - } - - if (!TestPreInitTypeData()) - { - Console.WriteLine("Failed"); - result = Fail; - } - - if (!TestPreInitMethodData()) - { - Console.WriteLine("Failed"); - result = Fail; - } - - // Make sure PreInitializedField works with other statics - if (!TestOtherStatics()) - { - Console.WriteLine("Failed"); - result = Fail; - } - - return result; - } - - static bool TestPreInitPrimitiveData() - { - Console.WriteLine("Testing preinitialized primitive data..."); - - if (PreInitData.PreInitializedInt32Field != 0x12345678) - return false; - - return true; - } - - static bool TestPreInitIntData() - { - Console.WriteLine("Testing preinitialized int array..."); - - for (int i = 0; i < PreInitData.PreInitializedIntField.Length; ++i) - { - if (PreInitData.PreInitializedIntField[i] != i + 1) - return false; - } - - return true; - } - - static bool TestPreInitTypeData() - { - Console.WriteLine("Testing preinitialized type array..."); - - if (!PreInitData.PreInitializedTypeField[0].Equals(typeof(int).TypeHandle)) - return false; - if (!PreInitData.PreInitializedTypeField[1].Equals(typeof(short).TypeHandle)) - return false; - if (!PreInitData.PreInitializedTypeField[2].Equals(typeof(long).TypeHandle)) - return false; - if (!PreInitData.PreInitializedTypeField[3].Equals(typeof(string).TypeHandle)) - return false; - - return true; - } - - public delegate void Func1Proc(int a); - public delegate void Func2Proc(float a); - - static bool TestPreInitMethodData() - { - Console.WriteLine("Testing preinitialized method array..."); - - if (PreInitData.PreInitializedMethodField[0] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func1)) - return false; - - if (PreInitData.PreInitializedMethodField[1] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func2)) - return false; - - return true; - } - - static bool TestOtherStatics() - { - Console.WriteLine("Testing other statics work well with preinitialized data in the same type..."); - - if (PreInitData.StaticStringFieldBefore != "BEFORE") - return false; - - if (PreInitData.StaticStringFieldAfter != "AFTER") - return false; - - return true; - } -} diff --git a/tests/src/Simple/PreInitData/PreInitData.ilproj b/tests/src/Simple/PreInitData/PreInitData.ilproj deleted file mode 100644 index 6455ed8e37d..00000000000 --- a/tests/src/Simple/PreInitData/PreInitData.ilproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tests/src/Simple/PreInitData/PreInitData32.il b/tests/src/Simple/PreInitData/PreInitData32.il deleted file mode 100644 index 2885e60d32a..00000000000 --- a/tests/src/Simple/PreInitData/PreInitData32.il +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// ============================================================= -// PreInitialized data -// ============================================================= - -.class private auto ansi beforefieldinit Details - extends [mscorlib]System.Object -{ - .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32 - - .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedField_DataBlob at Data_IntArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) // 9.. - - .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedTypeField_DataBlob at Data_TypeArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 14 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St - 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib, - 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, - 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral - 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken - 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 - 39 00 00 ) // 9.. - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 0C 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 08 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru - 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle, - 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime, - 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0 - 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra - 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke - 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a - 33 61 00 00 ) // 3a.. - - .field private static valuetype ''/'__StaticArrayInitTypeSize=16' PreInitializedMethodField_DataBlob at Data_MethodArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In - 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib, - 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, - 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral - 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken - 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 - 39 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, - class [mscorlib]System.Type, - string) = ( 01 00 08 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet - 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1.. - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, - class [mscorlib]System.Type, - string) = ( 01 00 0C 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet - 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2.. -} // end of class Details - -.class public auto ansi beforefieldinit PreInitData -{ - .field private static string StaticStringFieldBefore - - //=================================================================== - // preinitialized fields - .field private static int32[] PreInitializedIntField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D - 61 74 61 42 6C 6F 62 00 00 ) // ataBlob.. - - .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie - 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob.. - - .field static assembly native int[] PreInitializedMethodField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF - 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob.. - - .field static assembly int32 PreInitializedInt32Field - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn - 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi - 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob.. - - //=================================================================== - - .field private static string StaticStringFieldAfter - - .method private hidebysig specialname rtspecialname static - void .cctor() cil managed - { - // Code size 21 (0x15) - .maxstack 8 - IL_0000: ldstr "BEFORE" - IL_0005: stsfld string PreInitData::StaticStringFieldBefore - IL_000a: ldstr "AFTER" - IL_000f: stsfld string PreInitData::StaticStringFieldAfter - IL_0014: ret - } // end of method PreInitData::.cctor -} // end of class PreInitData - -.class private auto ansi '' - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .class explicit ansi sealed nested private '__StaticDataSize=4' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 4 - } // end of class '__StaticDataSize=4' - .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=24' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 24 - } // end of class '__StaticArrayInitTypeSize=24' - .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=16' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 16 - } // end of class '__StaticArrayInitTypeSize=16' -} // end of class '' - -.class private abstract auto ansi sealed beforefieldinit NativeMethods - extends [mscorlib]System.Object -{ - .method assembly hidebysig static void - Func1(int32 a) cil managed - { - .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 2 (0x2) - .maxstack 8 - IL_0000: nop - IL_0001: ret - } // end of method NativeMethods::Func1 - - .method assembly hidebysig static void - Func2(float32 b) cil managed - { - .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 2 (0x2) - .maxstack 8 - IL_0000: nop - IL_0001: ret - } // end of method NativeMethods::Func2 - -} // end of class NativeMethods - -// ============================================================= -// Data blob -// ============================================================= - -.data cil Data_IntArray = bytearray ( 00 00 00 00 04 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00) -.data cil Data_TypeArray = bytearray ( 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) -.data cil Data_MethodArray = bytearray ( 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ) -.data cil Data_Int32 = bytearray ( 78 56 34 12 ) - diff --git a/tests/src/Simple/PreInitData/PreInitData64.il b/tests/src/Simple/PreInitData/PreInitData64.il deleted file mode 100644 index aabec11e749..00000000000 --- a/tests/src/Simple/PreInitData/PreInitData64.il +++ /dev/null @@ -1,192 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// ============================================================= -// PreInitialized data -// ============================================================= - -.class private auto ansi beforefieldinit Details - extends [mscorlib]System.Object -{ - .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32 - .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedField_DataBlob at Data_IntArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) // 9.. - - .field private static valuetype ''/'__StaticArrayInitTypeSize=48' PreInitializedTypeField_DataBlob at Data_TypeArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 28 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St - 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib, - 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, - 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral - 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken - 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 - 39 00 00 ) // 9.. - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 20 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 18 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In - 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V - 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, - 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, - 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= - 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 - 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru - 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle, - 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime, - 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0 - 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra - 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke - 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a - 33 61 00 00 ) // 3a.. - - .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedMethodField_DataBlob at Data_MethodArray - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, - class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In - 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib, - 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, - 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral - 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken - 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 - 39 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, - class [mscorlib]System.Type, - string) = ( 01 00 10 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet - 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1.. - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, - class [mscorlib]System.Type, - string) = ( 01 00 18 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet - 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2.. -} // end of class Details - -.class public auto ansi beforefieldinit PreInitData -{ - .field private static string StaticStringFieldBefore - - //=================================================================== - // preinitialized fields - .field private static int32[] PreInitializedIntField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D - 61 74 61 42 6C 6F 62 00 00 ) // ataBlob.. - - .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie - 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob.. - - .field static assembly native int[] PreInitializedMethodField - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF - 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob.. - - .field static assembly int32 PreInitializedInt32Field - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn - 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi - 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob.. - - //=================================================================== - - .field private static string StaticStringFieldAfter - - .method private hidebysig specialname rtspecialname static - void .cctor() cil managed - { - // Code size 21 (0x15) - .maxstack 8 - IL_0000: ldstr "BEFORE" - IL_0005: stsfld string PreInitData::StaticStringFieldBefore - IL_000a: ldstr "AFTER" - IL_000f: stsfld string PreInitData::StaticStringFieldAfter - IL_0014: ret - } // end of method PreInitData::.cctor -} // end of class PreInitData - -.class private auto ansi '' - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .class explicit ansi sealed nested private '__StaticDataSize=4' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 4 - } // end of class '__StaticDataSize=4' - .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=32' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 32 - } // end of class '__StaticArrayInitTypeSize=32' - .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=48' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 48 - } // end of class '__StaticArrayInitTypeSize=16' -} // end of class '' - -.class private abstract auto ansi sealed beforefieldinit NativeMethods - extends [mscorlib]System.Object -{ - .method assembly hidebysig static void - Func1(int32 a) cil managed - { - .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 2 (0x2) - .maxstack 8 - IL_0000: nop - IL_0001: ret - } // end of method NativeMethods::Func1 - - .method assembly hidebysig static void - Func2(float32 b) cil managed - { - .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 2 (0x2) - .maxstack 8 - IL_0000: nop - IL_0001: ret - } // end of method NativeMethods::Func2 - -} // end of class NativeMethods - -// ============================================================= -// Data blob -// ============================================================= - -.data cil Data_IntArray = bytearray ( 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00) -.data cil Data_TypeArray = bytearray ( 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) -.data cil Data_MethodArray = bytearray ( 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) -.data cil Data_Int32 = bytearray ( 78 56 34 12 ) diff --git a/tests/src/Simple/PreInitData/PreInitDataTest.il b/tests/src/Simple/PreInitData/PreInitDataTest.il deleted file mode 100644 index 1f0ad1ed979..00000000000 --- a/tests/src/Simple/PreInitData/PreInitDataTest.il +++ /dev/null @@ -1,508 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. - .ver 4:0:0:0 -} - -.assembly extern System.Private.CoreLib -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:0:0:0 -} - -.assembly PreInitData -{ - .hash algorithm 0x00008004 - .ver 0:0:0:0 -} - -.module PreInitData.exe -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000001 // ILONLY -// Image base: 0x024E0000 - -// ============================================================= -// Intrinsics -// ============================================================= - -.class private auto ansi beforefieldinit System.Runtime.InteropServices.McgIntrinsicsAttribute - extends [mscorlib]System.Attribute -{ - .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 44 00 00 00 00 00 ) // ..D..... - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method McgIntrinsicsAttribute::.ctor - -} // end of class System.Runtime.InteropServices.McgIntrinsicsAttribute - -.class private abstract auto ansi sealed beforefieldinit System.Runtime.InteropServices.AddrofIntrinsics - extends [mscorlib]System.Object -{ - .custom instance void System.Runtime.InteropServices.McgIntrinsicsAttribute::.ctor() = ( 01 00 00 00 ) - .method assembly hidebysig static native int - AddrOf(!!T ftn) cil managed - { - // Code size 7 (0x7) - .maxstack 8 - IL_0000: nop - IL_0001: newobj instance void [mscorlib]System.PlatformNotSupportedException::.ctor() - IL_0006: throw - } // end of method AddrofIntrinsics::AddrOf - -} // end of class System.Runtime.InteropServices.AddrofIntrinsics - -// ============================================================= -// Test code -// ============================================================= - -.class public auto ansi beforefieldinit PreInitDataTest - extends [mscorlib]System.Object -{ -.class auto ansi sealed nested public Func1Proc - extends [mscorlib]System.MulticastDelegate - { - .method public hidebysig specialname rtspecialname - instance void .ctor(object 'object', - native int 'method') runtime managed - { - } // end of method Func1Proc::.ctor - - .method public hidebysig newslot virtual - instance void Invoke(int32 a) runtime managed - { - } // end of method Func1Proc::Invoke - - .method public hidebysig newslot virtual - instance class [mscorlib]System.IAsyncResult - BeginInvoke(int32 a, - class [mscorlib]System.AsyncCallback callback, - object 'object') runtime managed - { - } // end of method Func1Proc::BeginInvoke - - .method public hidebysig newslot virtual - instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed - { - } // end of method Func1Proc::EndInvoke - - } // end of class Func1Proc - - .class auto ansi sealed nested public Func2Proc - extends [mscorlib]System.MulticastDelegate - { - .method public hidebysig specialname rtspecialname - instance void .ctor(object 'object', - native int 'method') runtime managed - { - } // end of method Func2Proc::.ctor - - .method public hidebysig newslot virtual - instance void Invoke(float32 a) runtime managed - { - } // end of method Func2Proc::Invoke - - .method public hidebysig newslot virtual - instance class [mscorlib]System.IAsyncResult - BeginInvoke(float32 a, - class [mscorlib]System.AsyncCallback callback, - object 'object') runtime managed - { - } // end of method Func2Proc::BeginInvoke - - .method public hidebysig newslot virtual - instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed - { - } // end of method Func2Proc::EndInvoke - - } // end of class Func2Proc - - .field private static literal int32 Pass = int32(0x00000064) - .field private static literal int32 Fail = int32(0xFFFFFFFF) - .method public hidebysig static int32 Main(string[] args) cil managed - { - .entrypoint - // Code size 151 (0x97) - .maxstack 2 - .locals init (int32 V_0, - bool V_1, - bool V_2, - bool V_3, - bool V_4, - bool V_5, - int32 V_6) - IL_0000: nop - IL_0001: ldc.i4.s 100 - IL_0003: stloc.0 - IL_0004: call bool PreInitDataTest::TestPreInitPrimitiveData() - IL_0009: ldc.i4.0 - IL_000a: ceq - IL_000c: stloc.1 - IL_000d: ldloc.1 - IL_000e: brfalse.s IL_001f - - IL_0010: nop - IL_0011: ldstr "Failed" - IL_0016: call void [mscorlib]System.Console::WriteLine(string) - IL_001b: nop - IL_001c: ldc.i4.m1 - IL_001d: stloc.0 - IL_001e: nop - IL_001f: call bool PreInitDataTest::TestPreInitIntData() - IL_0024: ldc.i4.0 - IL_0025: ceq - IL_0027: stloc.2 - IL_0028: ldloc.2 - IL_0029: brfalse.s IL_003a - - IL_002b: nop - IL_002c: ldstr "Failed" - IL_0031: call void [mscorlib]System.Console::WriteLine(string) - IL_0036: nop - IL_0037: ldc.i4.m1 - IL_0038: stloc.0 - IL_0039: nop - IL_003a: call bool PreInitDataTest::TestPreInitTypeData() - IL_003f: ldc.i4.0 - IL_0040: ceq - IL_0042: stloc.3 - IL_0043: ldloc.3 - IL_0044: brfalse.s IL_0055 - - IL_0046: nop - IL_0047: ldstr "Failed" - IL_004c: call void [mscorlib]System.Console::WriteLine(string) - IL_0051: nop - IL_0052: ldc.i4.m1 - IL_0053: stloc.0 - IL_0054: nop - IL_0055: call bool PreInitDataTest::TestPreInitMethodData() - IL_005a: ldc.i4.0 - IL_005b: ceq - IL_005d: stloc.s V_4 - IL_005f: ldloc.s V_4 - IL_0061: brfalse.s IL_0072 - - IL_0063: nop - IL_0064: ldstr "Failed" - IL_0069: call void [mscorlib]System.Console::WriteLine(string) - IL_006e: nop - IL_006f: ldc.i4.m1 - IL_0070: stloc.0 - IL_0071: nop - IL_0072: call bool PreInitDataTest::TestOtherStatics() - IL_0077: ldc.i4.0 - IL_0078: ceq - IL_007a: stloc.s V_5 - IL_007c: ldloc.s V_5 - IL_007e: brfalse.s IL_008f - - IL_0080: nop - IL_0081: ldstr "Failed" - IL_0086: call void [mscorlib]System.Console::WriteLine(string) - IL_008b: nop - IL_008c: ldc.i4.m1 - IL_008d: stloc.0 - IL_008e: nop - IL_008f: ldloc.0 - IL_0090: stloc.s V_6 - IL_0092: br.s IL_0094 - - IL_0094: ldloc.s V_6 - IL_0096: ret - } // end of method PreInitDataTest::Main - - .method private hidebysig static bool TestPreInitPrimitiveData() cil managed - { - // Code size 41 (0x29) - .maxstack 2 - .locals init (bool V_0, - bool V_1) - IL_0000: nop - IL_0001: ldstr "Testing preinitialized primitive data..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldsfld int32 PreInitData::PreInitializedInt32Field - IL_0011: ldc.i4 0x12345678 - IL_0016: ceq - IL_0018: ldc.i4.0 - IL_0019: ceq - IL_001b: stloc.0 - IL_001c: ldloc.0 - IL_001d: brfalse.s IL_0023 - - IL_001f: ldc.i4.0 - IL_0020: stloc.1 - IL_0021: br.s IL_0027 - - IL_0023: ldc.i4.1 - IL_0024: stloc.1 - IL_0025: br.s IL_0027 - - IL_0027: ldloc.1 - IL_0028: ret - } // end of method PreInitDataTest::TestPreInitPrimitiveData - - .method private hidebysig static bool TestPreInitIntData() cil managed - { - // Code size 65 (0x41) - .maxstack 3 - .locals init (int32 V_0, - bool V_1, - bool V_2, - bool V_3) - IL_0000: nop - IL_0001: ldstr "Testing preinitialized int array..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldc.i4.0 - IL_000d: stloc.0 - IL_000e: br.s IL_002d - - IL_0010: nop - IL_0011: ldsfld int32[] PreInitData::PreInitializedIntField - IL_0016: ldloc.0 - IL_0017: ldelem.i4 - IL_0018: ldloc.0 - IL_0019: ldc.i4.1 - IL_001a: add - IL_001b: ceq - IL_001d: ldc.i4.0 - IL_001e: ceq - IL_0020: stloc.1 - IL_0021: ldloc.1 - IL_0022: brfalse.s IL_0028 - - IL_0024: ldc.i4.0 - IL_0025: stloc.2 - IL_0026: br.s IL_003f - - IL_0028: nop - IL_0029: ldloc.0 - IL_002a: ldc.i4.1 - IL_002b: add - IL_002c: stloc.0 - IL_002d: ldloc.0 - IL_002e: ldsfld int32[] PreInitData::PreInitializedIntField - IL_0033: ldlen - IL_0034: conv.i4 - IL_0035: clt - IL_0037: stloc.3 - IL_0038: ldloc.3 - IL_0039: brtrue.s IL_0010 - - IL_003b: ldc.i4.1 - IL_003c: stloc.2 - IL_003d: br.s IL_003f - - IL_003f: ldloc.2 - IL_0040: ret - } // end of method PreInitDataTest::TestPreInitIntData - -.method private hidebysig static bool TestPreInitTypeData() cil managed -{ - // Code size 191 (0xbf) - .maxstack 2 - .locals init (bool V_0, - bool V_1, - bool V_2, - bool V_3, - bool V_4) - IL_0000: nop - IL_0001: ldstr "Testing preinitialized type array..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField - IL_0011: ldc.i4.0 - IL_0012: ldelema [mscorlib]System.RuntimeTypeHandle - IL_0017: ldtoken [mscorlib]System.Int32 - IL_001c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_0021: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() - IL_0026: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_002b: ldc.i4.0 - IL_002c: ceq - IL_002e: stloc.0 - IL_002f: ldloc.0 - IL_0030: brfalse.s IL_0039 - IL_0032: ldc.i4.0 - IL_0033: stloc.1 - IL_0034: br IL_00bd - IL_0039: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField - IL_003e: ldc.i4.1 - IL_003f: ldelema [mscorlib]System.RuntimeTypeHandle - IL_0044: ldtoken [mscorlib]System.Int16 - IL_0049: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_004e: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() - IL_0053: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_0058: ldc.i4.0 - IL_0059: ceq - IL_005b: stloc.2 - IL_005c: ldloc.2 - IL_005d: brfalse.s IL_0063 - IL_005f: ldc.i4.0 - IL_0060: stloc.1 - IL_0061: br.s IL_00bd - IL_0063: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField - IL_0068: ldc.i4.2 - IL_0069: ldelema [mscorlib]System.RuntimeTypeHandle - IL_006e: ldtoken [mscorlib]System.Int64 - IL_0073: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_0078: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() - IL_007d: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_0082: ldc.i4.0 - IL_0083: ceq - IL_0085: stloc.3 - IL_0086: ldloc.3 - IL_0087: brfalse.s IL_008d - IL_0089: ldc.i4.0 - IL_008a: stloc.1 - IL_008b: br.s IL_00bd - IL_008d: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField - IL_0092: ldc.i4.3 - IL_0093: ldelema [mscorlib]System.RuntimeTypeHandle - IL_0098: ldtoken [mscorlib]System.String - IL_009d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_00a2: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() - IL_00a7: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) - IL_00ac: ldc.i4.0 - IL_00ad: ceq - IL_00af: stloc.s V_4 - IL_00b1: ldloc.s V_4 - IL_00b3: brfalse.s IL_00b9 - IL_00b5: ldc.i4.0 - IL_00b6: stloc.1 - IL_00b7: br.s IL_00bd - IL_00b9: ldc.i4.1 - IL_00ba: stloc.1 - IL_00bb: br.s IL_00bd - IL_00bd: ldloc.1 - IL_00be: ret -} // end of method PreInitDataTest::TestPreInitTypeData - - - .method private hidebysig static bool TestPreInitMethodData() cil managed - { - // Code size 92 (0x5c) - .maxstack 3 - .locals init (bool V_0, - bool V_1, - bool V_2) - IL_0000: nop - IL_0001: ldstr "Testing preinitialized method array..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldsfld native int[] PreInitData::PreInitializedMethodField - IL_0011: ldc.i4.0 - IL_0012: ldelem.i - IL_0013: ldnull - IL_0014: ldftn void NativeMethods::Func1(int32) - IL_001a: newobj instance void PreInitDataTest/Func1Proc::.ctor(object, - native int) - IL_001f: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0) - IL_0024: call bool [mscorlib]System.IntPtr::op_Inequality(native int, - native int) - IL_0029: stloc.0 - IL_002a: ldloc.0 - IL_002b: brfalse.s IL_0031 - - IL_002d: ldc.i4.0 - IL_002e: stloc.1 - IL_002f: br.s IL_005a - - IL_0031: ldsfld native int[] PreInitData::PreInitializedMethodField - IL_0036: ldc.i4.1 - IL_0037: ldelem.i - IL_0038: ldnull - IL_0039: ldftn void NativeMethods::Func2(float32) - IL_003f: newobj instance void PreInitDataTest/Func2Proc::.ctor(object, - native int) - IL_0044: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0) - IL_0049: call bool [mscorlib]System.IntPtr::op_Inequality(native int, - native int) - IL_004e: stloc.2 - IL_004f: ldloc.2 - IL_0050: brfalse.s IL_0056 - - IL_0052: ldc.i4.0 - IL_0053: stloc.1 - IL_0054: br.s IL_005a - - IL_0056: ldc.i4.1 - IL_0057: stloc.1 - IL_0058: br.s IL_005a - - IL_005a: ldloc.1 - IL_005b: ret - } // end of method PreInitDataTest::TestPreInitMethodData - - - .method private hidebysig static bool TestOtherStatics() cil managed - { - // Code size 64 (0x40) - .maxstack 2 - .locals init (bool V_0, - bool V_1, - bool V_2) - IL_0000: nop - IL_0001: ldstr "Testing other statics work well with preinitialize" - + "d data in the same type..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldsfld string PreInitData::StaticStringFieldBefore - IL_0011: ldstr "BEFORE" - IL_0016: call bool [mscorlib]System.String::op_Inequality(string, - string) - IL_001b: stloc.0 - IL_001c: ldloc.0 - IL_001d: brfalse.s IL_0023 - - IL_001f: ldc.i4.0 - IL_0020: stloc.1 - IL_0021: br.s IL_003e - - IL_0023: ldsfld string PreInitData::StaticStringFieldAfter - IL_0028: ldstr "AFTER" - IL_002d: call bool [mscorlib]System.String::op_Inequality(string, - string) - IL_0032: stloc.2 - IL_0033: ldloc.2 - IL_0034: brfalse.s IL_003a - - IL_0036: ldc.i4.0 - IL_0037: stloc.1 - IL_0038: br.s IL_003e - - IL_003a: ldc.i4.1 - IL_003b: stloc.1 - IL_003c: br.s IL_003e - - IL_003e: ldloc.1 - IL_003f: ret - } // end of method PreInitDataTest::TestOtherStatics - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method PreInitDataTest::.ctor - -} // end of class PreInitDataTest - diff --git a/tests/src/Simple/PreInitData/PreInitData.cmd b/tests/src/Simple/Preinitialization/Preinitialization.cmd similarity index 100% rename from tests/src/Simple/PreInitData/PreInitData.cmd rename to tests/src/Simple/Preinitialization/Preinitialization.cmd diff --git a/tests/src/Simple/Preinitialization/Preinitialization.cs b/tests/src/Simple/Preinitialization/Preinitialization.cs new file mode 100644 index 00000000000..55e0155828f --- /dev/null +++ b/tests/src/Simple/Preinitialization/Preinitialization.cs @@ -0,0 +1,588 @@ +// 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.Runtime.InteropServices; + +using BindingFlags = System.Reflection.BindingFlags; + +internal class Program +{ + private static int Main() + { +#if !MULTIMODULE_BUILD + TestLdstr.Run(); + TestException.Run(); + TestThreadStaticNotInitialized.Run(); + TestUntouchedThreadStaticInitialized.Run(); + TestPointers.Run(); + TestConstants.Run(); + TestArray.Run(); + TestMdArray.Run(); + TestSimpleObject.Run(); + TestFinalizableObject.Run(); + TestStoreIntoOtherStatic.Run(); + TestCctorCycle.Run(); + TestReferenceTypeAllocation.Run(); + TestReferenceTypeWithGCPointerAllocation.Run(); + TestRelationalOperators.Run(); + TestTryFinally.Run(); + TestTryCatch.Run(); + TestBadClass.Run(); + TestRefs.Run(); +#else + Console.WriteLine("Preinitialization is disabled in multimodule builds for now. Skipping test."); +#endif + + return 100; + } +} + +class TestLdstr +{ + static string s_mine; + static bool s_literalsEqual; + + static string GetOtherString() => "Hello"; + + static TestLdstr() + { + s_mine = nameof(TestLdstr); + s_literalsEqual = Object.ReferenceEquals("Hello", GetOtherString()); + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestLdstr)); + Assert.AreSame(nameof(TestLdstr), s_mine); + Assert.True(s_literalsEqual); + } +} + +class TestException +{ + static bool s_wasThrown; + + static TestException() + { + try + { + throw new Exception(); + } + catch (Exception) + { + s_wasThrown = true; + } + } + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestException)); + Assert.True(s_wasThrown); + } +} + +class TestThreadStaticNotInitialized +{ + [ThreadStatic] + static bool s_wasRun = true; + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestThreadStaticNotInitialized)); + Assert.True(s_wasRun); + } +} + +class TestUntouchedThreadStaticInitialized +{ + [ThreadStatic] +#pragma warning disable 169 + static bool s_unused; +#pragma warning restore 169 + static bool s_wasRun = true; + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestUntouchedThreadStaticInitialized)); + Assert.True(s_wasRun); + } +} + +unsafe class TestPointers +{ + static byte* s_myByte = (byte*)123; + static void* s_myVoid = GimmeVoid(s_myByte); + static byte*[] s_byteStarArray = new byte*[] { (byte*)123, (byte*)456 }; + + static void* GimmeVoid(byte* template) + { + return template; + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestPointers)); + Assert.AreEqual((void*)123, s_myByte); + Assert.AreEqual((void*)123, s_myVoid); + + Assert.AreEqual(2, s_byteStarArray.Length); + Assert.AreEqual((byte*)123, s_byteStarArray[0]); + Assert.AreEqual((byte*)456, s_byteStarArray[1]); + } +} + +class TestConstants +{ + static bool s_bool = true; + static int s_smallInt = 3; + static int s_mediumInd = 70; + static int s_bigInt = 2000000; + static long s_hugeInt = 20000000000; + static float s_float = 3.14f; + static double s_double = 3.14; + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestConstants)); + Assert.AreEqual(true, s_bool); + Assert.AreEqual(3, s_smallInt); + Assert.AreEqual(70, s_mediumInd); + Assert.AreEqual(2000000, s_bigInt); + Assert.AreEqual(20000000000, s_hugeInt); + Assert.AreEqual(3.14f, s_float); + Assert.AreEqual(3.14, s_double); + } +} + +class TestArray +{ + struct MyValueType + { + public bool B; + public int I; + } + + enum MyEnum + { + One, Two + } + + static byte[] s_byteArray; + static MyValueType[] s_valueTypeArray; + static int s_byteArrayCount; + static MyEnum[] s_enumArray; + + static TestArray() + { + s_byteArray = new byte[] + { + 1, 2, 3, 9, 8, 7, 1, 2, 3, 9, 8, 7 + }; + + s_byteArrayCount = s_byteArray.Length; + + s_valueTypeArray = new MyValueType[2] + { + new MyValueType { B = false, I = 555 }, + new MyValueType { B = true, I = 565 }, + }; + + s_enumArray = new MyEnum[2] { MyEnum.One, MyEnum.Two }; + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestArray)); + Assert.AreEqual(s_byteArray.Length, 12); + Assert.AreEqual(s_byteArray[0], 1); + Assert.AreEqual(s_byteArray[1], 2); + Assert.AreEqual(s_byteArray[11], 7); + Assert.AreEqual(s_byteArrayCount, 12); + + Assert.AreEqual(s_valueTypeArray.Length, 2); + Assert.AreEqual(s_valueTypeArray[0].B, false); + Assert.AreEqual(s_valueTypeArray[0].I, 555); + Assert.AreEqual(s_valueTypeArray[1].B, true); + Assert.AreEqual(s_valueTypeArray[1].I, 565); + + Assert.AreEqual(s_enumArray.Length, 2); + Assert.AreEqual((int)s_enumArray[0], (int)MyEnum.One); + Assert.AreEqual((int)s_enumArray[1], (int)MyEnum.Two); + } +} + +class TestMdArray +{ + static byte[,] s_myMdArray = new byte[10, 10]; + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestMdArray)); + Assert.AreEqual(100, s_myMdArray.Length); + } +} + +class TestSimpleObject +{ + static object s_object = new object(); + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestSimpleObject)); + Assert.AreSame(typeof(object), s_object.GetType()); + } +} + +class TestFinalizableObject +{ + class Finalizable + { + ~Finalizable() + { + Console.WriteLine("Finalized"); + } + } + + static object s_object = new Finalizable(); + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestFinalizableObject)); + Assert.AreSame(typeof(Finalizable), s_object.GetType()); + } +} + +static class TestStoreIntoOtherStatic +{ + class Park + { + public static int s_parked; + } + + static TestStoreIntoOtherStatic() + { + Park.s_parked = 123; + } + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestStoreIntoOtherStatic)); + } +} + +static class TestCctorCycle +{ + static readonly int s_value = Cycler.s_theValue; + + class Cycler + { + public static readonly int s_theValue = s_value; + } + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestCctorCycle)); + Assert.AreEqual(0, s_value); + } +} + +class TestReferenceTypeAllocation +{ + class ReferenceType + { + public int IntValue; + public double DoubleValue; + + public ReferenceType(int intValue, double doubleValue) + { + IntValue = intValue; + DoubleValue = doubleValue; + } + } + + static ReferenceType s_referenceType = new ReferenceType(12345, 3.14159); + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestReferenceTypeAllocation)); + Assert.AreEqual(12345, s_referenceType.IntValue); + Assert.AreEqual(3.14159, s_referenceType.DoubleValue); + } +} + +class TestReferenceTypeWithGCPointerAllocation +{ + class ReferenceType + { + public string StringValue; + + public ReferenceType(string stringvalue) + { + StringValue = stringvalue; + } + } + + static ReferenceType s_referenceType = new ReferenceType("hi"); + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestReferenceTypeWithGCPointerAllocation)); + Assert.AreSame("hi", s_referenceType.StringValue); + } +} + +static class TestRelationalOperators +{ + static int s_zeroInt = 0; + static double s_zeroDouble = 0.0; + static long s_zeroLong = 0; + static int s_minusOneInt = -1; + static long s_minusOneLong = -1; + + static bool s_finished; + + static TestRelationalOperators() + { + if (s_zeroInt > 0) + throw new Exception(); + if (s_zeroInt < 0) + throw new Exception(); + if (s_zeroInt >= 0 && s_zeroInt <= 0) + { + if (s_zeroLong > 0) + throw new Exception(); + if (s_zeroLong < 0) + throw new Exception(); + if (s_zeroLong >= 0 && s_zeroLong <= 0) + { + if (s_zeroDouble > 0) + throw new Exception(); + if (s_zeroDouble < 0) + throw new Exception(); + if (s_zeroDouble >= 0 && s_zeroDouble <= 0) + { + if ((uint)s_minusOneInt < (uint)s_zeroInt) + throw new Exception(); + if ((uint)s_zeroInt > (uint)s_minusOneInt) + throw new Exception(); + if ((ulong)s_minusOneLong < (ulong)s_zeroLong) + throw new Exception(); + if ((ulong)s_zeroLong > (ulong)s_minusOneLong) + throw new Exception(); + + if (s_zeroInt == 0 && s_zeroLong == 0) + s_finished = true; + } + } + } + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestRelationalOperators)); + Assert.AreEqual(true, s_finished); + } +} + +class TestTryFinally +{ + static int s_cookie; + + static TestTryFinally() + { + try + { + if (new byte[0].Length > 0) + throw new Exception(); + } + finally + { + s_cookie = 1985; + } + } + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestTryFinally)); + Assert.AreEqual(1985, s_cookie); + } +} + +class TestTryCatch +{ + static int s_cookie; + + static TestTryCatch() + { + try + { + if (s_cookie > 0) + throw null; + } + catch (Exception) + { + s_cookie = 100; + } + s_cookie = 2020; + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestTryCatch)); + Assert.AreEqual(2020, s_cookie); + } +} + +class TestBadClass +{ + [StructLayout(LayoutKind.Explicit)] + class BadLayoutClass + { + } + + static int s_cookie; + static object s_badClass; + + static object MakeBadLayoutClass() => new BadLayoutClass(); + + static TestBadClass() + { + try + { + s_badClass = MakeBadLayoutClass(); + s_cookie = -1; + } + catch (Exception) + { + s_cookie = 1; + } + } + + public static void Run() + { + Assert.IsLazyInitialized(typeof(TestBadClass)); + Assert.AreEqual(1, s_cookie); + Assert.AreSame(null, s_badClass); + } +} + +class TestRefs +{ + struct IntStruct { public int Value { get; set; } } + struct DoubleStruct { public double Value { get; set; } } + + static IntStruct s_value1; + static IntStruct s_value2; + static DoubleStruct s_doubleValue; + + static ref IntStruct PickOne(int which) + { + if (which == 1) + return ref s_value1; + return ref s_value2; + } + + static void Set(ref IntStruct location, int value) + { + location.Value = value; + } + + static TestRefs() + { + ref IntStruct loc1 = ref PickOne(1); + Set(ref loc1, 41); + s_value1.Value++; + + s_value2.Value = 98; + ref IntStruct loc2 = ref PickOne(2); + if (loc2.Value == 98) + { + loc2.Value++; + } + if (s_value2.Value == 99) + { + s_value2.Value++; + } + + ref DoubleStruct dblRef = ref s_doubleValue; + dblRef.Value = 3.14; + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(TestRefs)); + Assert.AreEqual(42, s_value1.Value); + Assert.AreEqual(100, s_value2.Value); + Assert.AreEqual(3.14, s_doubleValue.Value); + } +} + +static class Assert +{ + private static bool HasCctor(Type type) + { + return type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Static, null, Type.EmptyTypes, null) != null; + } + + public static void IsPreinitialized(Type type) + { + if (HasCctor(type)) + throw new Exception(); + } + + public static void IsLazyInitialized(Type type) + { + if (!HasCctor(type)) + throw new Exception(); + } + + public static unsafe void AreEqual(void* v1, void* v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static unsafe void AreEqual(bool v1, bool v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static unsafe void AreEqual(int v1, int v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static unsafe void AreEqual(long v1, long v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static unsafe void AreEqual(float v1, float v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static unsafe void AreEqual(double v1, double v2) + { + if (v1 != v2) + throw new Exception(); + } + + public static void True(bool v) + { + if (!v) + throw new Exception(); + } + + public static void AreSame(T v1, T v2) where T : class + { + if (v1 != v2) + throw new Exception(); + } +} diff --git a/tests/src/Simple/Preinitialization/Preinitialization.csproj b/tests/src/Simple/Preinitialization/Preinitialization.csproj new file mode 100644 index 00000000000..9a3883111e1 --- /dev/null +++ b/tests/src/Simple/Preinitialization/Preinitialization.csproj @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/src/Simple/PreInitData/PreInitData.sh b/tests/src/Simple/Preinitialization/Preinitialization.sh similarity index 100% rename from tests/src/Simple/PreInitData/PreInitData.sh rename to tests/src/Simple/Preinitialization/Preinitialization.sh diff --git a/tests/src/Simple/PreInitData/no_cpp b/tests/src/Simple/Preinitialization/no_cpp similarity index 100% rename from tests/src/Simple/PreInitData/no_cpp rename to tests/src/Simple/Preinitialization/no_cpp