diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5b5d22984f53f8..479196edabbaca 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -48,13 +48,13 @@ https://github.com/dotnet/command-line-api 5618b2d243ccdeb5c7e50a298b33b13036b4351b - + https://github.com/dotnet/emsdk - 76e6b338e87093ddc9b3b650f027706aba65d388 + 6625add9a3eadc2954af0311be35290cfefcddb0 - + https://github.com/dotnet/emsdk - 76e6b338e87093ddc9b3b650f027706aba65d388 + 6625add9a3eadc2954af0311be35290cfefcddb0 diff --git a/eng/Versions.props b/eng/Versions.props index bb3c6456d69106..f5469fd5a4f3d4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -22,8 +22,8 @@ false $(AssemblyVersion) true - 7.0.0-rc.2.22459.3 - 7.0.0-rc.2.22459.3 + 7.0.0-rc.2.22465.1 + 7.0.0-rc.2.22465.1 1.1.2-beta1.22403.2 - 7.0.0-preview-20220914.1 + 7.0.0-preview-20220916.1 7.0.100-1.22423.4 $(MicrosoftNETILLinkTasksVersion) diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index ca02a670d2142c..63253997dfc422 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -193,10 +193,10 @@ jobs: # WebAssembly Firefox - ${{ if eq(parameters.platform, 'Browser_wasm_firefox') }}: - - (Ubuntu.1804.Amd64)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-webassembly-20220504035734-67908a0 + - (Ubuntu.1804.Amd64)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-webassembly-20220908122752-67908a0 # WebAssembly windows - ${{ if eq(parameters.platform, 'Browser_wasm_win') }}: - - (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly-20220620175048-bf70060 + - (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly-20220908122953-3a6fb49 ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3b7c12f25ccb12..1f9015cf8760e4 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -18631,10 +18631,45 @@ bool GenTree::isCommutativeHWIntrinsic() const assert(gtOper == GT_HWINTRINSIC); #ifdef TARGET_XARCH - return HWIntrinsicInfo::IsCommutative(AsHWIntrinsic()->GetHWIntrinsicId()); -#else - return false; + const GenTreeHWIntrinsic* node = AsHWIntrinsic(); + NamedIntrinsic id = node->GetHWIntrinsicId(); + + if (HWIntrinsicInfo::IsCommutative(id)) + { + return true; + } + + if (HWIntrinsicInfo::IsMaybeCommutative(id)) + { + switch (id) + { + case NI_SSE_Max: + case NI_SSE_Min: + { + return false; + } + + case NI_SSE2_Max: + case NI_SSE2_Min: + { + return !varTypeIsFloating(node->GetSimdBaseType()); + } + + case NI_AVX_Max: + case NI_AVX_Min: + { + return false; + } + + default: + { + unreached(); + } + } + } #endif // TARGET_XARCH + + return false; } bool GenTree::isContainableHWIntrinsic() const diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 88e5b4ae575059..b1299df1c1f1cf 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -153,6 +153,11 @@ enum HWIntrinsicFlag : unsigned int // the intrinsic can be used on hardware with AVX but not AVX2 support HW_Flag_AvxOnlyCompatible = 0x40000, + // MaybeCommutative + // - if a binary-op intrinsic is maybe commutative (e.g., Max or Min for float/double), its op1 can possibly be + // contained + HW_Flag_MaybeCommutative = 0x80000, + #elif defined(TARGET_ARM64) // The intrinsic has an immediate operand // - the value can be (and should be) encoded in a corresponding instruction when the operand value is constant @@ -626,6 +631,18 @@ struct HWIntrinsicInfo return (flags & HW_Flag_Commutative) != 0; } + static bool IsMaybeCommutative(NamedIntrinsic id) + { + HWIntrinsicFlag flags = lookupFlags(id); +#if defined(TARGET_XARCH) + return (flags & HW_Flag_MaybeCommutative) != 0; +#elif defined(TARGET_ARM64) + return false; +#else +#error Unsupported platform +#endif + } + static bool RequiresCodegen(NamedIntrinsic id) { HWIntrinsicFlag flags = lookupFlags(id); diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 7068e39bd1b560..0fff3a98d3bec3 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -290,9 +290,9 @@ HARDWARE_INTRINSIC(SSE, LoadHigh, HARDWARE_INTRINSIC(SSE, LoadLow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movlps, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE, LoadScalarVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE, LoadVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movups, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) -HARDWARE_INTRINSIC(SSE, Max, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE, Max, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE, MaxScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxss, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(SSE, Min, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE, Min, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE, MinScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minss, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) HARDWARE_INTRINSIC(SSE, MoveHighToLow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movhlps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoContainment) HARDWARE_INTRINSIC(SSE, MoveLowToHigh, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movlhps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoContainment) @@ -403,10 +403,10 @@ HARDWARE_INTRINSIC(SSE2, LoadLow, HARDWARE_INTRINSIC(SSE2, LoadScalarVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movd, INS_movd, INS_movq, INS_movq, INS_invalid, INS_movsdsse2}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, LoadVector128, 16, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_invalid, INS_movupd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, MaskMove, 16, 3, {INS_maskmovdqu, INS_maskmovdqu, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_BaseTypeFromSecondArg) -HARDWARE_INTRINSIC(SSE2, Max, 16, 2, {INS_invalid, INS_pmaxub, INS_pmaxsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE2, Max, 16, 2, {INS_invalid, INS_pmaxub, INS_pmaxsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE2, MemoryFence, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, MaxScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(SSE2, Min, 16, 2, {INS_invalid, INS_pminub, INS_pminsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE2, Min, 16, 2, {INS_invalid, INS_pminub, INS_pminsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE2, MinScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) HARDWARE_INTRINSIC(SSE2, MoveMask, 16, 1, {INS_pmovmskb, INS_pmovmskb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movmskpd}, HW_Category_SimpleSIMD, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(SSE2, MoveScalar, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movq, INS_movq, INS_invalid, INS_movsdsse2}, HW_Category_SIMDScalar, HW_Flag_NoContainment) @@ -598,8 +598,8 @@ HARDWARE_INTRINSIC(AVX, InsertVector128, HARDWARE_INTRINSIC(AVX, LoadAlignedVector256, 32, 1, {INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movaps, INS_movapd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX, LoadDquVector256, 32, 1, {INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX, LoadVector256, 32, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) -HARDWARE_INTRINSIC(AVX, Max, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) -HARDWARE_INTRINSIC(AVX, Min, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(AVX, Max, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) +HARDWARE_INTRINSIC(AVX, Min, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(AVX, MaskLoad, -1, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vmaskmovps, INS_vmaskmovpd}, HW_Category_MemoryLoad, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX, MaskStore, -1, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vmaskmovps, INS_vmaskmovpd}, HW_Category_MemoryStore, HW_Flag_NoContainment|HW_Flag_BaseTypeFromSecondArg) HARDWARE_INTRINSIC(AVX, MoveMask, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movmskps, INS_movmskpd}, HW_Category_SimpleSIMD, HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index d3e4ec30779124..51e42b8676b774 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -953,7 +953,8 @@ private static TypeDesc GetActualTemplateTypeForType(NodeFactory factory, TypeDe private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) { - ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromStaticLayout(_type.GetClosestDefType())); + DefType closestCanonDefType = (DefType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific); + ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromStaticLayout(closestCanonDefType)); staticsBagKind = BagElementKind.GcStaticDesc; return symbol; @@ -961,7 +962,8 @@ private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind stati private ISymbolNode GetThreadStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) { - ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromThreadStaticLayout(_type.GetClosestDefType())); + DefType closestCanonDefType = (DefType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific); + ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromThreadStaticLayout(closestCanonDefType)); staticsBagKind = BagElementKind.ThreadStaticDesc; return symbol; @@ -997,13 +999,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (!_isUniversalCanon) { - if (_type.GetClosestDefType().GCStaticFieldSize.AsInt > 0) + DefType closestCanonDefType = (DefType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific); + if (closestCanonDefType.GCStaticFieldSize.AsInt > 0) { BagElementKind ignored; yield return new DependencyListEntry(GetStaticsNode(context, out ignored), "type gc static info"); } - if (_type.GetClosestDefType().ThreadGcStaticFieldSize.AsInt > 0) + if (closestCanonDefType.ThreadGcStaticFieldSize.AsInt > 0) { BagElementKind ignored; yield return new DependencyListEntry(GetThreadStaticsNode(context, out ignored), "type thread static info"); @@ -1207,24 +1210,24 @@ public override Vertex WriteVertex(NodeFactory factory) if (!_isUniversalCanon) { - DefType closestDefType = _type.GetClosestDefType(); - if (closestDefType.NonGCStaticFieldSize.AsInt != 0) + DefType closestCanonDefType = (DefType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific); + if (closestCanonDefType.NonGCStaticFieldSize.AsInt != 0) { - layoutInfo.AppendUnsigned(BagElementKind.NonGcStaticDataSize, checked((uint)closestDefType.NonGCStaticFieldSize.AsInt)); + layoutInfo.AppendUnsigned(BagElementKind.NonGcStaticDataSize, checked((uint)closestCanonDefType.NonGCStaticFieldSize.AsInt)); } - if (closestDefType.GCStaticFieldSize.AsInt != 0) + if (closestCanonDefType.GCStaticFieldSize.AsInt != 0) { - layoutInfo.AppendUnsigned(BagElementKind.GcStaticDataSize, checked((uint)closestDefType.GCStaticFieldSize.AsInt)); + layoutInfo.AppendUnsigned(BagElementKind.GcStaticDataSize, checked((uint)closestCanonDefType.GCStaticFieldSize.AsInt)); BagElementKind staticDescBagType; ISymbolNode staticsDescSymbol = GetStaticsNode(factory, out staticDescBagType); uint gcStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(staticsDescSymbol); layoutInfo.AppendUnsigned(staticDescBagType, gcStaticsSymbolIndex); } - if (closestDefType.ThreadGcStaticFieldSize.AsInt != 0) + if (closestCanonDefType.ThreadGcStaticFieldSize.AsInt != 0) { - layoutInfo.AppendUnsigned(BagElementKind.ThreadStaticDataSize, checked((uint)closestDefType.ThreadGcStaticFieldSize.AsInt)); + layoutInfo.AppendUnsigned(BagElementKind.ThreadStaticDataSize, checked((uint)closestCanonDefType.ThreadGcStaticFieldSize.AsInt)); BagElementKind threadStaticDescBagType; ISymbolNode threadStaticsDescSymbol = GetThreadStaticsNode(factory, out threadStaticDescBagType); uint threadStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(threadStaticsDescSymbol); diff --git a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs index 3c3e831fac98d4..a9ebcba2950c55 100644 --- a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs +++ b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs @@ -50,7 +50,9 @@ public Native(WAVEOUTCAPS managed) wMid = managed.wMid; wPid = managed.wPid; vDriverVersion = managed.vDriverVersion; - managed.szPname.CopyTo(MemoryMarshal.CreateSpan(ref szPname[0], szPnameLength)); + Span szPnameSpan = MemoryMarshal.CreateSpan(ref szPname[0], szPnameLength); + szPnameSpan.Clear(); + managed.szPname?.CopyTo(szPnameSpan); dwFormats = managed.dwFormats; wChannels = managed.wChannels; wReserved1 = managed.wReserved1; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index a26b2fe68e7190..e36ae28fd4316d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -299,43 +299,45 @@ private static void BindInstance( if (config != null && config.GetChildren().Any()) { - // for arrays, collections, and read-only list-like interfaces, we concatenate on to what is already there + // for arrays, collections, and read-only list-like interfaces, we concatenate on to what is already there, if we can if (type.IsArray || IsArrayCompatibleInterface(type)) { if (!bindingPoint.IsReadOnly) { bindingPoint.SetValue(BindArray(type, (IEnumerable?)bindingPoint.Value, config, options)); + return; + } + + // for getter-only collection properties that we can't add to, nothing more we can do + if (type.IsArray || IsImmutableArrayCompatibleInterface(type)) + { + return; } - return; } - // for sets and read-only set interfaces, we clone what's there into a new collection. - if (TypeIsASetInterface(type)) + // for sets and read-only set interfaces, we clone what's there into a new collection, if we can + if (TypeIsASetInterface(type) && !bindingPoint.IsReadOnly) { - if (!bindingPoint.IsReadOnly) + object? newValue = BindSet(type, (IEnumerable?)bindingPoint.Value, config, options); + if (newValue != null) { - object? newValue = BindSet(type, (IEnumerable?)bindingPoint.Value, config, options); - if (newValue != null) - { - bindingPoint.SetValue(newValue); - } + bindingPoint.SetValue(newValue); } + return; } // For other mutable interfaces like ICollection<>, IDictionary<,> and ISet<>, we prefer copying values and setting them // on a new instance of the interface over populating the existing instance implementing the interface. // This has already been done, so there's not need to check again. - if (TypeIsADictionaryInterface(type)) + if (TypeIsADictionaryInterface(type) && !bindingPoint.IsReadOnly) { - if (!bindingPoint.IsReadOnly) + object? newValue = BindDictionaryInterface(bindingPoint.Value, type, config, options); + if (newValue != null) { - object? newValue = BindDictionaryInterface(bindingPoint.Value, type, config, options); - if (newValue != null) - { - bindingPoint.SetValue(newValue); - } + bindingPoint.SetValue(newValue); } + return; } @@ -848,6 +850,16 @@ private static bool IsArrayCompatibleInterface(Type type) || genericTypeDefinition == typeof(IReadOnlyList<>); } + private static bool IsImmutableArrayCompatibleInterface(Type type) + { + if (!type.IsInterface || !type.IsConstructedGenericType) { return false; } + + Type genericTypeDefinition = type.GetGenericTypeDefinition(); + return genericTypeDefinition == typeof(IEnumerable<>) + || genericTypeDefinition == typeof(IReadOnlyCollection<>) + || genericTypeDefinition == typeof(IReadOnlyList<>); + } + private static bool TypeIsASetInterface(Type type) { if (!type.IsInterface || !type.IsConstructedGenericType) { return false; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs index 9eae194f02cf99..521501d5938de3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs @@ -72,6 +72,8 @@ public string ReadOnly public ISet InstantiatedISet { get; set; } = new HashSet(); + public ISet ISetNoSetter { get; } = new HashSet(); + public HashSet InstantiatedHashSetWithSomeValues { get; set; } = new HashSet(new[] {"existing1", "existing2"}); @@ -662,6 +664,27 @@ public void CanBindNonInstantiatedISet() Assert.Equal("Yo2", options.NonInstantiatedISet.ElementAt(1)); } + [Fact] + public void CanBindISetNoSetter() + { + var dic = new Dictionary + { + {"ISetNoSetter:0", "Yo1"}, + {"ISetNoSetter:1", "Yo2"}, + {"ISetNoSetter:2", "Yo2"}, + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(dic); + + var config = configurationBuilder.Build(); + + var options = config.Get()!; + + Assert.Equal(2, options.ISetNoSetter.Count); + Assert.Equal("Yo1", options.ISetNoSetter.ElementAt(0)); + Assert.Equal("Yo2", options.ISetNoSetter.ElementAt(1)); + } + #if NETCOREAPP [Fact] public void CanBindInstantiatedIReadOnlySet() diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs index 7367d0664cf356..4f2b5911b2b7a9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs @@ -579,7 +579,10 @@ public void AlreadyInitializedStringDictionaryBinding() { {"AlreadyInitializedStringDictionaryInterface:abc", "val_1"}, {"AlreadyInitializedStringDictionaryInterface:def", "val_2"}, - {"AlreadyInitializedStringDictionaryInterface:ghi", "val_3"} + {"AlreadyInitializedStringDictionaryInterface:ghi", "val_3"}, + + {"IDictionaryNoSetter:Key1", "Value1"}, + {"IDictionaryNoSetter:Key2", "Value2"}, }; var configurationBuilder = new ConfigurationBuilder(); @@ -596,6 +599,10 @@ public void AlreadyInitializedStringDictionaryBinding() Assert.Equal("val_1", options.AlreadyInitializedStringDictionaryInterface["abc"]); Assert.Equal("val_2", options.AlreadyInitializedStringDictionaryInterface["def"]); Assert.Equal("val_3", options.AlreadyInitializedStringDictionaryInterface["ghi"]); + + Assert.Equal(2, options.IDictionaryNoSetter.Count); + Assert.Equal("Value1", options.IDictionaryNoSetter["Key1"]); + Assert.Equal("Value2", options.IDictionaryNoSetter["Key2"]); } [Fact] @@ -1059,7 +1066,10 @@ public void CanBindInitializedIEnumerableAndTheOriginalItemsAreNotMutated() {"AlreadyInitializedIEnumerableInterface:0", "val0"}, {"AlreadyInitializedIEnumerableInterface:1", "val1"}, {"AlreadyInitializedIEnumerableInterface:2", "val2"}, - {"AlreadyInitializedIEnumerableInterface:x", "valx"} + {"AlreadyInitializedIEnumerableInterface:x", "valx"}, + + {"ICollectionNoSetter:0", "val0"}, + {"ICollectionNoSetter:1", "val1"}, }; var configurationBuilder = new ConfigurationBuilder(); @@ -1084,6 +1094,10 @@ public void CanBindInitializedIEnumerableAndTheOriginalItemsAreNotMutated() Assert.Equal(2, options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.Count); Assert.Equal("This was here too", options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.ElementAt(0)); Assert.Equal("Don't touch me!", options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.ElementAt(1)); + + Assert.Equal(2, options.ICollectionNoSetter.Count); + Assert.Equal("val0", options.ICollectionNoSetter.ElementAt(0)); + Assert.Equal("val1", options.ICollectionNoSetter.ElementAt(1)); } [Fact] @@ -1424,6 +1438,8 @@ public InitializedCollectionsOptions() new CustomListIndirectlyDerivedFromIEnumerable(); public IReadOnlyDictionary AlreadyInitializedDictionary { get; set; } + + public ICollection ICollectionNoSetter { get; } = new List(); } private class CustomList : List @@ -1564,6 +1580,8 @@ public OptionsWithDictionary() public Dictionary StringDictionary { get; set; } + public IDictionary IDictionaryNoSetter { get; } = new Dictionary(); + public Dictionary ObjectDictionary { get; set; } public Dictionary> ISetDictionary { get; set; } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs index 54b2c5352f8c21..c910e7f8228c5d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs @@ -201,7 +201,9 @@ public sealed class JSMarshalerType [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public struct JSMarshalerArgument { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToManagedCallback(ref JSMarshalerArgument arg, out T value); + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToJSCallback(ref JSMarshalerArgument arg, T value); public void Initialize() { throw null; } public void ToManaged(out bool value) { throw null; } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 5b932edb7965dc..2fbf3f448f5cd5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -124,7 +124,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return vaule + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value try { JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback(); @@ -195,6 +195,32 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } } + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // string GetManagedStackTrace(GCHandle exception) + public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) + { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + try + { + GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle; + if (exception_gc_handle.Target is Exception exception) + { + arg_return.ToJS(exception.StackTrace); + } + else + { + throw new InvalidOperationException("Exception is null"); + } + } + catch (Exception ex) + { + arg_exc.ToJS(ex); + } + } + #if FEATURE_WASM_THREADS [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index f3101e881fe9ac..13876867c8a544 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -13,12 +13,14 @@ public partial struct JSMarshalerArgument /// Helps with marshaling of the Task result or Function arguments. /// It's used by JSImport code generator and should not be used by developers in source code. /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToManagedCallback(ref JSMarshalerArgument arg, out T value); /// /// Helps with marshaling of the Task result or Function arguments. /// It's used by JSImport code generator and should not be used by developers in source code. /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToJSCallback(ref JSMarshalerArgument arg, T value); /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs index a8966dcaf521d0..79e9e3e0cecf50 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs @@ -1358,11 +1358,28 @@ public void JsExportException(Exception value, string clazz) [Fact] public void JsExportThrows() { - var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.Throw))); + var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport))); Assert.DoesNotContain("Unexpected error", ex.Message); Assert.Contains("-t-e-s-t-", ex.Message); } + [Fact] + public void JsExportCatchToString() + { + var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.DoesNotContain("Unexpected error", toString); + Assert.Contains("-t-e-s-t-", toString); + Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString); + } + + [Fact] + public void JsExportCatchStack() + { + var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack); + Assert.Contains("catch1stack", stack); + } + #endregion Exception #region JSObject @@ -1906,12 +1923,12 @@ private void JsImportTest(T value var exThrow0 = Assert.Throws(() => JavaScriptTestHelper.throw0()); Assert.Contains("throw-0-msg", exThrow0.Message); Assert.DoesNotContain(" at ", exThrow0.Message); - Assert.Contains(" at Module.throw0", exThrow0.StackTrace); + Assert.Contains("throw0fn", exThrow0.StackTrace); var exThrow1 = Assert.Throws(() => throw1(value)); Assert.Contains("throw1-msg", exThrow1.Message); Assert.DoesNotContain(" at ", exThrow1.Message); - Assert.Contains(" at Module.throw1", exThrow1.StackTrace); + Assert.Contains("throw1fn", exThrow1.StackTrace); // anything is a system.object, sometimes it would be JSObject wrapper if (typeof(T).IsPrimitive) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index b5a89b7f48fe98..67fe77cfe5fe80 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -32,8 +32,14 @@ public static void ConsoleWriteLine([JSMarshalAs] string message) Console.WriteLine(message); } + [JSImport("catch1toString", "JavaScriptTestHelper")] + public static partial string catch1toString(string message, string functionName); + + [JSImport("catch1stack", "JavaScriptTestHelper")] + public static partial string catch1stack(string message, string functionName); + [JSExport] - public static void Throw(string message) + public static void ThrowFromJSExport(string message) { throw new ArgumentException(message); } @@ -57,7 +63,7 @@ public static DateTime Now() [return: JSMarshalAs] internal static partial string getClass1(); - [JSImport("throw0", "JavaScriptTestHelper")] + [JSImport("throw0fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial void throw0(); @@ -215,7 +221,7 @@ internal static partial void Relaxed(string a1, Exception ex, [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int32([JSMarshalAs] int value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int throw1_Int32([JSMarshalAs] int value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -241,7 +247,7 @@ public static int EchoInt32([JSMarshalAs] int arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_String([JSMarshalAs] string value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial string throw1_String([JSMarshalAs] string value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -273,7 +279,7 @@ public static string EchoString([JSMarshalAs] string arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Object([JSMarshalAs] object value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial object throw1_Object([JSMarshalAs] object value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -299,7 +305,7 @@ public static object EchoObject([JSMarshalAs] object arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Exception([JSMarshalAs] Exception value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial Exception throw1_Exception([JSMarshalAs] Exception value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -431,7 +437,7 @@ public static Func BackFuncOfIntInt([JSMarshalAs] internal static partial bool identity1_Boolean([JSMarshalAs] bool value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool throw1_Boolean([JSMarshalAs] bool value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -458,7 +464,7 @@ public static bool EchoBoolean([JSMarshalAs] bool arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Char([JSMarshalAs] char value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial char throw1_Char([JSMarshalAs] char value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -484,7 +490,7 @@ public static char EchoChar([JSMarshalAs] char arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Byte([JSMarshalAs] byte value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial byte throw1_Byte([JSMarshalAs] byte value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -510,7 +516,7 @@ public static byte EchoByte([JSMarshalAs] byte arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int16([JSMarshalAs] short value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial short throw1_Int16([JSMarshalAs] short value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -536,7 +542,7 @@ public static short EchoInt16([JSMarshalAs] short arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int52([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_Int52([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -562,7 +568,7 @@ public static long EchoInt52([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_BigInt64([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_BigInt64([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -588,7 +594,7 @@ public static long EchoBigInt64([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Double([JSMarshalAs] double value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double throw1_Double([JSMarshalAs] double value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -614,7 +620,7 @@ public static double EchoDouble([JSMarshalAs] double arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Single([JSMarshalAs] float value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial float throw1_Single([JSMarshalAs] float value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -640,7 +646,7 @@ public static float EchoSingle([JSMarshalAs] float arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_IntPtr([JSMarshalAs] IntPtr value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr throw1_IntPtr([JSMarshalAs] IntPtr value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -667,7 +673,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial bool identity1_VoidPtr([JSMarshalAs] void* value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial void* throw1_VoidPtr([JSMarshalAs] void* value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -693,7 +699,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTime([JSMarshalAs] DateTime value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime throw1_DateTime([JSMarshalAs] DateTime value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -719,7 +725,7 @@ public static DateTime EchoDateTime([JSMarshalAs] DateTime arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTimeOffset throw1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -746,7 +752,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBoolean([JSMarshalAs] bool? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool? throw1_NullableBoolean([JSMarshalAs] bool? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -773,7 +779,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableInt32([JSMarshalAs] int? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int? throw1_NullableInt32([JSMarshalAs] int? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -800,7 +806,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBigInt64([JSMarshalAs] long? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long? throw1_NullableBigInt64([JSMarshalAs] long? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -827,7 +833,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableIntPtr([JSMarshalAs] IntPtr? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr? throw1_NullableIntPtr([JSMarshalAs] IntPtr? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -854,7 +860,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDouble([JSMarshalAs] double? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double? throw1_NullableDouble([JSMarshalAs] double? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -881,7 +887,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDateTime([JSMarshalAs] DateTime? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime? throw1_NullableDateTime([JSMarshalAs] DateTime? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -907,7 +913,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_JSObject([JSMarshalAs] JSObject value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial JSObject throw1_JSObject([JSMarshalAs] JSObject value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -1236,4 +1242,4 @@ public partial record struct NestedRecordStruct [System.Runtime.InteropServices.JavaScript.JSExport] public static string EchoString(string message) => message + "85"; } -} \ No newline at end of file +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs index 314e715e16b815..0811be477c64f6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs @@ -105,12 +105,34 @@ export function retrieve1() { return val; } -export function throw0() { +export function throw0fn() { //console.log(`throw0()`) throw new Error('throw-0-msg'); } -export function throw1(arg1) { +export function catch1toString(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.toString(); + } +} + +export function catch1stack(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.stack; + } +} + +export function throw1fn(arg1) { //console.log(`throw1(arg1:${arg1 !== null ? arg1 : ''})`) throw new Error('throw1-msg ' + arg1); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs index 20400c4721369e..3fea4b0fae2e21 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs @@ -33,7 +33,17 @@ public static Location FindTypeExpressionOrNullLocation(this AttributeArgumentSy switch (attributeTarget.Identifier.Kind()) { case SyntaxKind.ReturnKeyword: - return ((IMethodSymbol)targetSymbol).GetReturnTypeAttributes().First(attributeSyntaxLocationMatches); + if (targetSymbol is IMethodSymbol method) + { + // Sometimes an attribute is put on a symbol that is nested within the containing symbol. + // For example, the ContainingSymbol for an AttributeSyntax on a local function have a ContainingSymbol of the method. + // Since this method is internal and the callers don't care about attributes on local functions, + // we just allow this method to return null in those cases. + return method.GetReturnTypeAttributes().FirstOrDefault(attributeSyntaxLocationMatches); + } + // An attribute on the return value of a delegate type's Invoke method has a ContainingSymbol of the delegate type. + // We don't care about the attributes in this case for the callers, so we'll just return null. + return null; case SyntaxKind.AssemblyKeyword: return targetSymbol.ContainingAssembly.GetAttributes().First(attributeSyntaxLocationMatches); case SyntaxKind.ModuleKeyword: @@ -43,7 +53,8 @@ public static Location FindTypeExpressionOrNullLocation(this AttributeArgumentSy } } // Sometimes an attribute is put on a symbol that is nested within the containing symbol. - // For example, the ContainingSymbol for an AttributeSyntax on a parameter have a ContainingSymbol of the method. + // For example, the ContainingSymbol for an AttributeSyntax on a parameter have a ContainingSymbol of the method + // and an AttributeSyntax on a local function have a ContainingSymbol of the containing method. // Since this method is internal and the callers don't care about attributes on parameters, we just allow // this method to return null in those cases. return targetSymbol.GetAttributes().FirstOrDefault(attributeSyntaxLocationMatches); diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs index 582568718722b5..5b8769e19c9ee2 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs @@ -272,8 +272,18 @@ public class X { void Foo([MarshalAs(UnmanagedType.I4)] int i) { + [return:MarshalAs(UnmanagedType.I4)] + [SkipLocalsInit] + static int Local() + { + return 0; + } } } + + [return:MarshalAs(UnmanagedType.I4)] + delegate int Y(); + """; await VerifyCS.VerifyAnalyzerAsync(source); diff --git a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs index 015f36c8a979f7..acd7df3a9f8077 100644 --- a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs +++ b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs @@ -62,19 +62,27 @@ public async Task VerifyComputeHashAsync(int size) [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] public async Task ComputeHashAsync_SupportsCancellation() { - using (CancellationTokenSource cancellationSource = new CancellationTokenSource(100)) - using (PositionValueStream stream = new SlowPositionValueStream(10000)) + using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) + using (PositionValueStream stream = new SelfCancelingStream(10000, cancellationSource)) using (HashAlgorithm hash = new SummingTestHashAlgorithm()) { + // The stream has a length longer than ComputeHashAsync's read buffer, + // so ReadAsync will get called multiple times. + // The first call succeeds, but moves the cancellation source to canceled, + // and the second call then fails with an OperationCanceledException, canceling the + // whole operation. await Assert.ThrowsAnyAsync( () => hash.ComputeHashAsync(stream, cancellationSource.Token)); + + Assert.True(cancellationSource.IsCancellationRequested); } } [Fact] public void ComputeHashAsync_Disposed() { - using (PositionValueStream stream = new SlowPositionValueStream(10000)) + using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) + using (PositionValueStream stream = new SelfCancelingStream(10000, cancellationSource)) using (HashAlgorithm hash = new SummingTestHashAlgorithm()) { hash.Dispose(); @@ -85,6 +93,10 @@ public void ComputeHashAsync_Disposed() // Not returning or awaiting the Task, it never got created. hash.ComputeHashAsync(stream); }); + + // If SelfCancelingStream.Read (or ReadAsync) was called it will trip cancellation, + // so use that as a signal for whether or not the stream was ever read from. + Assert.False(cancellationSource.IsCancellationRequested, "Stream.Read was invoked"); } } @@ -123,15 +135,19 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize) // implementations by verifying the right value is produced. } - private class SlowPositionValueStream : PositionValueStream + private class SelfCancelingStream : PositionValueStream { - public SlowPositionValueStream(int totalCount) : base(totalCount) + private readonly CancellationTokenSource _cancellationSource; + + public SelfCancelingStream(int totalCount, CancellationTokenSource cancellationSource) + : base(totalCount) { + _cancellationSource = cancellationSource; } public override int Read(byte[] buffer, int offset, int count) { - System.Threading.Thread.Sleep(1000); + _cancellationSource.Cancel(throwOnFirstException: true); return base.Read(buffer, offset, count); } } diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index e17ce01c770357..8cd779f5a02188 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -36,6 +36,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs new file mode 100644 index 00000000000000..9c028f02165171 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json +{ + internal static class AppContextSwitchHelper + { + public static bool IsSourceGenReflectionFallbackEnabled => s_isSourceGenReflectionFallbackEnabled; + + private static readonly bool s_isSourceGenReflectionFallbackEnabled = + AppContext.TryGetSwitch( + switchName: "System.Text.Json.Serialization.EnableSourceGenReflectionFallback", + isEnabled: out bool value) + ? value : false; + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 596a9c37bae488..fa543671792fb0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -628,7 +628,20 @@ internal void InitializeForReflectionSerializer() // Even if a resolver has already been specified, we need to root // the default resolver to gain access to the default converters. DefaultJsonTypeInfoResolver defaultResolver = DefaultJsonTypeInfoResolver.RootDefaultInstance(); - _typeInfoResolver ??= defaultResolver; + + switch (_typeInfoResolver) + { + case null: + // Use the default reflection-based resolver if no resolver has been specified. + _typeInfoResolver = defaultResolver; + break; + + case JsonSerializerContext ctx when AppContextSwitchHelper.IsSourceGenReflectionFallbackEnabled: + // .NET 6 compatibility mode: enable fallback to reflection metadata for JsonSerializerContext + _effectiveJsonTypeInfoResolver = JsonTypeInfoResolver.Combine(ctx, defaultResolver); + break; + } + IsImmutable = true; _isInitializedForReflectionSerializer = true; } @@ -636,6 +649,9 @@ internal void InitializeForReflectionSerializer() internal bool IsInitializedForReflectionSerializer => _isInitializedForReflectionSerializer; private volatile bool _isInitializedForReflectionSerializer; + // Only populated in .NET 6 compatibility mode encoding reflection fallback in source gen + private IJsonTypeInfoResolver? _effectiveJsonTypeInfoResolver; + internal void InitializeForMetadataGeneration() { if (_typeInfoResolver is null) @@ -648,7 +664,7 @@ internal void InitializeForMetadataGeneration() private JsonTypeInfo? GetTypeInfoNoCaching(Type type) { - JsonTypeInfo? info = _typeInfoResolver?.GetTypeInfo(type, this); + JsonTypeInfo? info = (_effectiveJsonTypeInfoResolver ?? _typeInfoResolver)?.GetTypeInfo(type, this); if (info != null) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index a91467a9943a3e..05cd443bccf5ac 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -437,9 +437,18 @@ public static void Options_JsonSerializerContext_DoesNotFallbackToReflection() } [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToReflectionConverter() + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(false)] + [InlineData(true)] + public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToReflectionConverter(bool isCompatibilitySwitchExplicitlyDisabled) { + var options = new RemoteInvokeOptions(); + + if (isCompatibilitySwitchExplicitlyDisabled) + { + options.RuntimeConfigurationOptions.Add("System.Text.Json.Serialization.EnableSourceGenReflectionFallback", false); + } + RemoteExecutor.Invoke(static () => { JsonContext context = JsonContext.Default; @@ -460,7 +469,40 @@ public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToR Assert.Throws(() => context.Options.GetConverter(typeof(MyClass))); Assert.Throws(() => JsonSerializer.Serialize(unsupportedValue, context.Options)); - }).Dispose(); + }, options).Dispose(); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void Options_JsonSerializerContext_Net6CompatibilitySwitch_FallsBackToReflectionResolver() + { + var options = new RemoteInvokeOptions + { + RuntimeConfigurationOptions = + { + ["System.Text.Json.Serialization.EnableSourceGenReflectionFallback"] = true + } + }; + + RemoteExecutor.Invoke(static () => + { + var unsupportedValue = new MyClass { Value = "value" }; + + // JsonSerializerContext does not return metadata for the type + Assert.Null(JsonContext.Default.GetTypeInfo(typeof(MyClass))); + + // Serialization fails using the JsonSerializerContext overload + Assert.Throws(() => JsonSerializer.Serialize(unsupportedValue, unsupportedValue.GetType(), JsonContext.Default)); + + // Serialization uses reflection fallback using the JsonSerializerOptions overload + string json = JsonSerializer.Serialize(unsupportedValue, JsonContext.Default.Options); + JsonTestHelper.AssertJsonEqual("""{"Value":"value", "Thing":null}""", json); + + // A converter can be resolved when looking up JsonSerializerOptions + JsonConverter converter = JsonContext.Default.Options.GetConverter(typeof(MyClass)); + Assert.IsAssignableFrom>(converter); + + }, options).Dispose(); } [Fact] diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs index 24f4d8522cb845..38c6540b812d6c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs @@ -238,6 +238,9 @@ public static async Task ExpandFieldValues( JObject fieldValue = await ReadFieldValue(sdbHelper, retDebuggerCmdReader, field, id.Value, typeInfo, valtype, isOwn, parentTypeId, getCommandOptions, token); numFieldsRead++; + if (typeInfo.Info.IsNonUserCode && getCommandOptions.HasFlag(GetObjectCommandOptions.JustMyCode) && field.Attributes.HasFlag(FieldAttributes.Private)) + continue; + if (!Enum.TryParse(fieldValue["__state"].Value(), out DebuggerBrowsableState fieldState) || fieldState == DebuggerBrowsableState.Collapsed) { @@ -311,7 +314,7 @@ public static async Task> ExpandPropertyValues( int typeId, string typeName, ArraySegment getterParamsBuffer, - bool isAutoExpandable, + GetObjectCommandOptions getCommandOptions, DotnetObjectId objectId, bool isValueType, bool isOwn, @@ -347,6 +350,10 @@ public static async Task> ExpandPropertyValues( MethodAttributes getterAttrs = getterInfo.Info.Attributes; MethodAttributes getterMemberAccessAttrs = getterAttrs & MethodAttributes.MemberAccessMask; MethodAttributes vtableLayout = getterAttrs & MethodAttributes.VtableLayoutMask; + + if (typeInfo.Info.IsNonUserCode && getCommandOptions.HasFlag(GetObjectCommandOptions.JustMyCode) && getterMemberAccessAttrs == MethodAttributes.Private) + continue; + bool isNewSlot = (vtableLayout & MethodAttributes.NewSlot) == MethodAttributes.NewSlot; typePropertiesBrowsableInfo.TryGetValue(propName, out DebuggerBrowsableState? state); @@ -454,7 +461,7 @@ async Task AddProperty( { string returnTypeName = await sdbHelper.GetReturnType(getMethodId, token); JObject propRet = null; - if (isAutoExpandable || (state is DebuggerBrowsableState.RootHidden && IsACollectionType(returnTypeName))) + if (getCommandOptions.HasFlag(GetObjectCommandOptions.AutoExpandable) || getCommandOptions.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute) || (state is DebuggerBrowsableState.RootHidden && IsACollectionType(returnTypeName))) { try { @@ -568,10 +575,6 @@ public static async Task GetObjectMemberValues( for (int i = 0; i < typeIdsCnt; i++) { int typeId = typeIdsIncludingParents[i]; - var typeInfo = await sdbHelper.GetTypeInfo(typeId, token); - - if (typeInfo.Info.IsNonUserCode && getCommandType.HasFlag(GetObjectCommandOptions.JustMyCode)) - continue; int parentTypeId = i + 1 < typeIdsCnt ? typeIdsIncludingParents[i + 1] : -1; string typeName = await sdbHelper.GetTypeName(typeId, token); @@ -604,7 +607,7 @@ public static async Task GetObjectMemberValues( typeId, typeName, getPropertiesParamBuffer, - getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), + getCommandType, id, isValueType: false, isOwn, diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 8ff9f8ae9af99e..fb17d2e72453b3 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -60,7 +60,8 @@ internal enum GetObjectCommandOptions ForDebuggerProxyAttribute = 8, ForDebuggerDisplayAttribute = 16, WithProperties = 32, - JustMyCode = 64 + JustMyCode = 64, + AutoExpandable = 128 } internal enum CommandSet { diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs index c2a2513f00ba19..07b2036caacb3b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs @@ -293,7 +293,7 @@ public async Task ExpandPropertyValues(MonoSDBHelper sdbHelper, bool splitMember typeId, className, Buffer, - autoExpand, + autoExpand ? GetObjectCommandOptions.AutoExpandable : GetObjectCommandOptions.None, Id, isValueType: true, isOwn: i == 0, diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 9973811a56e499..e05f4d682db641 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -1067,7 +1067,11 @@ await EvaluateAndCheck( { myField = TNumber(0), myField2 = TNumber(0), - }, "this_props", num_fields: 2); + propB = TGetter("propB"), + propC = TGetter("propC"), + e = TNumber(50), + f = TNumber(60), + }, "this_props", num_fields: 6); } else { diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs index af11a6329d0d26..1626d47d5d4e1f 100644 --- a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs @@ -16,11 +16,11 @@ public class ClassNonUserCodeToInheritThatInheritsFromNormalClass : NormalClass private int d; public int e; protected int f; - public int G + private int G { get {return f + 1;} } - public int H => f; + private int H => f; public ClassNonUserCodeToInheritThatInheritsFromNormalClass() { diff --git a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs index 5dcdc29f93f2d0..4899b8869ef402 100644 --- a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs @@ -10,11 +10,11 @@ public class ClassWithoutDebugSymbolsToInherit private int d; public int e; protected int f; - public int G + private int G { get {return f + 1;} } - public int H => f; + private int H => f; public ClassWithoutDebugSymbolsToInherit() { diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index b8f52c788f0d2b..73a225cf5e16c4 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -1300,11 +1300,11 @@ public class ClassNonUserCodeToInherit private int d; public int e; protected int f; - public int G + private int G { get {return f + 1;} } - public int H => f; + private int H => f; public ClassNonUserCodeToInherit() { diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts index 2d40d7c4d20e29..cdb2d00dba723d 100644 --- a/src/mono/wasm/runtime/logging.ts +++ b/src/mono/wasm/runtime/logging.ts @@ -62,8 +62,9 @@ export function mono_wasm_symbolicate_string(message: string): string { export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string { let errObj: any = err; - if (!(err instanceof Error)) - errObj = new Error(err); + if (!(errObj instanceof Error)) { + errObj = new Error(errObj); + } // Error return mono_wasm_symbolicate_string(errObj.stack); diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts index 09f6d9a33d0241..65186e4c0b270f 100644 --- a/src/mono/wasm/runtime/managed-exports.ts +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -7,7 +7,7 @@ import { Module, runtimeHelpers, ENVIRONMENT_IS_PTHREAD } from "./imports"; import { alloc_stack_frame, get_arg, get_arg_gc_handle, MarshalerType, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; -import { marshal_int32_to_js, marshal_task_to_js } from "./marshal-to-js"; +import { marshal_int32_to_js, marshal_string_to_js, marshal_task_to_js } from "./marshal-to-js"; export function init_managed_exports(): void { const anyModule = Module as any; @@ -34,6 +34,9 @@ export function init_managed_exports(): void { mono_assert(complete_task_method, "Can't find CompleteTask method"); const call_delegate_method = get_method("CallDelegate"); mono_assert(call_delegate_method, "Can't find CallDelegate method"); + const get_managed_stack_trace_method = get_method("GetManagedStackTrace"); + mono_assert(get_managed_stack_trace_method, "Can't find GetManagedStackTrace method"); + runtimeHelpers.javaScriptExports.call_entry_point = (entry_point: MonoMethod, program_args?: string[]) => { const sp = anyModule.stackSave(); try { @@ -134,6 +137,22 @@ export function init_managed_exports(): void { anyModule.stackRestore(sp); } }; + runtimeHelpers.javaScriptExports.get_managed_stack_trace = (exception_gc_handle: GCHandle) => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(3); + + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Exception); + set_gc_handle(arg1, exception_gc_handle); + + invoke_method_and_handle_exception(get_managed_stack_trace_method, args); + const res = get_arg(args, 1); + return marshal_string_to_js(res); + } finally { + anyModule.stackRestore(sp); + } + }; if (install_sync_context) { runtimeHelpers.javaScriptExports.install_synchronization_context = () => { diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts index 318d10580ffec0..aac0155406c4ec 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -32,7 +32,7 @@ export function initialize_marshalers_to_js(): void { cs_to_js_marshalers.set(MarshalerType.Single, _marshal_float_to_js); cs_to_js_marshalers.set(MarshalerType.IntPtr, _marshal_intptr_to_js); cs_to_js_marshalers.set(MarshalerType.Double, _marshal_double_to_js); - cs_to_js_marshalers.set(MarshalerType.String, _marshal_string_to_js); + cs_to_js_marshalers.set(MarshalerType.String, marshal_string_to_js); cs_to_js_marshalers.set(MarshalerType.Exception, marshal_exception_to_js); cs_to_js_marshalers.set(MarshalerType.JSException, marshal_exception_to_js); cs_to_js_marshalers.set(MarshalerType.JSObject, _marshal_js_object_to_js); @@ -353,7 +353,7 @@ export function mono_wasm_marshal_promise(args: JSMarshalerArguments): void { set_arg_type(exc, MarshalerType.None); } -function _marshal_string_to_js(arg: JSMarshalerArgument): string | null { +export function marshal_string_to_js(arg: JSMarshalerArgument): string | null { const type = get_arg_type(arg); if (type == MarshalerType.None) { return null; @@ -383,7 +383,7 @@ export function marshal_exception_to_js(arg: JSMarshalerArgument): Error | null let result = _lookup_js_owned_object(gc_handle); if (result === null || result === undefined) { // this will create new ManagedError - const message = _marshal_string_to_js(arg); + const message = marshal_string_to_js(arg); result = new ManagedError(message!); setup_managed_proxy(result, gc_handle); @@ -462,7 +462,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh result = new Array(length); for (let index = 0; index < length; index++) { const element_arg = get_arg(buffer_ptr, index); - result[index] = _marshal_string_to_js(element_arg); + result[index] = marshal_string_to_js(element_arg); } cwraps.mono_wasm_deregister_root(buffer_ptr); } diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 77bec6b6aa762f..f20a620c5fb76b 100644 --- a/src/mono/wasm/runtime/marshal.ts +++ b/src/mono/wasm/runtime/marshal.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; -import { Module } from "./imports"; +import { Module, runtimeHelpers } from "./imports"; import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot } from "./types"; @@ -316,13 +316,31 @@ export class ManagedObject implements IDisposable { } export class ManagedError extends Error implements IDisposable { + private superStack: any; constructor(message: string) { super(message); + this.superStack = Object.getOwnPropertyDescriptor(this, "stack"); // this works on Chrome + Object.defineProperty(this, "stack", { + get: this.getManageStack, + }); } - get stack(): string | undefined { - //todo implement lazy managed stack strace from this[js_owned_gc_handle_symbol]! - return super.stack; + getSuperStack() { + if (this.superStack) { + return this.superStack.value; + } + return super.stack; // this works on FF + } + + getManageStack() { + const gc_handle = (this)[js_owned_gc_handle_symbol]; + if (gc_handle) { + const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); + if (managed_stack) { + return managed_stack + "\n" + this.getSuperStack(); + } + } + return this.getSuperStack(); } dispose(): void { @@ -332,10 +350,6 @@ export class ManagedError extends Error implements IDisposable { get isDisposed(): boolean { return (this)[js_owned_gc_handle_symbol] === GCHandleNull; } - - toString(): string { - return `ManagedError(gc_handle: ${(this)[js_owned_gc_handle_symbol]})`; - } } export function get_signature_marshaler(signature: JSFunctionSignature, index: number): JSHandle { diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index ea01f16a9e5c85..3664532e54c5e9 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -411,6 +411,9 @@ export interface JavaScriptExports { // the marshaled signature is: void InstallSynchronizationContext() install_synchronization_context(): void; + + // the marshaled signature is: string GetManagedStackTrace(GCHandle exception) + get_managed_stack_trace(exception_gc_handle: GCHandle): string | null } export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs) => any; diff --git a/src/mono/wasm/templates/templates/browser/README.md b/src/mono/wasm/templates/templates/browser/README.md index f3c4ddce82ffc9..8e1339897bef46 100644 --- a/src/mono/wasm/templates/templates/browser/README.md +++ b/src/mono/wasm/templates/templates/browser/README.md @@ -2,20 +2,20 @@ ## Build -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet build -c Debug/Release -r browser-wasm +dotnet build -c Debug/Release ``` After building the application, the result is in the `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory. ## Run -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet run -c Debug/Release -r browser-wasm +dotnet run -c Debug/Release ``` Or you can start any static file server from the AppBundle directory diff --git a/src/mono/wasm/templates/templates/console/README.md b/src/mono/wasm/templates/templates/console/README.md index a3b8dbc660e5d0..84fd14b27897e2 100644 --- a/src/mono/wasm/templates/templates/console/README.md +++ b/src/mono/wasm/templates/templates/console/README.md @@ -2,20 +2,20 @@ ## Build -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet build -c Debug/Release -r browser-wasm +dotnet build -c Debug/Release ``` After building the application, the result is in the `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory. ## Run -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet run -c Debug/Release -r browser-wasm -h=nodejs +dotnet run -c Debug/Release ``` Or you can start any static file server from the AppBundle directory diff --git a/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs new file mode 100644 index 00000000000000..415d9b00f5c8a6 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +public unsafe class ImageSharp_2117 +{ + public static int Main(string[] args) + { + if (Sse.IsSupported) + { + Vector128 fnan = Vector128.Create(float.NaN); + Vector128 res1 = Sse.Max(Sse.LoadVector128((float*)(&fnan)), Vector128.Zero); + Vector128 res2 = Sse.Min(Sse.LoadVector128((float*)(&fnan)), Vector128.Zero); + + if (float.IsNaN(res1[0]) || float.IsNaN(res2[0])) + { + return 0; + } + } + + if (Sse2.IsSupported) + { + Vector128 dnan = Vector128.Create(double.NaN); + Vector128 res3 = Sse2.Max(Sse2.LoadVector128((double*)(&dnan)), Vector128.Zero); + Vector128 res4 = Sse2.Min(Sse2.LoadVector128((double*)(&dnan)), Vector128.Zero); + + if (double.IsNaN(res3[0]) || double.IsNaN(res4[0])) + { + return 0; + } + } + + return 100; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj new file mode 100644 index 00000000000000..e7284d26ab2f18 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj @@ -0,0 +1,13 @@ + + + Exe + True + + + None + True + + + + +