PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Merlin/Main/Runtime/Microsoft.Scripting/Runtime/DelegateInfo.cs

https://github.com/mattsnyder/ironruby
C# | 233 lines | 142 code | 45 blank | 46 comment | 28 complexity | b47ebe627926d97c342adfd0f034010f MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  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.Reflection;
  20. using System.Reflection.Emit;
  21. using System.Runtime.CompilerServices;
  22. using Microsoft.Scripting.Actions;
  23. using Microsoft.Scripting.Generation;
  24. using Microsoft.Scripting.Utils;
  25. namespace Microsoft.Scripting.Runtime {
  26. /// <summary>
  27. /// Used as the value for the ScriptingRuntimeHelpers.GetDelegate method caching system
  28. /// </summary>
  29. internal sealed class DelegateInfo {
  30. private readonly Type _returnType;
  31. private readonly ParameterInfo[] _parameters;
  32. private readonly MethodInfo _method;
  33. private readonly object[] _constants;
  34. private WeakDictionary<object, WeakReference> _constantMap = new WeakDictionary<object, WeakReference>();
  35. private readonly InvokeBinder _invokeBinder;
  36. private readonly ConvertBinder _convertBinder;
  37. private static readonly object TargetPlaceHolder = new object();
  38. private static readonly object CallSitePlaceHolder = new object();
  39. private static readonly object ConvertSitePlaceHolder = new object();
  40. internal DelegateInfo(LanguageContext context, Type returnType, ParameterInfo[] parameters) {
  41. Assert.NotNull(returnType);
  42. Assert.NotNullItems(parameters);
  43. _returnType = returnType;
  44. _parameters = parameters;
  45. PerfTrack.NoteEvent(PerfTrack.Categories.DelegateCreate, ToString());
  46. if (_returnType != typeof(void)) {
  47. _convertBinder = context.CreateConvertBinder(_returnType, true);
  48. }
  49. _invokeBinder = context.CreateInvokeBinder(new CallInfo(_parameters.Length));
  50. Type[] delegateParams = new Type[_parameters.Length];
  51. for (int i = 0; i < _parameters.Length; i++) {
  52. delegateParams[i] = _parameters[i].ParameterType;
  53. }
  54. // Create the method with a special name so the langauge compiler knows that method's stack frame is not visible
  55. DynamicILGen cg = Snippets.Shared.CreateDynamicMethod("_Scripting_", _returnType, ArrayUtils.Insert(typeof(object[]), delegateParams), false);
  56. // Emit the stub
  57. _constants = EmitClrCallStub(cg);
  58. _method = cg.Finish();
  59. }
  60. internal Delegate CreateDelegate(Type delegateType, object target) {
  61. Assert.NotNull(delegateType, target);
  62. // to enable:
  63. // function x() { }
  64. // someClass.someEvent += delegateType(x)
  65. // someClass.someEvent -= delegateType(x)
  66. //
  67. // we need to avoid re-creating the object array because they won't
  68. // be compare equal when removing the delegate if they're difference
  69. // instances. Therefore we use a weak hashtable to get back the
  70. // original object array. The values also need to be weak to avoid
  71. // creating a circular reference from the constants target back to the
  72. // target. This is fine because as long as the delegate is referenced
  73. // the object array will stay alive. Once the delegate is gone it's not
  74. // wired up anywhere and -= will never be used again.
  75. object[] clone;
  76. lock (_constantMap) {
  77. WeakReference cloneRef;
  78. if (!_constantMap.TryGetValue(target, out cloneRef) ||
  79. (clone = (object[])cloneRef.Target) == null) {
  80. _constantMap[target] = new WeakReference(clone = (object[])_constants.Clone());
  81. Type[] siteTypes = MakeSiteSignature();
  82. CallSite callSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(siteTypes), _invokeBinder);
  83. Type siteType = callSite.GetType();
  84. Type convertSiteType = null;
  85. CallSite convertSite = null;
  86. if (_returnType != typeof(void)) {
  87. convertSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(typeof(object), _returnType), _convertBinder);
  88. convertSiteType = convertSite.GetType();
  89. }
  90. Debug.Assert(clone[0] == TargetPlaceHolder);
  91. Debug.Assert(clone[1] == CallSitePlaceHolder);
  92. Debug.Assert(clone[2] == ConvertSitePlaceHolder);
  93. clone[0] = target;
  94. clone[1] = callSite;
  95. clone[2] = convertSite;
  96. }
  97. }
  98. return ReflectionUtils.CreateDelegate(_method, delegateType, clone);
  99. }
  100. /// <summary>
  101. /// Generates stub to receive the CLR call and then call the dynamic language code.
  102. /// </summary>
  103. private object[] EmitClrCallStub(ILGen cg) {
  104. List<ReturnFixer> fixers = new List<ReturnFixer>(0);
  105. // Create strongly typed return type from the site.
  106. // This will, among other things, generate tighter code.
  107. Type[] siteTypes = MakeSiteSignature();
  108. CallSite callSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(siteTypes), _invokeBinder);
  109. Type siteType = callSite.GetType();
  110. Type convertSiteType = null;
  111. CallSite convertSite = null;
  112. if (_returnType != typeof(void)) {
  113. convertSite = CallSite.Create(DynamicSiteHelpers.MakeCallSiteDelegate(typeof(object), _returnType), _convertBinder);
  114. convertSiteType = convertSite.GetType();
  115. }
  116. // build up constants array
  117. object[] constants = new object[] { TargetPlaceHolder, CallSitePlaceHolder, ConvertSitePlaceHolder };
  118. const int TargetIndex = 0, CallSiteIndex = 1, ConvertSiteIndex = 2;
  119. LocalBuilder convertSiteLocal = null;
  120. FieldInfo convertTarget = null;
  121. if (_returnType != typeof(void)) {
  122. // load up the conversesion 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.GetField("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.GetField("Target");
  137. cg.EmitFieldGet(target);
  138. cg.Emit(OpCodes.Ldloc, site);
  139. EmitConstantGet(cg, TargetIndex, typeof(object));
  140. for (int i = 0; i < _parameters.Length; i++) {
  141. if (_parameters[i].ParameterType.IsByRef) {
  142. ReturnFixer rf = ReturnFixer.EmitArgument(cg, i + 1, _parameters[i].ParameterType);
  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. return constants;
  162. }
  163. private static void EmitConstantGet(ILGen il, int index, Type type) {
  164. il.Emit(OpCodes.Ldarg_0);
  165. il.EmitInt(index);
  166. il.Emit(OpCodes.Ldelem_Ref);
  167. if (type != typeof(object)) {
  168. il.Emit(OpCodes.Castclass, type);
  169. }
  170. }
  171. internal Type[] MakeSiteSignature() {
  172. Type[] sig = new Type[_parameters.Length + 2];
  173. // target object
  174. sig[0] = typeof(object);
  175. // arguments
  176. for (int i = 0; i < _parameters.Length; i++) {
  177. if (_parameters[i].IsByRefParameter()) {
  178. sig[i + 1] = typeof(object);
  179. } else {
  180. sig[i + 1] = _parameters[i].ParameterType;
  181. }
  182. }
  183. // return type
  184. sig[sig.Length - 1] = typeof(object);
  185. return sig;
  186. }
  187. }
  188. }