From 5c4f9a2cc7225afdd69eeaf293ef000fcce186ad Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 18 Jul 2023 05:29:56 -0700 Subject: [PATCH 1/7] First --- .../UnsafeAccessors/UnsafeAccessorsTests.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 5077691409fe0b..e6d1f461d9c100 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -373,16 +373,23 @@ public static void Verify_ManagedUnmanagedFunctionPointersDontMatch() extern static string CallManagedMethod(UserDataClass d, delegate* unmanaged[Cdecl] fptr); } - class InheritanceBase + abstract class InheritanceBase { private static string OnBase() => nameof(OnBase); private static string FieldOnBase = nameof(FieldOnBase); + protected abstract string Abstract(); + protected virtual string Virtual() => $"{nameof(InheritanceBase)}.{nameof(Virtual)}"; + protected virtual string NewVirtual() => $"{nameof(InheritanceBase)}.{nameof(NewVirtual)}"; + protected virtual string BaseVirtual() => $"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}"; } class InheritanceDerived : InheritanceBase { private static string OnDerived() => nameof(OnDerived); private static string FieldOnDerived = nameof(FieldOnDerived); + protected override string Abstract() => $"{nameof(InheritanceDerived)}.{nameof(Abstract)}"; + protected override string Virtual() => $"{nameof(InheritanceDerived)}.{nameof(Virtual)}"; + protected new virtual string NewVirtual() => $"{nameof(InheritanceDerived)}.{nameof(NewVirtual)}"; } [Fact] @@ -393,12 +400,28 @@ public static void Verify_InheritanceMethodResolution() var instance = new InheritanceDerived(); Assert.Throws(() => OnBase(instance)); Assert.Equal(nameof(OnDerived), OnDerived(instance)); + Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Abstract)}", Abstract(instance)); + Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Virtual)}", Virtual(instance)); + Assert.Equal($"{nameof(InheritanceBase)}.{nameof(NewVirtual)}", NewVirtual(instance)); + Assert.Equal($"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}", BaseVirtual(instance)); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnBase))] extern static string OnBase(InheritanceDerived i); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnDerived))] extern static string OnDerived(InheritanceDerived i); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(Abstract))] + extern static string Abstract(InheritanceBase target); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(Virtual))] + extern static string Virtual(InheritanceBase target); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NewVirtual))] + extern static string NewVirtual(InheritanceBase target); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(BaseVirtual))] + extern static string BaseVirtual(InheritanceDerived target); } [Fact] From cbcd25e86c90ce00415c75858441057f864f321b Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:11:29 -0700 Subject: [PATCH 2/7] Disable on mono --- .../UnsafeAccessors/UnsafeAccessorsTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index e6d1f461d9c100..c595ee0657c627 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -403,7 +403,6 @@ public static void Verify_InheritanceMethodResolution() Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Abstract)}", Abstract(instance)); Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Virtual)}", Virtual(instance)); Assert.Equal($"{nameof(InheritanceBase)}.{nameof(NewVirtual)}", NewVirtual(instance)); - Assert.Equal($"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}", BaseVirtual(instance)); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnBase))] extern static string OnBase(InheritanceDerived i); @@ -419,6 +418,15 @@ public static void Verify_InheritanceMethodResolution() [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(NewVirtual))] extern static string NewVirtual(InheritanceBase target); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] + public static void Verify_InheritanceMethodResolution_BaseViaDerived() + { + var instance = new InheritanceDerived(); + + Assert.Equal($"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}", BaseVirtual(instance)); [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(BaseVirtual))] extern static string BaseVirtual(InheritanceDerived target); From bfaa458c70d9edc8f93bbc1c80a86411cee1b1fd Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:16:58 -0700 Subject: [PATCH 3/7] Disable the problematic test everywhere for now/ --- .../compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index c595ee0657c627..2fedaaefb1d202 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -421,7 +421,7 @@ public static void Verify_InheritanceMethodResolution() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/89212")] // This passes on CoreCLR, but fails on Mono and NativeAOT public static void Verify_InheritanceMethodResolution_BaseViaDerived() { var instance = new InheritanceDerived(); From 6a6b2c1dc30bbee76ec20a4067d99112dfb8eb40 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 19 Jul 2023 16:08:27 -0700 Subject: [PATCH 4/7] Fix NativeAOT --- .../TypeSystem/Ecma/EffectiveVisibility.cs | 12 +++ .../Common/TypeSystem/IL/UnsafeAccessors.cs | 78 ++++++++++++------- .../UnsafeAccessors/UnsafeAccessorsTests.cs | 8 +- 3 files changed, 68 insertions(+), 30 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs index 88f708d5d971f6..0483c570e95382 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs @@ -126,6 +126,18 @@ public static EffectiveVisibility GetEffectiveVisibility(this EcmaMethod method) return visibility; } + public static EffectiveVisibility GetAttributeEffectiveVisibility(this MethodDesc method) + { + if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) + { + return ecmaMethod.Attributes.ToEffectiveVisibility(); + } + else + { + return EffectiveVisibility.Public; + } + } + public static EffectiveVisibility GetEffectiveVisibility(this EcmaType type) { EffectiveVisibility visibility = type.Attributes.ToEffectiveVisibility(); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs b/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs index e9c9b526213693..4aef0131e2a4dc 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs @@ -372,45 +372,67 @@ private static bool TrySetTargetMethod(ref GenerationContext context, string nam { TypeDesc targetType = context.TargetType; + bool skipPrivate = false; MethodDesc targetMaybe = null; - foreach (MethodDesc md in targetType.GetMethods()) + do { - // Check the target and current method match static/instance state. - if (context.IsTargetStatic != md.Signature.IsStatic) + foreach (MethodDesc md in targetType.GetMethods()) { - continue; - } + // Respect private visibility for inheritance scenarios. + if (skipPrivate + && md.GetAttributeEffectiveVisibility() == EffectiveVisibility.Private) + { + continue; + } - // Check for matching name - if (!md.Name.Equals(name)) - { - continue; - } + // Check the target and current method match static/instance state. + if (context.IsTargetStatic != md.Signature.IsStatic) + { + continue; + } - // Check signature - if (!DoesMethodMatchUnsafeAccessorDeclaration(ref context, md, ignoreCustomModifiers)) - { - continue; - } + // Check for matching name + if (!md.Name.Equals(name)) + { + continue; + } - // Check if there is some ambiguity. - if (targetMaybe != null) - { - if (ignoreCustomModifiers) + // Check signature + if (!DoesMethodMatchUnsafeAccessorDeclaration(ref context, md, ignoreCustomModifiers)) { - // We have detected ambiguity when ignoring custom modifiers. - // Start over, but look for a match requiring custom modifiers - // to match precisely. - if (TrySetTargetMethod(ref context, name, out isAmbiguous, ignoreCustomModifiers: false)) - return true; + continue; } - isAmbiguous = true; - return false; + // Check if there is some ambiguity. + if (targetMaybe != null) + { + if (ignoreCustomModifiers) + { + // We have detected ambiguity when ignoring custom modifiers. + // Start over, but look for a match requiring custom modifiers + // to match precisely. + if (TrySetTargetMethod(ref context, name, out isAmbiguous, ignoreCustomModifiers: false)) + return true; + } + + isAmbiguous = true; + return false; + } + + targetMaybe = md; } - targetMaybe = md; - } + // Ignore base class constructors as they can be ambiguous. + if (targetMaybe != null && targetMaybe.IsConstructor) + { + break; + } + + // After the most derived type, we skip private members to simulate + // .NET OOP visibility rules. + skipPrivate = true; + targetType = targetType.BaseType; + } while (targetType != null); isAmbiguous = false; context.TargetMethod = targetMaybe; diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 2fedaaefb1d202..543997c915055f 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -421,9 +421,11 @@ public static void Verify_InheritanceMethodResolution() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/89212")] // This passes on CoreCLR, but fails on Mono and NativeAOT + [ActiveIssue("https://github.com/dotnet/runtime/issues/89212", TestRuntimes.Mono)] public static void Verify_InheritanceMethodResolution_BaseViaDerived() { + Console.WriteLine($"Running {nameof(Verify_InheritanceMethodResolution_BaseViaDerived)}"); + var instance = new InheritanceDerived(); Assert.Equal($"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}", BaseVirtual(instance)); @@ -467,7 +469,7 @@ public static void Verify_InvalidTargetUnsafeAccessor() () => FieldNotFound(null)); AssertExtensions.ThrowsMissingMemberException( isNativeAot ? null : UserDataClass.StaticFieldName, - () => + () => { UserDataValue value = default; FieldNotFoundStaticMismatch1(ref value); @@ -520,6 +522,8 @@ public static void Verify_InvalidTargetUnsafeAccessor() [ActiveIssue("https://github.com/dotnet/runtime/issues/86040", TestRuntimes.Mono)] public static void Verify_InvalidTargetUnsafeAccessorAmbiguousMatch() { + Console.WriteLine($"Running {nameof(Verify_InvalidTargetUnsafeAccessorAmbiguousMatch)}"); + Assert.Throws( () => CallAmbiguousMethod(CallPrivateConstructorClass(), null)); From 2a7e7e56099962ff87adb435250d0438c55ba73d Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Jul 2023 07:38:22 -0700 Subject: [PATCH 5/7] UnsafeAccessors are limited to lookup on the defined typed. --- .../TypeSystem/Ecma/EffectiveVisibility.cs | 12 --- .../Common/TypeSystem/IL/UnsafeAccessors.cs | 78 +++++++------------ src/coreclr/vm/prestub.cpp | 11 ++- .../UnsafeAccessors/UnsafeAccessorsTests.cs | 18 +---- 4 files changed, 37 insertions(+), 82 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs index 0483c570e95382..88f708d5d971f6 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EffectiveVisibility.cs @@ -126,18 +126,6 @@ public static EffectiveVisibility GetEffectiveVisibility(this EcmaMethod method) return visibility; } - public static EffectiveVisibility GetAttributeEffectiveVisibility(this MethodDesc method) - { - if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) - { - return ecmaMethod.Attributes.ToEffectiveVisibility(); - } - else - { - return EffectiveVisibility.Public; - } - } - public static EffectiveVisibility GetEffectiveVisibility(this EcmaType type) { EffectiveVisibility visibility = type.Attributes.ToEffectiveVisibility(); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs b/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs index 4aef0131e2a4dc..e9c9b526213693 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/UnsafeAccessors.cs @@ -372,67 +372,45 @@ private static bool TrySetTargetMethod(ref GenerationContext context, string nam { TypeDesc targetType = context.TargetType; - bool skipPrivate = false; MethodDesc targetMaybe = null; - do + foreach (MethodDesc md in targetType.GetMethods()) { - foreach (MethodDesc md in targetType.GetMethods()) + // Check the target and current method match static/instance state. + if (context.IsTargetStatic != md.Signature.IsStatic) { - // Respect private visibility for inheritance scenarios. - if (skipPrivate - && md.GetAttributeEffectiveVisibility() == EffectiveVisibility.Private) - { - continue; - } - - // Check the target and current method match static/instance state. - if (context.IsTargetStatic != md.Signature.IsStatic) - { - continue; - } + continue; + } - // Check for matching name - if (!md.Name.Equals(name)) - { - continue; - } + // Check for matching name + if (!md.Name.Equals(name)) + { + continue; + } - // Check signature - if (!DoesMethodMatchUnsafeAccessorDeclaration(ref context, md, ignoreCustomModifiers)) - { - continue; - } + // Check signature + if (!DoesMethodMatchUnsafeAccessorDeclaration(ref context, md, ignoreCustomModifiers)) + { + continue; + } - // Check if there is some ambiguity. - if (targetMaybe != null) + // Check if there is some ambiguity. + if (targetMaybe != null) + { + if (ignoreCustomModifiers) { - if (ignoreCustomModifiers) - { - // We have detected ambiguity when ignoring custom modifiers. - // Start over, but look for a match requiring custom modifiers - // to match precisely. - if (TrySetTargetMethod(ref context, name, out isAmbiguous, ignoreCustomModifiers: false)) - return true; - } - - isAmbiguous = true; - return false; + // We have detected ambiguity when ignoring custom modifiers. + // Start over, but look for a match requiring custom modifiers + // to match precisely. + if (TrySetTargetMethod(ref context, name, out isAmbiguous, ignoreCustomModifiers: false)) + return true; } - targetMaybe = md; - } - - // Ignore base class constructors as they can be ambiguous. - if (targetMaybe != null && targetMaybe.IsConstructor) - { - break; + isAmbiguous = true; + return false; } - // After the most derived type, we skip private members to simulate - // .NET OOP visibility rules. - skipPrivate = true; - targetType = targetType.BaseType; - } while (targetType != null); + targetMaybe = md; + } isAmbiguous = false; context.TargetMethod = targetMaybe; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 014d06c000b396..174e48565f31b8 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1266,13 +1266,12 @@ namespace MethodDesc* targetMaybe = NULL; - // Following the iteration pattern found in MemberLoader::FindMethod(). - // Reverse order is recommended - see comments in MemberLoader::FindMethod(). - MethodTable::MethodIterator iter(targetType.AsMethodTable()); - iter.MoveToEnd(); - for (; iter.IsValid(); iter.Prev()) + // Following a similar iteration pattern found in MemberLoader::FindMethod(). + // However, we are only operating on the current type not walking the type hierarchy. + MethodTable::IntroducedMethodIterator iter(targetType.AsMethodTable()); + for (; iter.IsValid(); iter.Next()) { - MethodDesc* curr = iter.GetDeclMethodDesc(); + MethodDesc* curr = iter.GetMethodDesc(); // Check the target and current method match static/instance state. if (cxt.IsTargetStatic != (!!curr->IsStatic())) diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 543997c915055f..9a73f1b0de3b93 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -399,6 +399,7 @@ public static void Verify_InheritanceMethodResolution() var instance = new InheritanceDerived(); Assert.Throws(() => OnBase(instance)); + Assert.Throws(() => BaseVirtual(instance)); Assert.Equal(nameof(OnDerived), OnDerived(instance)); Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Abstract)}", Abstract(instance)); Assert.Equal($"{nameof(InheritanceDerived)}.{nameof(Virtual)}", Virtual(instance)); @@ -407,6 +408,9 @@ public static void Verify_InheritanceMethodResolution() [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnBase))] extern static string OnBase(InheritanceDerived i); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(BaseVirtual))] + extern static string BaseVirtual(InheritanceDerived target); + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = nameof(OnDerived))] extern static string OnDerived(InheritanceDerived i); @@ -420,20 +424,6 @@ public static void Verify_InheritanceMethodResolution() extern static string NewVirtual(InheritanceBase target); } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/89212", TestRuntimes.Mono)] - public static void Verify_InheritanceMethodResolution_BaseViaDerived() - { - Console.WriteLine($"Running {nameof(Verify_InheritanceMethodResolution_BaseViaDerived)}"); - - var instance = new InheritanceDerived(); - - Assert.Equal($"{nameof(InheritanceBase)}.{nameof(BaseVirtual)}", BaseVirtual(instance)); - - [UnsafeAccessor(UnsafeAccessorKind.Method, Name = nameof(BaseVirtual))] - extern static string BaseVirtual(InheritanceDerived target); - } - [Fact] public static void Verify_InheritanceFieldResolution() { From a7528e314fd9797631e2a518f6b0a7a664aef908 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Jul 2023 07:44:40 -0700 Subject: [PATCH 6/7] Update documentation for attribute. --- .../System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs index a1cff0324264e4..476cb27fbf6eb1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/UnsafeAccessorAttribute.cs @@ -45,6 +45,8 @@ public enum UnsafeAccessorKind /// The runtime will try to find the matching method or field and forward the call /// to it. If the matching method or field is not found, the body of the extern /// method will throw or . + /// Only the specific type defined will be examined for inaccessible members. The type hierarchy + /// is not walked looking for a match. /// /// For , , /// , and , the type of From 3cc17a23be461b8d3f0944a2e51af59fc2509687 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Jul 2023 16:21:54 -0700 Subject: [PATCH 7/7] Disable test on mono --- .../compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index 9a73f1b0de3b93..28d88ca0dd3d5b 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -393,6 +393,7 @@ class InheritanceDerived : InheritanceBase } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/89212", TestRuntimes.Mono)] public static void Verify_InheritanceMethodResolution() { Console.WriteLine($"Running {nameof(Verify_InheritanceMethodResolution)}");