Merge pull request #4155 from BrzVlad/fix-tls-lmf-addr
[mono-project.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Utils / DynamicUtils.cs
blob3c8e955c587d845137cb8ceb63f105425b62613b
1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
16 #if FEATURE_TASKS
17 using System.Threading.Tasks;
18 #endif
20 #if FEATURE_CORE_DLR
21 using System.Linq.Expressions;
22 #else
23 using Microsoft.Scripting.Ast;
24 #endif
26 using System;
27 using System.Collections.ObjectModel;
28 using System.Diagnostics;
29 using System.Dynamic;
30 using System.Reflection;
31 using System.Runtime.CompilerServices;
32 using System.Threading;
33 using Microsoft.Scripting.Generation;
34 using Microsoft.Scripting.Interpreter;
35 using Microsoft.Scripting.Runtime;
37 #if !FEATURE_DYNAMIC_EXPRESSION_VISITOR
38 #if FEATURE_CORE_DLR
39 namespace System.Linq.Expressions {
40 #else
41 namespace Microsoft.Scripting.Ast {
42 #endif
43 public abstract class DynamicExpressionVisitor : ExpressionVisitor {
46 #endif
48 namespace Microsoft.Scripting.Utils {
49 using AstUtils = Microsoft.Scripting.Ast.Utils;
51 public static class DynamicUtils {
52 /// <summary>
53 /// Returns the list of expressions represented by the <see cref="DynamicMetaObject"/> instances.
54 /// </summary>
55 /// <param name="objects">An array of <see cref="DynamicMetaObject"/> instances to extract expressions from.</param>
56 /// <returns>The array of expressions.</returns>
57 public static Expression[] GetExpressions(DynamicMetaObject[] objects) {
58 ContractUtils.RequiresNotNull(objects, "objects");
60 Expression[] res = new Expression[objects.Length];
61 for (int i = 0; i < objects.Length; i++) {
62 DynamicMetaObject mo = objects[i];
63 res[i] = mo != null ? mo.Expression : null;
66 return res;
69 /// <summary>
70 /// Creates an instance of <see cref="DynamicMetaObject"/> for a runtime value and the expression that represents it during the binding process.
71 /// </summary>
72 /// <param name="argValue">The runtime value to be represented by the <see cref="DynamicMetaObject"/>.</param>
73 /// <param name="parameterExpression">An expression to represent this <see cref="DynamicMetaObject"/> during the binding process.</param>
74 /// <returns>The new instance of <see cref="DynamicMetaObject"/>.</returns>
75 public static DynamicMetaObject ObjectToMetaObject(object argValue, Expression parameterExpression) {
76 IDynamicMetaObjectProvider ido = argValue as IDynamicMetaObjectProvider;
77 if (ido != null) {
78 return ido.GetMetaObject(parameterExpression);
79 } else {
80 return new DynamicMetaObject(parameterExpression, BindingRestrictions.Empty, argValue);
84 /// <summary>
85 /// Produces an interpreted binding using the given binder which falls over to a compiled
86 /// binding after hitCount tries.
87 ///
88 /// This method should be called whenever an interpreted binding is required. Sometimes it will
89 /// return a compiled binding if a previous binding was produced and it's hit count was exhausted.
90 /// In this case the binder will not be called back for a new binding - the previous one will
91 /// be used.
92 /// </summary>
93 /// <typeparam name="T">The delegate type being used for the call site</typeparam>
94 /// <param name="binder">The binder used for the call site</param>
95 /// <param name="compilationThreshold">The number of calls before the binder should switch to a compiled mode.</param>
96 /// <param name="args">The arguments that are passed for the binding (as received in a BindDelegate call)</param>
97 /// <returns>A delegate which represents the interpreted binding.</returns>
98 public static T/*!*/ LightBind<T>(this DynamicMetaObjectBinder/*!*/ binder, object[]/*!*/ args, int compilationThreshold) where T : class {
99 ContractUtils.RequiresNotNull(binder, "binder");
100 ContractUtils.RequiresNotNull(args, "args");
102 return GenericInterpretedBinder<T>.Instance.Bind(binder, compilationThreshold < 0 ? LightCompiler.DefaultCompilationThreshold : compilationThreshold, args);
105 private class GenericInterpretedBinder<T> where T : class {
106 public static GenericInterpretedBinder<T>/*!*/ Instance = new GenericInterpretedBinder<T>();
107 private readonly ReadOnlyCollection<ParameterExpression>/*!*/ _parameters;
108 private readonly Expression/*!*/ _updateExpression;
110 private GenericInterpretedBinder() {
111 var invokeMethod = typeof(T).GetMethod("Invoke");
112 var methodParams = invokeMethod.GetParameters();
114 ReadOnlyCollectionBuilder<ParameterExpression> prms = new ReadOnlyCollectionBuilder<ParameterExpression>(methodParams.Length);
115 ReadOnlyCollectionBuilder<Expression> invokePrms = new ReadOnlyCollectionBuilder<Expression>(methodParams.Length);
116 for (int i = 0; i < methodParams.Length; i++) {
117 var param = Expression.Parameter(methodParams[i].ParameterType);
118 if (i == 0) {
119 invokePrms.Add(Expression.Convert(param, typeof(CallSite<T>)));
120 } else {
121 invokePrms.Add(param);
123 prms.Add(param);
126 _parameters = prms.ToReadOnlyCollection();
128 _updateExpression = Expression.Block(
129 Expression.Label(CallSiteBinder.UpdateLabel),
130 Expression.Invoke(
131 Expression.Property(
132 invokePrms[0],
133 typeof(CallSite<T>).GetDeclaredProperty("Update")
135 invokePrms.ToReadOnlyCollection()
140 public T/*!*/ Bind(DynamicMetaObjectBinder/*!*/ binder, int compilationThreshold, object[] args) {
141 if (CachedBindingInfo<T>.LastInterpretedFailure != null && CachedBindingInfo<T>.LastInterpretedFailure.Binder == binder) {
142 // we failed the rule because we have a compiled target available, return the compiled target
143 Debug.Assert(CachedBindingInfo<T>.LastInterpretedFailure.CompiledTarget != null);
144 var res = CachedBindingInfo<T>.LastInterpretedFailure.CompiledTarget;
145 CachedBindingInfo<T>.LastInterpretedFailure = null;
146 return res;
149 // we haven't produced a rule yet....
150 var bindingInfo = new CachedBindingInfo<T>(binder, compilationThreshold);
152 var targetMO = DynamicMetaObject.Create(args[0], _parameters[1]); // 1 is skipping CallSite
153 DynamicMetaObject[] argsMO = new DynamicMetaObject[args.Length - 1];
154 for (int i = 0; i < argsMO.Length; i++) {
155 argsMO[i] = DynamicMetaObject.Create(args[i + 1], _parameters[i + 2]);
157 var binding = binder.Bind(targetMO, argsMO);
159 return CreateDelegate(binding, bindingInfo);
162 private T/*!*/ CreateDelegate(DynamicMetaObject/*!*/ binding, CachedBindingInfo<T>/*!*/ bindingInfo) {
163 return Compile(binding, bindingInfo).LightCompile(Int32.MaxValue);
166 private Expression<T>/*!*/ Compile(DynamicMetaObject/*!*/ obj, CachedBindingInfo<T>/*!*/ bindingInfo) {
167 var restrictions = obj.Restrictions.ToExpression();
169 var body = Expression.Condition(
170 new InterpretedRuleHitCheckExpression(restrictions, bindingInfo),
171 AstUtils.Convert(obj.Expression, _updateExpression.Type),
172 _updateExpression
175 var res = Expression.Lambda<T>(
176 body,
177 "CallSite.Target",
178 true, // always compile the rules with tail call optimization
179 _parameters
182 bindingInfo.Target = res;
183 return res;
186 /// <summary>
187 /// Expression which reduces to the normal test but under the interpreter adds a count down
188 /// check which enables compiling when the count down is reached.
189 /// </summary>
190 class InterpretedRuleHitCheckExpression : Expression, IInstructionProvider {
191 private readonly Expression/*!*/ _test;
192 private readonly CachedBindingInfo/*!*/ _bindingInfo;
194 private static readonly MethodInfo InterpretedCallSiteTest = typeof(ScriptingRuntimeHelpers).GetMethod("InterpretedCallSiteTest");
195 public InterpretedRuleHitCheckExpression(Expression/*!*/ test, CachedBindingInfo/*!*/ bindingInfo) {
196 Assert.NotNull(test, bindingInfo);
198 _test = test;
199 _bindingInfo = bindingInfo;
202 public override Expression Reduce() {
203 return _test;
206 protected override Expression VisitChildren(ExpressionVisitor visitor) {
207 var test = visitor.Visit(_test);
208 if (test != _test) {
209 return new InterpretedRuleHitCheckExpression(test, _bindingInfo);
211 return this;
214 public override bool CanReduce {
215 get { return true; }
218 public override ExpressionType NodeType {
219 get { return ExpressionType.Extension; }
222 public override Type Type {
223 get { return typeof(bool); }
226 #region IInstructionProvider Members
228 public void AddInstructions(LightCompiler compiler) {
229 compiler.Compile(_test);
230 compiler.Instructions.EmitLoad(_bindingInfo);
231 compiler.EmitCall(InterpretedCallSiteTest);
234 #endregion
240 /// <summary>
241 /// Base class for storing information about the binding that a specific rule is applicable for.
242 ///
243 /// We have a derived generic class but this class enables us to refer to it w/o having the
244 /// generic type information around.
245 ///
246 /// This class tracks both the count down to when we should compile. When we compile we
247 /// take the Expression[T] that was used before and compile it. While this is happening
248 /// we continue to allow the interpreted code to run. When the compilation is complete we
249 /// store a thread static which tells us what binding failed and the current rule is no
250 /// longer functional. Finally the language binder will call us again and we'll retrieve
251 /// and return the compiled overload.
252 /// </summary>
253 abstract class CachedBindingInfo {
254 public readonly DynamicMetaObjectBinder/*!*/ Binder;
255 public int CompilationThreshold;
257 public CachedBindingInfo(DynamicMetaObjectBinder binder, int compilationThreshold) {
258 Binder = binder;
259 CompilationThreshold = compilationThreshold;
262 public abstract bool CheckCompiled();
265 class CachedBindingInfo<T> : CachedBindingInfo where T : class {
266 public T CompiledTarget;
267 public Expression<T> Target;
269 [ThreadStatic]
270 public static CachedBindingInfo<T> LastInterpretedFailure;
272 public CachedBindingInfo(DynamicMetaObjectBinder binder, int compilationThreshold)
273 : base(binder, compilationThreshold) {
276 public override bool CheckCompiled() {
277 if (Target != null) {
278 // start compiling the target if no one else has
279 var lambda = Interlocked.Exchange(ref Target, null);
280 if (lambda != null) {
281 #if FEATURE_TASKS
282 new Task(() => { CompiledTarget = lambda.Compile(); }).Start();
283 #else
284 ThreadPool.QueueUserWorkItem(x => { CompiledTarget = lambda.Compile(); });
285 #endif
289 if (CompiledTarget != null) {
290 LastInterpretedFailure = this;
291 return false;
294 return true;