PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/DLR/Microsoft.Dynamic/Runtime/DelegateInfo.cs

#
C# | 229 lines | 140 code | 42 blank | 47 comment | 25 complexity | 4158794dd9bcb4e9b128cd9ea74dd3d6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Dynamic;
  19. using System.Linq.Expressions;
  20. using System.Reflection;
  21. using System.Reflection.Emit;
  22. using System.Runtime.CompilerServices;
  23. using Microsoft.Scripting.Actions;
  24. using Microsoft.Scripting.Generation;
  25. using Microsoft.Scripting.Utils;
  26. namespace Microsoft.Scripting.Runtime {
  27. /// <summary>
  28. /// Used as the value for the ScriptingRuntimeHelpers.GetDelegate method caching system
  29. /// </summary>
  30. public sealed class DelegateInfo {
  31. private const int TargetIndex = 0;
  32. private const int CallSiteIndex = 1;
  33. private const int ConvertSiteIndex = 2;
  34. private static readonly object TargetPlaceHolder = new object();
  35. private static readonly object CallSitePlaceHolder = new object();
  36. private static readonly object ConvertSitePlaceHolder = new object();
  37. // to enable:
  38. // function x() { }
  39. // someClass.someEvent += delegateType(x)
  40. // someClass.someEvent -= delegateType(x)
  41. //
  42. // We need to avoid re-creating the closure because the delegates won't
  43. // compare equal when removing the delegate if they have different closure
  44. // instances. Therefore we use a weak hashtable to get back the
  45. // original closure. The closures also need to be held via a weak refererence to avoid
  46. // creating a circular reference from the constants target back to the
  47. // target. This is fine because as long as the delegate is referenced
  48. // the object array will stay alive. Once the delegate is gone it's not
  49. // wired up anywhere and -= will never be used again.
  50. //
  51. // Note that the closure content depends on the signature of the delegate. So a single dynamic object
  52. // might need multiple closures if it is converted to delegates of different signatures.
  53. private WeakDictionary<object, WeakReference> _closureMap = new WeakDictionary<object, WeakReference>();
  54. private readonly Type _returnType;
  55. private readonly Type[] _parameterTypes;
  56. private readonly MethodInfo _method;
  57. private readonly InvokeBinder _invokeBinder;
  58. private readonly ConvertBinder _convertBinder;
  59. public DelegateInfo(LanguageContext context, Type returnType, Type[] parameters) {
  60. Assert.NotNull(returnType);
  61. Assert.NotNullItems(parameters);
  62. _returnType = returnType;
  63. _parameterTypes = parameters;
  64. PerfTrack.NoteEvent(PerfTrack.Categories.DelegateCreate, ToString());
  65. if (_returnType != typeof(void)) {
  66. _convertBinder = context.CreateConvertBinder(_returnType, true);
  67. }
  68. _invokeBinder = context.CreateInvokeBinder(new CallInfo(_parameterTypes.Length));
  69. Type[] delegateParams = new Type[1 + _parameterTypes.Length];
  70. delegateParams[0] = typeof(object[]);
  71. for (int i = 0; i < _parameterTypes.Length; i++) {
  72. delegateParams[1 + i] = _parameterTypes[i];
  73. }
  74. EmitClrCallStub(returnType, delegateParams, out _method);
  75. }
  76. public Delegate CreateDelegate(Type delegateType, object dynamicObject) {
  77. Assert.NotNull(delegateType, dynamicObject);
  78. object[] closure;
  79. lock (_closureMap) {
  80. WeakReference weakClosure;
  81. if (!_closureMap.TryGetValue(dynamicObject, out weakClosure) || (closure = (object[])weakClosure.Target) == null) {
  82. closure = new[] { TargetPlaceHolder, CallSitePlaceHolder, ConvertSitePlaceHolder };
  83. _closureMap[dynamicObject] = new WeakReference(closure);
  84. Type[] siteTypes = MakeSiteSignature(_parameterTypes);
  85. CallSite callSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(siteTypes), _invokeBinder);
  86. CallSite convertSite = null;
  87. if (_returnType != typeof(void)) {
  88. convertSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(typeof(object), _returnType), _convertBinder);
  89. }
  90. closure[TargetIndex] = dynamicObject;
  91. closure[CallSiteIndex] = callSite;
  92. closure[ConvertSiteIndex] = convertSite;
  93. }
  94. }
  95. return _method.CreateDelegate(delegateType, closure);
  96. }
  97. private void EmitClrCallStub(Type returnType, Type[] parameterTypes, out MethodInfo method) {
  98. // Create the method with a special name so the langauge compiler knows that method's stack frame is not visible
  99. DynamicILGen cg = Snippets.Shared.CreateDynamicMethod("_Scripting_", returnType, parameterTypes, false);
  100. EmitClrCallStub(cg);
  101. method = cg.Finish();
  102. }
  103. /// <summary>
  104. /// Generates stub to receive the CLR call and then call the dynamic language code.
  105. /// </summary>
  106. private void EmitClrCallStub(ILGen cg) {
  107. List<ReturnFixer> fixers = new List<ReturnFixer>(0);
  108. // Create strongly typed return type from the site.
  109. // This will, among other things, generate tighter code.
  110. Type[] siteTypes = MakeSiteSignature(_parameterTypes);
  111. CallSite callSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(siteTypes), _invokeBinder);
  112. Type siteType = callSite.GetType();
  113. Type convertSiteType = null;
  114. CallSite convertSite = null;
  115. if (_returnType != typeof(void)) {
  116. convertSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(typeof(object), _returnType), _convertBinder);
  117. convertSiteType = convertSite.GetType();
  118. }
  119. LocalBuilder convertSiteLocal = null;
  120. FieldInfo convertTarget = null;
  121. if (_returnType != typeof(void)) {
  122. // load up the conversion logic on the stack
  123. convertSiteLocal = cg.DeclareLocal(convertSiteType);
  124. EmitConstantGet(cg, ConvertSiteIndex, convertSiteType);
  125. cg.Emit(OpCodes.Dup);
  126. cg.Emit(OpCodes.Stloc, convertSiteLocal);
  127. convertTarget = convertSiteType.GetDeclaredField("Target");
  128. cg.EmitFieldGet(convertTarget);
  129. cg.Emit(OpCodes.Ldloc, convertSiteLocal);
  130. }
  131. // load up the invoke logic on the stack
  132. LocalBuilder site = cg.DeclareLocal(siteType);
  133. EmitConstantGet(cg, CallSiteIndex, siteType);
  134. cg.Emit(OpCodes.Dup);
  135. cg.Emit(OpCodes.Stloc, site);
  136. FieldInfo target = siteType.GetDeclaredField("Target");
  137. cg.EmitFieldGet(target);
  138. cg.Emit(OpCodes.Ldloc, site);
  139. EmitConstantGet(cg, TargetIndex, typeof(object));
  140. for (int i = 0; i < _parameterTypes.Length; i++) {
  141. if (_parameterTypes[i].IsByRef) {
  142. ReturnFixer rf = ReturnFixer.EmitArgument(cg, i + 1, _parameterTypes[i]);
  143. if (rf != null) fixers.Add(rf);
  144. } else {
  145. cg.EmitLoadArg(i + 1);
  146. }
  147. }
  148. // emit the invoke for the call
  149. cg.EmitCall(target.FieldType, "Invoke");
  150. // emit the invoke for the convert
  151. if (_returnType == typeof(void)) {
  152. cg.Emit(OpCodes.Pop);
  153. } else {
  154. cg.EmitCall(convertTarget.FieldType, "Invoke");
  155. }
  156. // fixup any references
  157. foreach (ReturnFixer rf in fixers) {
  158. rf.FixReturn(cg);
  159. }
  160. cg.Emit(OpCodes.Ret);
  161. }
  162. private static void EmitConstantGet(ILGen il, int index, Type type) {
  163. il.Emit(OpCodes.Ldarg_0);
  164. il.EmitInt(index);
  165. il.Emit(OpCodes.Ldelem_Ref);
  166. if (type != typeof(object)) {
  167. il.Emit(OpCodes.Castclass, type);
  168. }
  169. }
  170. private static Type[] MakeSiteSignature(Type[] parameterTypes) {
  171. Type[] sig = new Type[parameterTypes.Length + 2];
  172. // target object
  173. sig[0] = typeof(object);
  174. // arguments
  175. for (int i = 0; i < parameterTypes.Length; i++) {
  176. if (parameterTypes[i].IsByRef) {
  177. sig[i + 1] = typeof(object);
  178. } else {
  179. sig[i + 1] = parameterTypes[i];
  180. }
  181. }
  182. // return type
  183. sig[sig.Length - 1] = typeof(object);
  184. return sig;
  185. }
  186. }
  187. }