diff --git a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyInstanceContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/ClassProxySerializableContributor.cs similarity index 92% rename from src/Castle.Core/DynamicProxy/Contributors/ClassProxyInstanceContributor.cs rename to src/Castle.Core/DynamicProxy/Contributors/ClassProxySerializableContributor.cs index f8e27c0399..891c0eed5f 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/ClassProxyInstanceContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/ClassProxySerializableContributor.cs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if FEATURE_SERIALIZATION + namespace Castle.DynamicProxy.Contributors { using System; using System.Collections.Generic; using System.Reflection; -#if FEATURE_SERIALIZATION using System.Runtime.Serialization; -#endif using Castle.DynamicProxy.Generators.Emitters; using Castle.DynamicProxy.Generators.Emitters.CodeBuilders; @@ -27,51 +27,33 @@ namespace Castle.DynamicProxy.Contributors using Castle.DynamicProxy.Internal; using Castle.DynamicProxy.Tokens; - internal class ClassProxyInstanceContributor : ProxyInstanceContributor + internal class ClassProxySerializableContributor : SerializableContributor { -#if FEATURE_SERIALIZATION private readonly bool delegateToBaseGetObjectData; private readonly bool implementISerializable; private ConstructorInfo serializationConstructor; private readonly IList serializedFields = new List(); -#endif - public ClassProxyInstanceContributor(Type targetType, IList methodsToSkip, Type[] interfaces, + public ClassProxySerializableContributor(Type targetType, IList methodsToSkip, Type[] interfaces, string typeId) : base(targetType, interfaces, typeId) { -#if FEATURE_SERIALIZATION if (targetType.IsSerializable) { implementISerializable = true; delegateToBaseGetObjectData = VerifyIfBaseImplementsGetObjectData(targetType, methodsToSkip); } -#endif - } - - protected override Reference GetTargetReference(ClassEmitter emitter) - { - return SelfReference.Self; } public override void Generate(ClassEmitter @class) { - var interceptors = @class.GetField("__interceptors"); -#if FEATURE_SERIALIZATION if (implementISerializable) { ImplementGetObjectData(@class); Constructor(@class); } -#endif - ImplementProxyTargetAccessor(@class, interceptors); - foreach (var attribute in targetType.GetNonInheritableAttributes()) - { - @class.DefineCustomAttribute(attribute.Builder); - } } -#if FEATURE_SERIALIZATION protected override void AddAddValueInvocation(ArgumentReference serializationInfo, MethodEmitter getObjectData, FieldReference field) { @@ -229,6 +211,7 @@ private bool VerifyIfBaseImplementsGetObjectData(Type baseType, IList methodsToSkip, - Type[] interfaces, string typeId) - : base(targetType, methodsToSkip, interfaces, typeId) - { - } - - protected override Reference GetTargetReference(ClassEmitter emitter) - { - return emitter.GetField("__target"); - } - } -} diff --git a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyInstanceContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxySerializableContributor.cs similarity index 88% rename from src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyInstanceContributor.cs rename to src/Castle.Core/DynamicProxy/Contributors/InterfaceProxySerializableContributor.cs index e00e143fa6..6078965d5d 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxyInstanceContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/InterfaceProxySerializableContributor.cs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if FEATURE_SERIALIZATION + namespace Castle.DynamicProxy.Contributors { using System; @@ -21,19 +23,13 @@ namespace Castle.DynamicProxy.Contributors using Castle.DynamicProxy.Generators.Emitters.SimpleAST; using Castle.DynamicProxy.Tokens; - internal class InterfaceProxyInstanceContributor : ProxyInstanceContributor + internal class InterfaceProxySerializableContributor : SerializableContributor { - protected override Reference GetTargetReference(ClassEmitter emitter) - { - return emitter.GetField("__target"); - } - - public InterfaceProxyInstanceContributor(Type targetType, string proxyGeneratorId, Type[] interfaces) + public InterfaceProxySerializableContributor(Type targetType, string proxyGeneratorId, Type[] interfaces) : base(targetType, interfaces, proxyGeneratorId) { } -#if FEATURE_SERIALIZATION protected override void CustomizeGetObjectData(AbstractCodeBuilder codebuilder, ArgumentReference serializationInfo, ArgumentReference streamingContext, ClassEmitter emitter) { @@ -52,6 +48,7 @@ public InterfaceProxyInstanceContributor(Type targetType, string proxyGeneratorI new ConstReference(targetType.AssemblyQualifiedName). ToExpression()))); } -#endif } -} \ No newline at end of file +} + +#endif diff --git a/src/Castle.Core/DynamicProxy/Contributors/NonInheritableAttributesContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/NonInheritableAttributesContributor.cs new file mode 100644 index 0000000000..0f30bec9af --- /dev/null +++ b/src/Castle.Core/DynamicProxy/Contributors/NonInheritableAttributesContributor.cs @@ -0,0 +1,47 @@ +// Copyright 2004-2021 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Contributors +{ + using System; + + using Castle.DynamicProxy.Generators; + using Castle.DynamicProxy.Generators.Emitters; + using Castle.DynamicProxy.Internal; + + /// + /// Reproduces the proxied type's non-inheritable custom attributes on the proxy type. + /// + internal sealed class NonInheritableAttributesContributor : ITypeContributor + { + private readonly Type targetType; + + public NonInheritableAttributesContributor(Type targetType) + { + this.targetType = targetType; + } + + public void Generate(ClassEmitter emitter) + { + foreach (var attribute in targetType.GetNonInheritableAttributes()) + { + emitter.DefineCustomAttribute(attribute.Builder); + } + } + + public void CollectElementsToProxy(IProxyGenerationHook hook, MetaType model) + { + } + } +} diff --git a/src/Castle.Core/DynamicProxy/Contributors/ProxyTargetAccessorContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/ProxyTargetAccessorContributor.cs new file mode 100644 index 0000000000..f5f363e88d --- /dev/null +++ b/src/Castle.Core/DynamicProxy/Contributors/ProxyTargetAccessorContributor.cs @@ -0,0 +1,74 @@ +// Copyright 2004-2021 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.DynamicProxy.Contributors +{ + using System; + + using Castle.DynamicProxy.Generators; + using Castle.DynamicProxy.Generators.Emitters; + using Castle.DynamicProxy.Generators.Emitters.SimpleAST; + + /// + /// Adds an implementation for to the proxy type. + /// + internal sealed class ProxyTargetAccessorContributor : ITypeContributor + { + private readonly Func getTargetReference; + private readonly Type targetType; + + public ProxyTargetAccessorContributor(Func getTargetReference, Type targetType) + { + this.getTargetReference = getTargetReference; + this.targetType = targetType; + } + + public void CollectElementsToProxy(IProxyGenerationHook hook, MetaType model) + { + } + + public void Generate(ClassEmitter emitter) + { + var interceptorsField = emitter.GetField("__interceptors"); + var targetReference = getTargetReference(); + + var dynProxyGetTarget = emitter.CreateMethod(nameof(IProxyTargetAccessor.DynProxyGetTarget), typeof(object)); + + dynProxyGetTarget.CodeBuilder.AddStatement( + new ReturnStatement(new ConvertExpression(typeof(object), targetType, targetReference.ToExpression()))); + + var dynProxySetTarget = emitter.CreateMethod(nameof(IProxyTargetAccessor.DynProxySetTarget), typeof(void), typeof(object)); + + // we can only change the target of the interface proxy + if (targetReference is FieldReference targetField) + { + dynProxySetTarget.CodeBuilder.AddStatement( + new AssignStatement(targetField, + new ConvertExpression(targetField.Fieldbuilder.FieldType, dynProxySetTarget.Arguments[0].ToExpression()))); + } + else + { + dynProxySetTarget.CodeBuilder.AddStatement( + new ThrowStatement(typeof(InvalidOperationException), "Cannot change the target of the class proxy.")); + } + + dynProxySetTarget.CodeBuilder.AddStatement(new ReturnStatement()); + + var getInterceptors = emitter.CreateMethod(nameof(IProxyTargetAccessor.GetInterceptors), typeof(IInterceptor[])); + + getInterceptors.CodeBuilder.AddStatement( + new ReturnStatement(interceptorsField)); + } + } +} diff --git a/src/Castle.Core/DynamicProxy/Contributors/ProxyInstanceContributor.cs b/src/Castle.Core/DynamicProxy/Contributors/SerializableContributor.cs similarity index 72% rename from src/Castle.Core/DynamicProxy/Contributors/ProxyInstanceContributor.cs rename to src/Castle.Core/DynamicProxy/Contributors/SerializableContributor.cs index 454ecc6173..4b1f2b222a 100644 --- a/src/Castle.Core/DynamicProxy/Contributors/ProxyInstanceContributor.cs +++ b/src/Castle.Core/DynamicProxy/Contributors/SerializableContributor.cs @@ -12,89 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if FEATURE_SERIALIZATION + namespace Castle.DynamicProxy.Contributors { using System; - using System.Reflection; -#if FEATURE_SERIALIZATION using System.Runtime.Serialization; -#endif using Castle.DynamicProxy.Generators; using Castle.DynamicProxy.Generators.Emitters; using Castle.DynamicProxy.Generators.Emitters.CodeBuilders; using Castle.DynamicProxy.Generators.Emitters.SimpleAST; - using Castle.DynamicProxy.Internal; -#if FEATURE_SERIALIZATION using Castle.DynamicProxy.Serialization; -#endif using Castle.DynamicProxy.Tokens; - internal abstract class ProxyInstanceContributor : ITypeContributor + internal abstract class SerializableContributor : ITypeContributor { protected readonly Type targetType; private readonly string proxyTypeId; private readonly Type[] interfaces; - protected ProxyInstanceContributor(Type targetType, Type[] interfaces, string proxyTypeId) + protected SerializableContributor(Type targetType, Type[] interfaces, string proxyTypeId) { this.targetType = targetType; this.proxyTypeId = proxyTypeId; this.interfaces = interfaces ?? Type.EmptyTypes; } - protected abstract Reference GetTargetReference(ClassEmitter emitter); - - private Expression GetTargetReferenceExpression(ClassEmitter emitter) - { - return GetTargetReference(emitter).ToExpression(); - } - public virtual void Generate(ClassEmitter @class) { - var interceptors = @class.GetField("__interceptors"); -#if FEATURE_SERIALIZATION ImplementGetObjectData(@class); -#endif - ImplementProxyTargetAccessor(@class, interceptors); - foreach (var attribute in targetType.GetNonInheritableAttributes()) - { - @class.DefineCustomAttribute(attribute.Builder); - } } - protected void ImplementProxyTargetAccessor(ClassEmitter emitter, FieldReference interceptorsField) - { - var dynProxyGetTarget = emitter.CreateMethod("DynProxyGetTarget", typeof(object)); - - dynProxyGetTarget.CodeBuilder.AddStatement( - new ReturnStatement(new ConvertExpression(typeof(object), targetType, GetTargetReferenceExpression(emitter)))); - - var dynProxySetTarget = emitter.CreateMethod("DynProxySetTarget", typeof(void), typeof(object)); - - // we can only change the target of the interface proxy - var targetField = GetTargetReference(emitter) as FieldReference; - if (targetField != null) - { - dynProxySetTarget.CodeBuilder.AddStatement( - new AssignStatement(targetField, - new ConvertExpression(targetField.Fieldbuilder.FieldType, dynProxySetTarget.Arguments[0].ToExpression()))); - } - else - { - dynProxySetTarget.CodeBuilder.AddStatement( - new ThrowStatement(typeof(InvalidOperationException), "Cannot change the target of the class proxy.")); - } - - dynProxySetTarget.CodeBuilder.AddStatement(new ReturnStatement()); - - var getInterceptors = emitter.CreateMethod("GetInterceptors", typeof(IInterceptor[])); - - getInterceptors.CodeBuilder.AddStatement( - new ReturnStatement(interceptorsField)); - } - -#if FEATURE_SERIALIZATION protected void ImplementGetObjectData(ClassEmitter emitter) { var getObjectData = emitter.CreateMethod("GetObjectData", typeof(void), @@ -200,10 +149,11 @@ protected void ImplementGetObjectData(ClassEmitter emitter) protected abstract void CustomizeGetObjectData(AbstractCodeBuilder builder, ArgumentReference serializationInfo, ArgumentReference streamingContext, ClassEmitter emitter); -#endif public void CollectElementsToProxy(IProxyGenerationHook hook, MetaType model) { } } -} \ No newline at end of file +} + +#endif diff --git a/src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs index 638f32131e..b3f3e17ade 100644 --- a/src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs @@ -33,10 +33,14 @@ protected BaseClassProxyGenerator(ModuleScope scope, Type targetType, Type[] int protected abstract FieldReference TargetField { get; } - protected abstract ProxyInstanceContributor GetProxyInstanceContributor(List methodsToSkip); +#if FEATURE_SERIALIZATION + protected abstract SerializableContributor GetSerializableContributor(List methodsToSkip); +#endif protected abstract CompositeTypeContributor GetProxyTargetContributor(List methodsToSkip, INamingScope namingScope); + protected abstract ProxyTargetAccessorContributor GetProxyTargetAccessorContributor(); + protected sealed override Type GenerateType(string name, INamingScope namingScope) { IEnumerable contributors; @@ -91,6 +95,10 @@ protected sealed override Type GenerateType(string name, INamingScope namingScop // Complete type initializer code body CompleteInitCacheMethod(cctor.CodeBuilder); + // non-inheritable attributes from proxied type + var nonInheritableAttributesContributor = new NonInheritableAttributesContributor(targetType); + nonInheritableAttributesContributor.Generate(emitter); + // Crosses fingers and build type var proxyType = emitter.BuildType(); @@ -100,7 +108,7 @@ protected sealed override Type GenerateType(string name, INamingScope namingScop private IEnumerable GetTypeImplementerMapping(out IEnumerable contributors, INamingScope namingScope) { - var contributorsList = new List(capacity: 4); + var contributorsList = new List(capacity: 5); var methodsToSkip = new List(); // TODO: the trick with methodsToSkip is not very nice... var targetInterfaces = targetType.GetAllInterfaces(); var typeImplementerMapping = new Dictionary(); @@ -171,17 +179,21 @@ private IEnumerable GetTypeImplementerMapping(out IEnumerable typeImplementerMapping, ICollection targetInterfaces); @@ -129,6 +131,10 @@ protected override Type GenerateType(string typeName, INamingScope namingScope) // Complete type initializer code body CompleteInitCacheMethod(cctor.CodeBuilder); + // non-inheritable attributes from proxied type + var nonInheritableAttributesContributor = new NonInheritableAttributesContributor(targetType); + nonInheritableAttributesContributor.Generate(emitter); + // Crosses fingers and build type var generatedType = emitter.BuildType(); @@ -146,7 +152,7 @@ protected override Type GenerateType(string typeName, INamingScope namingScope) out IEnumerable contributors, INamingScope namingScope) { - var contributorsList = new List(capacity: 4); + var contributorsList = new List(capacity: 5); var targetInterfaces = proxyTargetType.GetAllInterfaces(); var typeImplementerMapping = new Dictionary(); @@ -208,14 +214,18 @@ protected override Type GenerateType(string typeName, INamingScope namingScope) } // 4. plus special interfaces - var instanceContributor = new InterfaceProxyInstanceContributor(targetType, GeneratorType, interfaces); - contributorsList.Add(instanceContributor); + #if FEATURE_SERIALIZATION - AddMappingForISerializable(typeImplementerMapping, instanceContributor); + var serializableContributor = new InterfaceProxySerializableContributor(targetType, GeneratorType, interfaces); + contributorsList.Add(serializableContributor); + AddMappingForISerializable(typeImplementerMapping, serializableContributor); #endif + + var proxyTargetAccessorContributor = GetProxyTargetAccessorContributor(); + contributorsList.Add(proxyTargetAccessorContributor); try { - AddMappingNoCheck(typeof(IProxyTargetAccessor), instanceContributor, typeImplementerMapping); + AddMappingNoCheck(typeof(IProxyTargetAccessor), proxyTargetAccessorContributor, typeImplementerMapping); } catch (ArgumentException) { diff --git a/src/Castle.Core/DynamicProxy/Generators/ClassProxyGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/ClassProxyGenerator.cs index 886c06a844..5d69669dfa 100644 --- a/src/Castle.Core/DynamicProxy/Generators/ClassProxyGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/ClassProxyGenerator.cs @@ -36,14 +36,23 @@ protected override CacheKey GetCacheKey() return new CacheKey(targetType, interfaces, ProxyGenerationOptions); } - protected override ProxyInstanceContributor GetProxyInstanceContributor(List methodsToSkip) +#if FEATURE_SERIALIZATION + protected override SerializableContributor GetSerializableContributor(List methodsToSkip) { - return new ClassProxyInstanceContributor(targetType, methodsToSkip, interfaces, ProxyTypeConstants.Class); + return new ClassProxySerializableContributor(targetType, methodsToSkip, interfaces, ProxyTypeConstants.Class); } +#endif protected override CompositeTypeContributor GetProxyTargetContributor(List methodsToSkip, INamingScope namingScope) { return new ClassProxyTargetContributor(targetType, methodsToSkip, namingScope) { Logger = Logger }; } + + protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor() + { + return new ProxyTargetAccessorContributor( + getTargetReference: () => SelfReference.Self, + targetType); + } } } diff --git a/src/Castle.Core/DynamicProxy/Generators/ClassProxyWithTargetGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/ClassProxyWithTargetGenerator.cs index e4c82645d6..647eaee207 100644 --- a/src/Castle.Core/DynamicProxy/Generators/ClassProxyWithTargetGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/ClassProxyWithTargetGenerator.cs @@ -50,16 +50,25 @@ protected override void CreateFields(ClassEmitter emitter) CreateTargetField(emitter); } - protected override ProxyInstanceContributor GetProxyInstanceContributor(List methodsToSkip) +#if FEATURE_SERIALIZATION + protected override SerializableContributor GetSerializableContributor(List methodsToSkip) { - return new ClassProxyWithTargetInstanceContributor(targetType, methodsToSkip, interfaces, ProxyTypeConstants.ClassWithTarget); + return new ClassProxySerializableContributor(targetType, methodsToSkip, interfaces, ProxyTypeConstants.ClassWithTarget); } +#endif protected override CompositeTypeContributor GetProxyTargetContributor(List methodsToSkip, INamingScope namingScope) { return new ClassProxyWithTargetTargetContributor(targetType, methodsToSkip, namingScope) { Logger = Logger }; } + protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor() + { + return new ProxyTargetAccessorContributor( + getTargetReference: () => targetField, + targetType); + } + private void CreateTargetField(ClassEmitter emitter) { targetField = emitter.CreateField("__target", targetType); diff --git a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs index ab30754080..dc4ac91c41 100644 --- a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetGenerator.cs @@ -37,6 +37,13 @@ protected override CompositeTypeContributor GetProxyTargetContributor(Type proxy return new InterfaceProxyTargetContributor(proxyTargetType, AllowChangeTarget, namingScope) { Logger = Logger }; } + protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor() + { + return new ProxyTargetAccessorContributor( + getTargetReference: () => targetField, + proxyTargetType); + } + protected override void AddMappingForAdditionalInterfaces(CompositeTypeContributor contributor, Type[] proxiedInterfaces, IDictionary typeImplementerMapping, ICollection targetInterfaces) diff --git a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetInterfaceGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetInterfaceGenerator.cs index 77091c2052..99b216cd08 100644 --- a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetInterfaceGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithTargetInterfaceGenerator.cs @@ -41,6 +41,13 @@ protected override CompositeTypeContributor GetProxyTargetContributor(Type proxy return new InterfaceProxyWithTargetInterfaceTargetContributor(proxyTargetType, AllowChangeTarget, namingScope) { Logger = Logger }; } + protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor() + { + return new ProxyTargetAccessorContributor( + getTargetReference: () => targetField, + proxyTargetType); + } + protected override void AddMappingForAdditionalInterfaces(CompositeTypeContributor contributor, Type[] proxiedInterfaces, IDictionary typeImplementerMapping, ICollection targetInterfaces) diff --git a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithoutTargetGenerator.cs b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithoutTargetGenerator.cs index 35fe2497fa..d916e4dc62 100644 --- a/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithoutTargetGenerator.cs +++ b/src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithoutTargetGenerator.cs @@ -38,6 +38,13 @@ protected override CompositeTypeContributor GetProxyTargetContributor(Type proxy return new InterfaceProxyWithoutTargetContributor(namingScope, (c, m) => NullExpression.Instance) { Logger = Logger }; } + protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor() + { + return new ProxyTargetAccessorContributor( + getTargetReference: () => targetField, + proxyTargetType); + } + protected override void AddMappingForAdditionalInterfaces(CompositeTypeContributor contributor, Type[] proxiedInterfaces, IDictionary typeImplementerMapping, ICollection targetInterfaces)