Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / IArgumentProvider.cs
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
16 using System;
17 using System.Collections.Generic;
18 using System.Text;
19
20 #if !FEATURE_CORE_DLR
21 namespace Microsoft.Scripting.Ast {
22     using Microsoft.Scripting.Utils;
23 #else
24 namespace System.Linq.Expressions {
25 #endif
26     /// <summary>
27     /// Provides an internal interface for accessing the arguments that multiple tree
28     /// nodes (DynamicExpression, ElementInit, MethodCallExpression, InvocationExpression, NewExpression,
29     /// and InexExpression).
30     /// 
31     /// This enables two optimizations which reduce the size of the trees.  The first is it enables
32     /// the nodes to hold onto an IList of T instead of a ReadOnlyCollection.  This saves the cost
33     /// of allocating the ReadOnlyCollection for each node.  The second is that it enables specialized
34     /// subclasses to be created which hold onto a specific number of arguments.  For example Block2,
35     /// Block3, Block4.  These nodes can therefore avoid allocating both a ReadOnlyCollection and an
36     /// array for storing their elements saving 32 bytes per node.
37     /// 
38     /// Meanwhile the nodes can continue to expose the original LINQ properties of ReadOnlyCollections.  They
39     /// do this by re-using 1 field for storing both the array or an element that would normally be stored
40     /// in the array.  
41     /// 
42     /// For the array case the collection is typed to IList of T instead of ReadOnlyCollection of T.
43     /// When the node is initially constructed it is an array.  When the compiler accesses the members it
44     /// uses this interface.  If a user accesses the members the array is promoted to a ReadOnlyCollection.
45     /// 
46     /// For the object case we store the 1st argument in a field typed to object and when the node is initially
47     /// constructed this holds directly onto the Expression.  When the compiler accesses the members
48     /// it again uses this interface and the accessor for the 1st argument uses Expression.ReturnObject to
49     /// return the object which handles the Expression or ReadOnlyCollection case.  When the user accesses
50     /// the ReadOnlyCollection then the object field is updated to hold directly onto the ReadOnlyCollection.
51     /// 
52     /// It is important that the Expressions consistently return the same ReadOnlyCollection otherwise the
53     /// re-writer will be broken and it would be a breaking change from LINQ v1.  The problem is that currently
54     /// users can rely on object identity to tell if the node has changed.  Storing the readonly collection in 
55     /// an overloaded field enables us to both reduce memory usage as well as maintain compatibility and an 
56     /// easy to use external API.
57     /// </summary>
58     internal interface IArgumentProvider {
59         Expression GetArgument(int index);
60         int ArgumentCount {
61             get;
62         }
63     }
64
65     static class ArgumentProviderOps {
66         internal static T[] Map<T>(this IArgumentProvider collection, Func<Expression, T> select) {
67             int count = collection.ArgumentCount;
68             T[] result = new T[count];
69             count = 0;
70             for (int i = 0; i < count; i++) {            
71                 result[i] = select(collection.GetArgument(i));
72             }
73             return result;
74         }
75
76     }
77 }