diff options
Diffstat (limited to 'mcs/class/dlr')
10 files changed, 2379 insertions, 2385 deletions
diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/.gitattributes b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/.gitattributes deleted file mode 100644 index 9e416188c5d..00000000000 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/BindingRestrictions.cs -crlf -/ExpandoObject.cs -crlf diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs index 2b1115cb49c..f4fd62fa89a 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs @@ -1,368 +1,368 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-#if CLR2
-using Microsoft.Scripting.Ast;
-#else
-using System.Linq.Expressions;
-#endif
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Dynamic.Utils;
-using System.Runtime.CompilerServices;
-
-namespace System.Dynamic {
-
- /// <summary>
- /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
- /// </summary>
-#if !SILVERLIGHT
- [DebuggerTypeProxy(typeof(BindingRestrictionsProxy)), DebuggerDisplay("{DebugView}")]
-#endif
- public abstract class BindingRestrictions {
- /// <summary>
- /// Represents an empty set of binding restrictions. This field is read only.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly BindingRestrictions Empty = new CustomRestriction(Expression.Constant(true));
-
- private const int TypeRestrictionHash = 0x10000000;
- private const int InstanceRestrictionHash = 0x20000000;
- private const int CustomRestrictionHash = 0x40000000;
-
- private BindingRestrictions() {
- }
-
- // Overridden by specialized subclasses
- internal abstract Expression GetExpression();
-
- /// <summary>
- /// Merges the set of binding restrictions with the current binding restrictions.
- /// </summary>
- /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param>
- /// <returns>The new set of binding restrictions.</returns>
- public BindingRestrictions Merge(BindingRestrictions restrictions) {
- ContractUtils.RequiresNotNull(restrictions, "restrictions");
- if (this == Empty) {
- return restrictions;
- }
- if (restrictions == Empty) {
- return this;
- }
- return new MergedRestriction(this, restrictions);
- }
-
- /// <summary>
- /// Creates the binding restriction that check the expression for runtime type identity.
- /// </summary>
- /// <param name="expression">The expression to test.</param>
- /// <param name="type">The exact type to test.</param>
- /// <returns>The new binding restrictions.</returns>
- public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) {
- ContractUtils.RequiresNotNull(expression, "expression");
- ContractUtils.RequiresNotNull(type, "type");
-
- return new TypeRestriction(expression, type);
- }
-
- /// <summary>
- /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object
- /// holds a null value, otherwise returns a type restriction.
- /// </summary>
- internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
- if (obj.Value == null && obj.HasValue) {
- return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
- } else {
- return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
- }
- }
-
- /// <summary>
- /// Creates the binding restriction that checks the expression for object instance identity.
- /// </summary>
- /// <param name="expression">The expression to test.</param>
- /// <param name="instance">The exact object instance to test.</param>
- /// <returns>The new binding restrictions.</returns>
- public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) {
- ContractUtils.RequiresNotNull(expression, "expression");
-
- return new InstanceRestriction(expression, instance);
- }
-
- /// <summary>
- /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
- /// </summary>
- /// <param name="expression">The expression expression the restrictions.</param>
- /// <returns>The new binding restrictions.</returns>
- /// <remarks>
- /// By convention, the general restrictions created by this method must only test
- /// immutable object properties.
- /// </remarks>
- public static BindingRestrictions GetExpressionRestriction(Expression expression) {
- ContractUtils.RequiresNotNull(expression, "expression");
- ContractUtils.Requires(expression.Type == typeof(bool), "expression");
- return new CustomRestriction(expression);
- }
-
- /// <summary>
- /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
- /// </summary>
- /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param>
- /// <returns>The new set of binding restrictions.</returns>
- public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) {
- BindingRestrictions res = BindingRestrictions.Empty;
- if (contributingObjects != null) {
- foreach (DynamicMetaObject mo in contributingObjects) {
- if (mo != null) {
- res = res.Merge(mo.Restrictions);
- }
- }
- }
- return res;
- }
-
- /// <summary>
- /// Builds a balanced tree of AndAlso nodes.
- /// We do this so the compiler won't stack overflow if we have many
- /// restrictions.
- /// </summary>
- private sealed class TestBuilder {
- private readonly Set<BindingRestrictions> _unique = new Set<BindingRestrictions>();
- private readonly Stack<AndNode> _tests = new Stack<AndNode>();
-
- private struct AndNode {
- internal int Depth;
- internal Expression Node;
- }
-
- internal void Append(BindingRestrictions restrictions) {
- if (_unique.Contains(restrictions)) {
- return;
- }
- _unique.Add(restrictions);
-
- Push(restrictions.GetExpression(), 0);
- }
-
- internal Expression ToExpression() {
- Expression result = _tests.Pop().Node;
- while (_tests.Count > 0) {
- result = Expression.AndAlso(_tests.Pop().Node, result);
- }
- return result;
- }
-
- private void Push(Expression node, int depth) {
- while (_tests.Count > 0 && _tests.Peek().Depth == depth) {
- node = Expression.AndAlso(_tests.Pop().Node, node);
- depth++;
- }
- _tests.Push(new AndNode { Node = node, Depth = depth });
- }
- }
-
- /// <summary>
- /// Creates the <see cref="Expression"/> representing the binding restrictions.
- /// </summary>
- /// <returns>The expression tree representing the restrictions.</returns>
- public Expression ToExpression() {
- // We could optimize this better, e.g. common subexpression elimination
- // But for now, it's good enough.
-
- if (this == Empty) {
- return Expression.Constant(true);
- }
-
- var testBuilder = new TestBuilder();
-
- // Visit the tree, left to right.
- // Use an explicit stack so we don't stack overflow.
- //
- // Left-most node is on top of the stack, so we always expand the
- // left most node each iteration.
- var stack = new Stack<BindingRestrictions>();
- stack.Push(this);
- do {
- var top = stack.Pop();
- var m = top as MergedRestriction;
- if (m != null) {
- stack.Push(m.Right);
- stack.Push(m.Left);
- } else {
- testBuilder.Append(top);
- }
- } while (stack.Count > 0);
-
- return testBuilder.ToExpression();
- }
-
- private sealed class MergedRestriction : BindingRestrictions {
- internal readonly BindingRestrictions Left;
- internal readonly BindingRestrictions Right;
-
- internal MergedRestriction(BindingRestrictions left, BindingRestrictions right) {
- Left = left;
- Right = right;
- }
- internal override Expression GetExpression() {
- throw ContractUtils.Unreachable;
- }
- }
-
- private sealed class CustomRestriction : BindingRestrictions {
- private readonly Expression _expression;
-
- internal CustomRestriction(Expression expression) {
- _expression = expression;
- }
-
- public override bool Equals(object obj) {
- var other = obj as CustomRestriction;
- return other != null && other._expression == _expression;
- }
-
- public override int GetHashCode() {
- return CustomRestrictionHash ^ _expression.GetHashCode();
- }
-
- internal override Expression GetExpression() {
- return _expression;
- }
- }
-
- private sealed class TypeRestriction : BindingRestrictions {
- private readonly Expression _expression;
- private readonly Type _type;
-
- internal TypeRestriction(Expression parameter, Type type) {
- _expression = parameter;
- _type = type;
- }
-
- public override bool Equals(object obj) {
- var other = obj as TypeRestriction;
- return other != null && TypeUtils.AreEquivalent(other._type, _type) && other._expression == _expression;
- }
-
- public override int GetHashCode() {
- return TypeRestrictionHash ^ _expression.GetHashCode() ^ _type.GetHashCode();
- }
-
- internal override Expression GetExpression() {
- return Expression.TypeEqual(_expression, _type);
- }
- }
-
- private sealed class InstanceRestriction : BindingRestrictions {
- private readonly Expression _expression;
- private readonly object _instance;
-
- internal InstanceRestriction(Expression parameter, object instance) {
- _expression = parameter;
- _instance = instance;
- }
-
- public override bool Equals(object obj) {
- var other = obj as InstanceRestriction;
- return other != null && other._instance == _instance && other._expression == _expression;
- }
-
- public override int GetHashCode() {
- return InstanceRestrictionHash ^ RuntimeHelpers.GetHashCode(_instance) ^ _expression.GetHashCode();
- }
-
- internal override Expression GetExpression() {
- if (_instance == null) {
- return Expression.Equal(
- Expression.Convert(_expression, typeof(object)),
- Expression.Constant(null)
- );
- }
-
- ParameterExpression temp = Expression.Parameter(typeof(object), null);
- return Expression.Block(
- new[] { temp },
- Expression.Assign(
- temp,
- Expression.Property(
- Expression.Constant(new WeakReference(_instance)),
- typeof(WeakReference).GetProperty("Target")
- )
- ),
- Expression.AndAlso(
- //check that WeekReference was not collected.
- Expression.NotEqual(temp, Expression.Constant(null)),
- Expression.Equal(
- Expression.Convert(_expression, typeof(object)),
- temp
- )
- )
- );
- }
- }
-
- private string DebugView {
- get { return ToExpression().ToString(); }
- }
-
- private sealed class BindingRestrictionsProxy {
- private readonly BindingRestrictions _node;
-
- public BindingRestrictionsProxy(BindingRestrictions node) {
- _node = node;
- }
-
- public bool IsEmpty {
- get { return _node == Empty; }
- }
-
- public Expression Test {
- get { return _node.ToExpression(); }
- }
-
- public BindingRestrictions[] Restrictions {
- get {
- var restrictions = new List<BindingRestrictions>();
-
- // Visit the tree, left to right
- //
- // Left-most node is on top of the stack, so we always expand the
- // left most node each iteration.
- var stack = new Stack<BindingRestrictions>();
- stack.Push(_node);
- do {
- var top = stack.Pop();
- var m = top as MergedRestriction;
- if (m != null) {
- stack.Push(m.Right);
- stack.Push(m.Left);
- } else {
- restrictions.Add(top);
- }
- } while (stack.Count > 0);
-
- return restrictions.ToArray();
- }
- }
-
- public override string ToString() {
- // To prevent fxcop warning about this field
- return _node.DebugView;
- }
- }
- }
-}
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if CLR2 +using Microsoft.Scripting.Ast; +#else +using System.Linq.Expressions; +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic.Utils; +using System.Runtime.CompilerServices; + +namespace System.Dynamic { + + /// <summary> + /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid. + /// </summary> +#if !SILVERLIGHT + [DebuggerTypeProxy(typeof(BindingRestrictionsProxy)), DebuggerDisplay("{DebugView}")] +#endif + public abstract class BindingRestrictions { + /// <summary> + /// Represents an empty set of binding restrictions. This field is read only. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + public static readonly BindingRestrictions Empty = new CustomRestriction(Expression.Constant(true)); + + private const int TypeRestrictionHash = 0x10000000; + private const int InstanceRestrictionHash = 0x20000000; + private const int CustomRestrictionHash = 0x40000000; + + private BindingRestrictions() { + } + + // Overridden by specialized subclasses + internal abstract Expression GetExpression(); + + /// <summary> + /// Merges the set of binding restrictions with the current binding restrictions. + /// </summary> + /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param> + /// <returns>The new set of binding restrictions.</returns> + public BindingRestrictions Merge(BindingRestrictions restrictions) { + ContractUtils.RequiresNotNull(restrictions, "restrictions"); + if (this == Empty) { + return restrictions; + } + if (restrictions == Empty) { + return this; + } + return new MergedRestriction(this, restrictions); + } + + /// <summary> + /// Creates the binding restriction that check the expression for runtime type identity. + /// </summary> + /// <param name="expression">The expression to test.</param> + /// <param name="type">The exact type to test.</param> + /// <returns>The new binding restrictions.</returns> + public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.RequiresNotNull(type, "type"); + + return new TypeRestriction(expression, type); + } + + /// <summary> + /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object + /// holds a null value, otherwise returns a type restriction. + /// </summary> + internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) { + if (obj.Value == null && obj.HasValue) { + return BindingRestrictions.GetInstanceRestriction(obj.Expression, null); + } else { + return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType); + } + } + + /// <summary> + /// Creates the binding restriction that checks the expression for object instance identity. + /// </summary> + /// <param name="expression">The expression to test.</param> + /// <param name="instance">The exact object instance to test.</param> + /// <returns>The new binding restrictions.</returns> + public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) { + ContractUtils.RequiresNotNull(expression, "expression"); + + return new InstanceRestriction(expression, instance); + } + + /// <summary> + /// Creates the binding restriction that checks the expression for arbitrary immutable properties. + /// </summary> + /// <param name="expression">The expression expression the restrictions.</param> + /// <returns>The new binding restrictions.</returns> + /// <remarks> + /// By convention, the general restrictions created by this method must only test + /// immutable object properties. + /// </remarks> + public static BindingRestrictions GetExpressionRestriction(Expression expression) { + ContractUtils.RequiresNotNull(expression, "expression"); + ContractUtils.Requires(expression.Type == typeof(bool), "expression"); + return new CustomRestriction(expression); + } + + /// <summary> + /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions. + /// </summary> + /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param> + /// <returns>The new set of binding restrictions.</returns> + public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) { + BindingRestrictions res = BindingRestrictions.Empty; + if (contributingObjects != null) { + foreach (DynamicMetaObject mo in contributingObjects) { + if (mo != null) { + res = res.Merge(mo.Restrictions); + } + } + } + return res; + } + + /// <summary> + /// Builds a balanced tree of AndAlso nodes. + /// We do this so the compiler won't stack overflow if we have many + /// restrictions. + /// </summary> + private sealed class TestBuilder { + private readonly Set<BindingRestrictions> _unique = new Set<BindingRestrictions>(); + private readonly Stack<AndNode> _tests = new Stack<AndNode>(); + + private struct AndNode { + internal int Depth; + internal Expression Node; + } + + internal void Append(BindingRestrictions restrictions) { + if (_unique.Contains(restrictions)) { + return; + } + _unique.Add(restrictions); + + Push(restrictions.GetExpression(), 0); + } + + internal Expression ToExpression() { + Expression result = _tests.Pop().Node; + while (_tests.Count > 0) { + result = Expression.AndAlso(_tests.Pop().Node, result); + } + return result; + } + + private void Push(Expression node, int depth) { + while (_tests.Count > 0 && _tests.Peek().Depth == depth) { + node = Expression.AndAlso(_tests.Pop().Node, node); + depth++; + } + _tests.Push(new AndNode { Node = node, Depth = depth }); + } + } + + /// <summary> + /// Creates the <see cref="Expression"/> representing the binding restrictions. + /// </summary> + /// <returns>The expression tree representing the restrictions.</returns> + public Expression ToExpression() { + // We could optimize this better, e.g. common subexpression elimination + // But for now, it's good enough. + + if (this == Empty) { + return Expression.Constant(true); + } + + var testBuilder = new TestBuilder(); + + // Visit the tree, left to right. + // Use an explicit stack so we don't stack overflow. + // + // Left-most node is on top of the stack, so we always expand the + // left most node each iteration. + var stack = new Stack<BindingRestrictions>(); + stack.Push(this); + do { + var top = stack.Pop(); + var m = top as MergedRestriction; + if (m != null) { + stack.Push(m.Right); + stack.Push(m.Left); + } else { + testBuilder.Append(top); + } + } while (stack.Count > 0); + + return testBuilder.ToExpression(); + } + + private sealed class MergedRestriction : BindingRestrictions { + internal readonly BindingRestrictions Left; + internal readonly BindingRestrictions Right; + + internal MergedRestriction(BindingRestrictions left, BindingRestrictions right) { + Left = left; + Right = right; + } + internal override Expression GetExpression() { + throw ContractUtils.Unreachable; + } + } + + private sealed class CustomRestriction : BindingRestrictions { + private readonly Expression _expression; + + internal CustomRestriction(Expression expression) { + _expression = expression; + } + + public override bool Equals(object obj) { + var other = obj as CustomRestriction; + return other != null && other._expression == _expression; + } + + public override int GetHashCode() { + return CustomRestrictionHash ^ _expression.GetHashCode(); + } + + internal override Expression GetExpression() { + return _expression; + } + } + + private sealed class TypeRestriction : BindingRestrictions { + private readonly Expression _expression; + private readonly Type _type; + + internal TypeRestriction(Expression parameter, Type type) { + _expression = parameter; + _type = type; + } + + public override bool Equals(object obj) { + var other = obj as TypeRestriction; + return other != null && TypeUtils.AreEquivalent(other._type, _type) && other._expression == _expression; + } + + public override int GetHashCode() { + return TypeRestrictionHash ^ _expression.GetHashCode() ^ _type.GetHashCode(); + } + + internal override Expression GetExpression() { + return Expression.TypeEqual(_expression, _type); + } + } + + private sealed class InstanceRestriction : BindingRestrictions { + private readonly Expression _expression; + private readonly object _instance; + + internal InstanceRestriction(Expression parameter, object instance) { + _expression = parameter; + _instance = instance; + } + + public override bool Equals(object obj) { + var other = obj as InstanceRestriction; + return other != null && other._instance == _instance && other._expression == _expression; + } + + public override int GetHashCode() { + return InstanceRestrictionHash ^ RuntimeHelpers.GetHashCode(_instance) ^ _expression.GetHashCode(); + } + + internal override Expression GetExpression() { + if (_instance == null) { + return Expression.Equal( + Expression.Convert(_expression, typeof(object)), + Expression.Constant(null) + ); + } + + ParameterExpression temp = Expression.Parameter(typeof(object), null); + return Expression.Block( + new[] { temp }, + Expression.Assign( + temp, + Expression.Property( + Expression.Constant(new WeakReference(_instance)), + typeof(WeakReference).GetProperty("Target") + ) + ), + Expression.AndAlso( + //check that WeekReference was not collected. + Expression.NotEqual(temp, Expression.Constant(null)), + Expression.Equal( + Expression.Convert(_expression, typeof(object)), + temp + ) + ) + ); + } + } + + private string DebugView { + get { return ToExpression().ToString(); } + } + + private sealed class BindingRestrictionsProxy { + private readonly BindingRestrictions _node; + + public BindingRestrictionsProxy(BindingRestrictions node) { + _node = node; + } + + public bool IsEmpty { + get { return _node == Empty; } + } + + public Expression Test { + get { return _node.ToExpression(); } + } + + public BindingRestrictions[] Restrictions { + get { + var restrictions = new List<BindingRestrictions>(); + + // Visit the tree, left to right + // + // Left-most node is on top of the stack, so we always expand the + // left most node each iteration. + var stack = new Stack<BindingRestrictions>(); + stack.Push(_node); + do { + var top = stack.Pop(); + var m = top as MergedRestriction; + if (m != null) { + stack.Push(m.Right); + stack.Push(m.Left); + } else { + restrictions.Add(top); + } + } while (stack.Count > 0); + + return restrictions.ToArray(); + } + } + + public override string ToString() { + // To prevent fxcop warning about this field + return _node.DebugView; + } + } + } +} diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/ExpandoObject.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/ExpandoObject.cs index 6ba7c7d510b..0eb1fcbc28f 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/ExpandoObject.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/ExpandoObject.cs @@ -1,1100 +1,1100 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-#if CLR2
-using Microsoft.Scripting.Ast;
-using Microsoft.Scripting.Utils;
-#else
-using System.Linq.Expressions;
-#endif
-#if SILVERLIGHT
-using System.Core;
-#endif
-
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Dynamic;
-using System.Dynamic.Utils;
-using System.Runtime.CompilerServices;
-
-namespace System.Dynamic {
- /// <summary>
- /// Represents an object with members that can be dynamically added and removed at runtime.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
- public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary<string, object>, INotifyPropertyChanged {
- internal readonly object LockObject; // the readonly field is used for locking the Expando object
- private ExpandoData _data; // the data currently being held by the Expando object
- private int _count; // the count of available members
-
- internal readonly static object Uninitialized = new object(); // A marker object used to identify that a value is uninitialized.
-
- internal const int AmbiguousMatchFound = -2; // The value is used to indicate there exists ambiguous match in the Expando object
- internal const int NoMatch = -1; // The value is used to indicate there is no matching member
-
- private PropertyChangedEventHandler _propertyChanged;
-
- /// <summary>
- /// Creates a new ExpandoObject with no members.
- /// </summary>
- public ExpandoObject() {
- _data = ExpandoData.Empty;
- LockObject = new object();
- }
-
- #region Get/Set/Delete Helpers
-
- /// <summary>
- /// Try to get the data stored for the specified class at the specified index. If the
- /// class has changed a full lookup for the slot will be performed and the correct
- /// value will be retrieved.
- /// </summary>
- internal bool TryGetValue(object indexClass, int index, string name, bool ignoreCase, out object value) {
- // read the data now. The data is immutable so we get a consistent view.
- // If there's a concurrent writer they will replace data and it just appears
- // that we won the race
- ExpandoData data = _data;
- if (data.Class != indexClass || ignoreCase) {
- /* Re-search for the index matching the name here if
- * 1) the class has changed, we need to get the correct index and return
- * the value there.
- * 2) the search is case insensitive:
- * a. the member specified by index may be deleted, but there might be other
- * members matching the name if the binder is case insensitive.
- * b. the member that exactly matches the name didn't exist before and exists now,
- * need to find the exact match.
- */
- index = data.Class.GetValueIndex(name, ignoreCase, this);
- if (index == ExpandoObject.AmbiguousMatchFound) {
- throw Error.AmbiguousMatchInExpandoObject(name);
- }
- }
-
- if (index == ExpandoObject.NoMatch) {
- value = null;
- return false;
- }
-
- // Capture the value into a temp, so it doesn't get mutated after we check
- // for Uninitialized.
- object temp = data[index];
- if (temp == Uninitialized) {
- value = null;
- return false;
- }
-
- // index is now known to be correct
- value = temp;
- return true;
- }
-
- /// <summary>
- /// Sets the data for the specified class at the specified index. If the class has
- /// changed then a full look for the slot will be performed. If the new class does
- /// not have the provided slot then the Expando's class will change. Only case sensitive
- /// setter is supported in ExpandoObject.
- /// </summary>
- internal void TrySetValue(object indexClass, int index, object value, string name, bool ignoreCase, bool add) {
- ExpandoData data;
- object oldValue;
-
- lock (LockObject) {
- data = _data;
-
- if (data.Class != indexClass || ignoreCase) {
- // The class has changed or we are doing a case-insensitive search,
- // we need to get the correct index and set the value there. If we
- // don't have the value then we need to promote the class - that
- // should only happen when we have multiple concurrent writers.
- index = data.Class.GetValueIndex(name, ignoreCase, this);
- if (index == ExpandoObject.AmbiguousMatchFound) {
- throw Error.AmbiguousMatchInExpandoObject(name);
- }
- if (index == ExpandoObject.NoMatch) {
- // Before creating a new class with the new member, need to check
- // if there is the exact same member but is deleted. We should reuse
- // the class if there is such a member.
- int exactMatch = ignoreCase ?
- data.Class.GetValueIndexCaseSensitive(name) :
- index;
- if (exactMatch != ExpandoObject.NoMatch) {
- Debug.Assert(data[exactMatch] == Uninitialized);
- index = exactMatch;
- } else {
- ExpandoClass newClass = data.Class.FindNewClass(name);
- data = PromoteClassCore(data.Class, newClass);
- // After the class promotion, there must be an exact match,
- // so we can do case-sensitive search here.
- index = data.Class.GetValueIndexCaseSensitive(name);
- Debug.Assert(index != ExpandoObject.NoMatch);
- }
- }
- }
-
- // Setting an uninitialized member increases the count of available members
- oldValue = data[index];
- if (oldValue == Uninitialized) {
- _count++;
- } else if (add) {
- throw Error.SameKeyExistsInExpando(name);
- }
-
- data[index] = value;
- }
-
- // Notify property changed, outside of the lock.
- var propertyChanged = _propertyChanged;
- if (propertyChanged != null && value != oldValue) {
- // Use the canonical case for the key.
- propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[index]));
- }
- }
-
- /// <summary>
- /// Deletes the data stored for the specified class at the specified index.
- /// </summary>
- internal bool TryDeleteValue(object indexClass, int index, string name, bool ignoreCase, object deleteValue) {
- ExpandoData data;
- lock (LockObject) {
- data = _data;
-
- if (data.Class != indexClass || ignoreCase) {
- // the class has changed or we are doing a case-insensitive search,
- // we need to get the correct index. If there is no associated index
- // we simply can't have the value and we return false.
- index = data.Class.GetValueIndex(name, ignoreCase, this);
- if (index == ExpandoObject.AmbiguousMatchFound) {
- throw Error.AmbiguousMatchInExpandoObject(name);
- }
- }
- if (index == ExpandoObject.NoMatch) {
- return false;
- }
-
- object oldValue = data[index];
- if (oldValue == Uninitialized) {
- return false;
- }
-
- // Make sure the value matches, if requested.
- //
- // It's a shame we have to call Equals with the lock held but
- // there doesn't seem to be a good way around that, and
- // ConcurrentDictionary in mscorlib does the same thing.
- if (deleteValue != Uninitialized && !object.Equals(oldValue, deleteValue)) {
- return false;
- }
-
- data[index] = Uninitialized;
-
- // Deleting an available member decreases the count of available members
- _count--;
- }
-
- // Notify property changed, outside of the lock.
- var propertyChanged = _propertyChanged;
- if (propertyChanged != null) {
- // Use the canonical case for the key.
- propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[index]));
- }
-
- return true;
- }
-
- /// <summary>
- /// Returns true if the member at the specified index has been deleted,
- /// otherwise false. Call this function holding the lock.
- /// </summary>
- internal bool IsDeletedMember(int index) {
- Debug.Assert(index >= 0 && index <= _data.Length);
-
- if (index == _data.Length) {
- // The member is a newly added by SetMemberBinder and not in data yet
- return false;
- }
-
- return _data[index] == ExpandoObject.Uninitialized;
- }
-
- /// <summary>
- /// Exposes the ExpandoClass which we've associated with this
- /// Expando object. Used for type checks in rules.
- /// </summary>
- internal ExpandoClass Class {
- get {
- return _data.Class;
- }
- }
-
- /// <summary>
- /// Promotes the class from the old type to the new type and returns the new
- /// ExpandoData object.
- /// </summary>
- private ExpandoData PromoteClassCore(ExpandoClass oldClass, ExpandoClass newClass) {
- Debug.Assert(oldClass != newClass);
-
- lock (LockObject) {
- if (_data.Class == oldClass) {
- _data = _data.UpdateClass(newClass);
- }
- return _data;
- }
- }
-
- /// <summary>
- /// Internal helper to promote a class. Called from our RuntimeOps helper. This
- /// version simply doesn't expose the ExpandoData object which is a private
- /// data structure.
- /// </summary>
- internal void PromoteClass(object oldClass, object newClass) {
- PromoteClassCore((ExpandoClass)oldClass, (ExpandoClass)newClass);
- }
-
- #endregion
-
- #region IDynamicMetaObjectProvider Members
-
- DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
- return new MetaExpando(parameter, this);
- }
- #endregion
-
- #region Helper methods
- private void TryAddMember(string key, object value) {
- ContractUtils.RequiresNotNull(key, "key");
- // Pass null to the class, which forces lookup.
- TrySetValue(null, -1, value, key, false, true);
- }
-
- private bool TryGetValueForKey(string key, out object value) {
- // Pass null to the class, which forces lookup.
- return TryGetValue(null, -1, key, false, out value);
- }
-
- private bool ExpandoContainsKey(string key) {
- return _data.Class.GetValueIndexCaseSensitive(key) >= 0;
- }
-
- // We create a non-generic type for the debug view for each different collection type
- // that uses DebuggerTypeProxy, instead of defining a generic debug view type and
- // using different instantiations. The reason for this is that support for generics
- // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only
- // open types (from MSDN http://msdn.microsoft.com/en-us/library/d8eyd8zc.aspx).
- private sealed class KeyCollectionDebugView {
- private ICollection<string> collection;
- public KeyCollectionDebugView(ICollection<string> collection) {
- Debug.Assert(collection != null);
- this.collection = collection;
- }
-
- [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
- public string[] Items {
- get {
- string[] items = new string[collection.Count];
- collection.CopyTo(items, 0);
- return items;
- }
- }
- }
-
- [DebuggerTypeProxy(typeof(KeyCollectionDebugView))]
- [DebuggerDisplay("Count = {Count}")]
- private class KeyCollection : ICollection<string> {
- private readonly ExpandoObject _expando;
- private readonly int _expandoVersion;
- private readonly int _expandoCount;
- private readonly ExpandoData _expandoData;
-
- internal KeyCollection(ExpandoObject expando) {
- lock (expando.LockObject) {
- _expando = expando;
- _expandoVersion = expando._data.Version;
- _expandoCount = expando._count;
- _expandoData = expando._data;
- }
- }
-
- private void CheckVersion() {
- if (_expando._data.Version != _expandoVersion || _expandoData != _expando._data) {
- //the underlying expando object has changed
- throw Error.CollectionModifiedWhileEnumerating();
- }
- }
-
- #region ICollection<string> Members
-
- public void Add(string item) {
- throw Error.CollectionReadOnly();
- }
-
- public void Clear() {
- throw Error.CollectionReadOnly();
- }
-
- public bool Contains(string item) {
- lock (_expando.LockObject) {
- CheckVersion();
- return _expando.ExpandoContainsKey(item);
- }
- }
-
- public void CopyTo(string[] array, int arrayIndex) {
- ContractUtils.RequiresNotNull(array, "array");
- ContractUtils.RequiresArrayRange(array, arrayIndex, _expandoCount, "arrayIndex", "Count");
- lock (_expando.LockObject) {
- CheckVersion();
- ExpandoData data = _expando._data;
- for (int i = 0; i < data.Class.Keys.Length; i++) {
- if (data[i] != Uninitialized) {
- array[arrayIndex++] = data.Class.Keys[i];
- }
- }
- }
- }
-
- public int Count {
- get {
- CheckVersion();
- return _expandoCount;
- }
- }
-
- public bool IsReadOnly {
- get { return true; }
- }
-
- public bool Remove(string item) {
- throw Error.CollectionReadOnly();
- }
-
- #endregion
-
- #region IEnumerable<string> Members
-
- public IEnumerator<string> GetEnumerator() {
- for (int i = 0, n = _expandoData.Class.Keys.Length; i < n; i++) {
- CheckVersion();
- if (_expandoData[i] != Uninitialized) {
- yield return _expandoData.Class.Keys[i];
- }
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- return GetEnumerator();
- }
-
- #endregion
- }
-
- // We create a non-generic type for the debug view for each different collection type
- // that uses DebuggerTypeProxy, instead of defining a generic debug view type and
- // using different instantiations. The reason for this is that support for generics
- // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only
- // open types (from MSDN http://msdn.microsoft.com/en-us/library/d8eyd8zc.aspx).
- private sealed class ValueCollectionDebugView {
- private ICollection<object> collection;
- public ValueCollectionDebugView(ICollection<object> collection) {
- Debug.Assert(collection != null);
- this.collection = collection;
- }
-
- [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
- public object[] Items {
- get {
- object[] items = new object[collection.Count];
- collection.CopyTo(items, 0);
- return items;
- }
- }
- }
-
- [DebuggerTypeProxy(typeof(ValueCollectionDebugView))]
- [DebuggerDisplay("Count = {Count}")]
- private class ValueCollection : ICollection<object> {
- private readonly ExpandoObject _expando;
- private readonly int _expandoVersion;
- private readonly int _expandoCount;
- private readonly ExpandoData _expandoData;
-
- internal ValueCollection(ExpandoObject expando) {
- lock (expando.LockObject) {
- _expando = expando;
- _expandoVersion = expando._data.Version;
- _expandoCount = expando._count;
- _expandoData = expando._data;
- }
- }
-
- private void CheckVersion() {
- if (_expando._data.Version != _expandoVersion || _expandoData != _expando._data) {
- //the underlying expando object has changed
- throw Error.CollectionModifiedWhileEnumerating();
- }
- }
-
- #region ICollection<string> Members
-
- public void Add(object item) {
- throw Error.CollectionReadOnly();
- }
-
- public void Clear() {
- throw Error.CollectionReadOnly();
- }
-
- public bool Contains(object item) {
- lock (_expando.LockObject) {
- CheckVersion();
-
- ExpandoData data = _expando._data;
- for (int i = 0; i < data.Class.Keys.Length; i++) {
-
- // See comment in TryDeleteValue; it's okay to call
- // object.Equals with the lock held.
- if (object.Equals(data[i], item)) {
- return true;
- }
- }
- return false;
- }
- }
-
- public void CopyTo(object[] array, int arrayIndex) {
- ContractUtils.RequiresNotNull(array, "array");
- ContractUtils.RequiresArrayRange(array, arrayIndex, _expandoCount, "arrayIndex", "Count");
- lock (_expando.LockObject) {
- CheckVersion();
- ExpandoData data = _expando._data;
- for (int i = 0; i < data.Class.Keys.Length; i++) {
- if (data[i] != Uninitialized) {
- array[arrayIndex++] = data[i];
- }
- }
- }
- }
-
- public int Count {
- get {
- CheckVersion();
- return _expandoCount;
- }
- }
-
- public bool IsReadOnly {
- get { return true; }
- }
-
- public bool Remove(object item) {
- throw Error.CollectionReadOnly();
- }
-
- #endregion
-
- #region IEnumerable<string> Members
-
- public IEnumerator<object> GetEnumerator() {
- ExpandoData data = _expando._data;
- for (int i = 0; i < data.Class.Keys.Length; i++) {
- CheckVersion();
- // Capture the value into a temp so we don't inadvertently
- // return Uninitialized.
- object temp = data[i];
- if (temp != Uninitialized) {
- yield return temp;
- }
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- return GetEnumerator();
- }
-
- #endregion
- }
-
- #endregion
-
- #region IDictionary<string, object> Members
- ICollection<string> IDictionary<string, object>.Keys {
- get {
- return new KeyCollection(this);
- }
- }
-
- ICollection<object> IDictionary<string, object>.Values {
- get {
- return new ValueCollection(this);
- }
- }
-
- object IDictionary<string, object>.this[string key] {
- get {
- object value;
- if (!TryGetValueForKey(key, out value)) {
- throw Error.KeyDoesNotExistInExpando(key);
- }
- return value;
- }
- set {
- ContractUtils.RequiresNotNull(key, "key");
- // Pass null to the class, which forces lookup.
- TrySetValue(null, -1, value, key, false, false);
- }
- }
-
- void IDictionary<string, object>.Add(string key, object value) {
- this.TryAddMember(key, value);
- }
-
- bool IDictionary<string, object>.ContainsKey(string key) {
- ContractUtils.RequiresNotNull(key, "key");
-
- ExpandoData data = _data;
- int index = data.Class.GetValueIndexCaseSensitive(key);
- return index >= 0 && data[index] != Uninitialized;
- }
-
- bool IDictionary<string, object>.Remove(string key) {
- ContractUtils.RequiresNotNull(key, "key");
- // Pass null to the class, which forces lookup.
- return TryDeleteValue(null, -1, key, false, Uninitialized);
- }
-
- bool IDictionary<string, object>.TryGetValue(string key, out object value) {
- return TryGetValueForKey(key, out value);
- }
-
- #endregion
-
- #region ICollection<KeyValuePair<string, object>> Members
- int ICollection<KeyValuePair<string, object>>.Count {
- get {
- return _count;
- }
- }
-
- bool ICollection<KeyValuePair<string, object>>.IsReadOnly {
- get { return false; }
- }
-
- void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) {
- TryAddMember(item.Key, item.Value);
- }
-
- void ICollection<KeyValuePair<string, object>>.Clear() {
- // We remove both class and data!
- ExpandoData data;
- lock (LockObject) {
- data = _data;
- _data = ExpandoData.Empty;
- _count = 0;
- }
-
- // Notify property changed for all properties.
- var propertyChanged = _propertyChanged;
- if (propertyChanged != null) {
- for (int i = 0, n = data.Class.Keys.Length; i < n; i++) {
- if (data[i] != Uninitialized) {
- propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[i]));
- }
- }
- }
- }
-
- bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) {
- object value;
- if (!TryGetValueForKey(item.Key, out value)) {
- return false;
- }
-
- return object.Equals(value, item.Value);
- }
-
- void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) {
- ContractUtils.RequiresNotNull(array, "array");
- ContractUtils.RequiresArrayRange(array, arrayIndex, _count, "arrayIndex", "Count");
-
- // We want this to be atomic and not throw
- lock (LockObject) {
- foreach (KeyValuePair<string, object> item in this) {
- array[arrayIndex++] = item;
- }
- }
- }
-
- bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) {
- return TryDeleteValue(null, -1, item.Key, false, item.Value);
- }
- #endregion
-
- #region IEnumerable<KeyValuePair<string, object>> Member
-
- IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() {
- ExpandoData data = _data;
- return GetExpandoEnumerator(data, data.Version);
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- ExpandoData data = _data;
- return GetExpandoEnumerator(data, data.Version);
- }
-
- // Note: takes the data and version as parameters so they will be
- // captured before the first call to MoveNext().
- private IEnumerator<KeyValuePair<string, object>> GetExpandoEnumerator(ExpandoData data, int version) {
- for (int i = 0; i < data.Class.Keys.Length; i++) {
- if (_data.Version != version || data != _data) {
- // The underlying expando object has changed:
- // 1) the version of the expando data changed
- // 2) the data object is changed
- throw Error.CollectionModifiedWhileEnumerating();
- }
- // Capture the value into a temp so we don't inadvertently
- // return Uninitialized.
- object temp = data[i];
- if (temp != Uninitialized) {
- yield return new KeyValuePair<string,object>(data.Class.Keys[i], temp);
- }
- }
- }
- #endregion
-
- #region MetaExpando
-
- private class MetaExpando : DynamicMetaObject {
- public MetaExpando(Expression expression, ExpandoObject value)
- : base(expression, BindingRestrictions.Empty, value) {
- }
-
- private DynamicMetaObject BindGetOrInvokeMember(DynamicMetaObjectBinder binder, string name, bool ignoreCase, DynamicMetaObject fallback, Func<DynamicMetaObject, DynamicMetaObject> fallbackInvoke) {
- ExpandoClass klass = Value.Class;
-
- //try to find the member, including the deleted members
- int index = klass.GetValueIndex(name, ignoreCase, Value);
-
- ParameterExpression value = Expression.Parameter(typeof(object), "value");
-
- Expression tryGetValue = Expression.Call(
- typeof(RuntimeOps).GetMethod("ExpandoTryGetValue"),
- GetLimitedSelf(),
- Expression.Constant(klass, typeof(object)),
- Expression.Constant(index),
- Expression.Constant(name),
- Expression.Constant(ignoreCase),
- value
- );
-
- var result = new DynamicMetaObject(value, BindingRestrictions.Empty);
- if (fallbackInvoke != null) {
- result = fallbackInvoke(result);
- }
-
- result = new DynamicMetaObject(
- Expression.Block(
- new[] { value },
- Expression.Condition(
- tryGetValue,
- result.Expression,
- fallback.Expression,
- typeof(object)
- )
- ),
- result.Restrictions.Merge(fallback.Restrictions)
- );
-
- return AddDynamicTestAndDefer(binder, Value.Class, null, result);
- }
-
- public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
- ContractUtils.RequiresNotNull(binder, "binder");
- return BindGetOrInvokeMember(
- binder,
- binder.Name,
- binder.IgnoreCase,
- binder.FallbackGetMember(this),
- null
- );
- }
-
- public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
- ContractUtils.RequiresNotNull(binder, "binder");
- return BindGetOrInvokeMember(
- binder,
- binder.Name,
- binder.IgnoreCase,
- binder.FallbackInvokeMember(this, args),
- value => binder.FallbackInvoke(value, args, null)
- );
- }
-
- public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
- ContractUtils.RequiresNotNull(binder, "binder");
- ContractUtils.RequiresNotNull(value, "value");
-
- ExpandoClass klass;
- int index;
-
- ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, Value, out klass, out index);
-
- return AddDynamicTestAndDefer(
- binder,
- klass,
- originalClass,
- new DynamicMetaObject(
- Expression.Call(
- typeof(RuntimeOps).GetMethod("ExpandoTrySetValue"),
- GetLimitedSelf(),
- Expression.Constant(klass, typeof(object)),
- Expression.Constant(index),
- Expression.Convert(value.Expression, typeof(object)),
- Expression.Constant(binder.Name),
- Expression.Constant(binder.IgnoreCase)
- ),
- BindingRestrictions.Empty
- )
- );
- }
-
- public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
- ContractUtils.RequiresNotNull(binder, "binder");
-
- int index = Value.Class.GetValueIndex(binder.Name, binder.IgnoreCase, Value);
-
- Expression tryDelete = Expression.Call(
- typeof(RuntimeOps).GetMethod("ExpandoTryDeleteValue"),
- GetLimitedSelf(),
- Expression.Constant(Value.Class, typeof(object)),
- Expression.Constant(index),
- Expression.Constant(binder.Name),
- Expression.Constant(binder.IgnoreCase)
- );
- DynamicMetaObject fallback = binder.FallbackDeleteMember(this);
-
- DynamicMetaObject target = new DynamicMetaObject(
- Expression.IfThen(Expression.Not(tryDelete), fallback.Expression),
- fallback.Restrictions
- );
-
- return AddDynamicTestAndDefer(binder, Value.Class, null, target);
- }
-
- public override IEnumerable<string> GetDynamicMemberNames() {
- var expandoData = Value._data;
- var klass = expandoData.Class;
- for (int i = 0; i < klass.Keys.Length; i++) {
- object val = expandoData[i];
- if (val != ExpandoObject.Uninitialized) {
- yield return klass.Keys[i];
- }
- }
- }
-
- /// <summary>
- /// Adds a dynamic test which checks if the version has changed. The test is only necessary for
- /// performance as the methods will do the correct thing if called with an incorrect version.
- /// </summary>
- private DynamicMetaObject AddDynamicTestAndDefer(DynamicMetaObjectBinder binder, ExpandoClass klass, ExpandoClass originalClass, DynamicMetaObject succeeds) {
-
- Expression ifTestSucceeds = succeeds.Expression;
- if (originalClass != null) {
- // we are accessing a member which has not yet been defined on this class.
- // We force a class promotion after the type check. If the class changes the
- // promotion will fail and the set/delete will do a full lookup using the new
- // class to discover the name.
- Debug.Assert(originalClass != klass);
-
- ifTestSucceeds = Expression.Block(
- Expression.Call(
- null,
- typeof(RuntimeOps).GetMethod("ExpandoPromoteClass"),
- GetLimitedSelf(),
- Expression.Constant(originalClass, typeof(object)),
- Expression.Constant(klass, typeof(object))
- ),
- succeeds.Expression
- );
- }
-
- return new DynamicMetaObject(
- Expression.Condition(
- Expression.Call(
- null,
- typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"),
- GetLimitedSelf(),
- Expression.Constant(originalClass ?? klass, typeof(object))
- ),
- ifTestSucceeds,
- binder.GetUpdateExpression(ifTestSucceeds.Type)
- ),
- GetRestrictions().Merge(succeeds.Restrictions)
- );
- }
-
- /// <summary>
- /// Gets the class and the index associated with the given name. Does not update the expando object. Instead
- /// this returns both the original and desired new class. A rule is created which includes the test for the
- /// original class, the promotion to the new class, and the set/delete based on the class post-promotion.
- /// </summary>
- private ExpandoClass GetClassEnsureIndex(string name, bool caseInsensitive, ExpandoObject obj, out ExpandoClass klass, out int index) {
- ExpandoClass originalClass = Value.Class;
-
- index = originalClass.GetValueIndex(name, caseInsensitive, obj) ;
- if (index == ExpandoObject.AmbiguousMatchFound) {
- klass = originalClass;
- return null;
- }
- if (index == ExpandoObject.NoMatch) {
- // go ahead and find a new class now...
- ExpandoClass newClass = originalClass.FindNewClass(name);
-
- klass = newClass;
- index = newClass.GetValueIndexCaseSensitive(name);
-
- Debug.Assert(index != ExpandoObject.NoMatch);
- return originalClass;
- } else {
- klass = originalClass;
- return null;
- }
- }
-
- /// <summary>
- /// Returns our Expression converted to our known LimitType
- /// </summary>
- private Expression GetLimitedSelf() {
- if (TypeUtils.AreEquivalent(Expression.Type, LimitType)) {
- return Expression;
- }
- return Expression.Convert(Expression, LimitType);
- }
-
- /// <summary>
- /// Returns a Restrictions object which includes our current restrictions merged
- /// with a restriction limiting our type
- /// </summary>
- private BindingRestrictions GetRestrictions() {
- Debug.Assert(Restrictions == BindingRestrictions.Empty, "We don't merge, restrictions are always empty");
-
- return BindingRestrictions.GetTypeRestriction(this);
- }
-
- public new ExpandoObject Value {
- get {
- return (ExpandoObject)base.Value;
- }
- }
- }
-
- #endregion
-
- #region ExpandoData
-
- /// <summary>
- /// Stores the class and the data associated with the class as one atomic
- /// pair. This enables us to do a class check in a thread safe manner w/o
- /// requiring locks.
- /// </summary>
- private class ExpandoData {
- internal static ExpandoData Empty = new ExpandoData();
-
- /// <summary>
- /// the dynamically assigned class associated with the Expando object
- /// </summary>
- internal readonly ExpandoClass Class;
-
- /// <summary>
- /// data stored in the expando object, key names are stored in the class.
- ///
- /// Expando._data must be locked when mutating the value. Otherwise a copy of it
- /// could be made and lose values.
- /// </summary>
- private readonly object[] _dataArray;
-
- /// <summary>
- /// Indexer for getting/setting the data
- /// </summary>
- internal object this[int index] {
- get {
- return _dataArray[index];
- }
- set {
- //when the array is updated, version increases, even the new value is the same
- //as previous. Dictionary type has the same behavior.
- _version++;
- _dataArray[index] = value;
- }
- }
-
- internal int Version {
- get { return _version; }
- }
-
- internal int Length {
- get { return _dataArray.Length; }
- }
-
- /// <summary>
- /// Constructs an empty ExpandoData object with the empty class and no data.
- /// </summary>
- private ExpandoData() {
- Class = ExpandoClass.Empty;
- _dataArray = new object[0];
- }
-
- /// <summary>
- /// the version of the ExpandoObject that tracks set and delete operations
- /// </summary>
- private int _version;
-
- /// <summary>
- /// Constructs a new ExpandoData object with the specified class and data.
- /// </summary>
- internal ExpandoData(ExpandoClass klass, object[] data, int version) {
- Class = klass;
- _dataArray = data;
- _version = version;
- }
-
- /// <summary>
- /// Update the associated class and increases the storage for the data array if needed.
- /// </summary>
- /// <returns></returns>
- internal ExpandoData UpdateClass(ExpandoClass newClass) {
- if (_dataArray.Length >= newClass.Keys.Length) {
- // we have extra space in our buffer, just initialize it to Uninitialized.
- this[newClass.Keys.Length - 1] = ExpandoObject.Uninitialized;
- return new ExpandoData(newClass, this._dataArray, this._version);
- } else {
- // we've grown too much - we need a new object array
- int oldLength = _dataArray.Length;
- object[] arr = new object[GetAlignedSize(newClass.Keys.Length)];
- Array.Copy(_dataArray, arr, _dataArray.Length);
- ExpandoData newData = new ExpandoData(newClass, arr, this._version);
- newData[oldLength] = ExpandoObject.Uninitialized;
- return newData;
- }
- }
-
- private static int GetAlignedSize(int len) {
- // the alignment of the array for storage of values (must be a power of two)
- const int DataArrayAlignment = 8;
-
- // round up and then mask off lower bits
- return (len + (DataArrayAlignment - 1)) & (~(DataArrayAlignment - 1));
- }
- }
-
- #endregion
-
- #region INotifyPropertyChanged Members
-
- event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged {
- add { _propertyChanged += value; }
- remove { _propertyChanged -= value; }
- }
-
- #endregion
- }
-}
-
-namespace System.Runtime.CompilerServices {
-
- //
- // Note: these helpers are kept as simple wrappers so they have a better
- // chance of being inlined.
- //
- public static partial class RuntimeOps {
-
- /// <summary>
- /// Gets the value of an item in an expando object.
- /// </summary>
- /// <param name="expando">The expando object.</param>
- /// <param name="indexClass">The class of the expando object.</param>
- /// <param name="index">The index of the member.</param>
- /// <param name="name">The name of the member.</param>
- /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param>
- /// <param name="value">The out parameter containing the value of the member.</param>
- /// <returns>True if the member exists in the expando object, otherwise false.</returns>
- [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
- public static bool ExpandoTryGetValue(ExpandoObject expando, object indexClass, int index, string name, bool ignoreCase, out object value) {
- return expando.TryGetValue(indexClass, index, name, ignoreCase, out value);
- }
-
- /// <summary>
- /// Sets the value of an item in an expando object.
- /// </summary>
- /// <param name="expando">The expando object.</param>
- /// <param name="indexClass">The class of the expando object.</param>
- /// <param name="index">The index of the member.</param>
- /// <param name="value">The value of the member.</param>
- /// <param name="name">The name of the member.</param>
- /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param>
- /// <returns>
- /// Returns the index for the set member.
- /// </returns>
- [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
- public static object ExpandoTrySetValue(ExpandoObject expando, object indexClass, int index, object value, string name, bool ignoreCase) {
- expando.TrySetValue(indexClass, index, value, name, ignoreCase, false);
- return value;
- }
-
- /// <summary>
- /// Deletes the value of an item in an expando object.
- /// </summary>
- /// <param name="expando">The expando object.</param>
- /// <param name="indexClass">The class of the expando object.</param>
- /// <param name="index">The index of the member.</param>
- /// <param name="name">The name of the member.</param>
- /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param>
- /// <returns>true if the item was successfully removed; otherwise, false.</returns>
- [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
- public static bool ExpandoTryDeleteValue(ExpandoObject expando, object indexClass, int index, string name, bool ignoreCase) {
- return expando.TryDeleteValue(indexClass, index, name, ignoreCase, ExpandoObject.Uninitialized);
- }
-
- /// <summary>
- /// Checks the version of the expando object.
- /// </summary>
- /// <param name="expando">The expando object.</param>
- /// <param name="version">The version to check.</param>
- /// <returns>true if the version is equal; otherwise, false.</returns>
- [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
- public static bool ExpandoCheckVersion(ExpandoObject expando, object version) {
- return expando.Class == version;
- }
-
- /// <summary>
- /// Promotes an expando object from one class to a new class.
- /// </summary>
- /// <param name="expando">The expando object.</param>
- /// <param name="oldClass">The old class of the expando object.</param>
- /// <param name="newClass">The new class of the expando object.</param>
- [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
- public static void ExpandoPromoteClass(ExpandoObject expando, object oldClass, object newClass) {
- expando.PromoteClass(oldClass, newClass);
- }
- }
-}
-
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if CLR2 +using Microsoft.Scripting.Ast; +using Microsoft.Scripting.Utils; +#else +using System.Linq.Expressions; +#endif +#if SILVERLIGHT +using System.Core; +#endif + +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Dynamic; +using System.Dynamic.Utils; +using System.Runtime.CompilerServices; + +namespace System.Dynamic { + /// <summary> + /// Represents an object with members that can be dynamically added and removed at runtime. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary<string, object>, INotifyPropertyChanged { + internal readonly object LockObject; // the readonly field is used for locking the Expando object + private ExpandoData _data; // the data currently being held by the Expando object + private int _count; // the count of available members + + internal readonly static object Uninitialized = new object(); // A marker object used to identify that a value is uninitialized. + + internal const int AmbiguousMatchFound = -2; // The value is used to indicate there exists ambiguous match in the Expando object + internal const int NoMatch = -1; // The value is used to indicate there is no matching member + + private PropertyChangedEventHandler _propertyChanged; + + /// <summary> + /// Creates a new ExpandoObject with no members. + /// </summary> + public ExpandoObject() { + _data = ExpandoData.Empty; + LockObject = new object(); + } + + #region Get/Set/Delete Helpers + + /// <summary> + /// Try to get the data stored for the specified class at the specified index. If the + /// class has changed a full lookup for the slot will be performed and the correct + /// value will be retrieved. + /// </summary> + internal bool TryGetValue(object indexClass, int index, string name, bool ignoreCase, out object value) { + // read the data now. The data is immutable so we get a consistent view. + // If there's a concurrent writer they will replace data and it just appears + // that we won the race + ExpandoData data = _data; + if (data.Class != indexClass || ignoreCase) { + /* Re-search for the index matching the name here if + * 1) the class has changed, we need to get the correct index and return + * the value there. + * 2) the search is case insensitive: + * a. the member specified by index may be deleted, but there might be other + * members matching the name if the binder is case insensitive. + * b. the member that exactly matches the name didn't exist before and exists now, + * need to find the exact match. + */ + index = data.Class.GetValueIndex(name, ignoreCase, this); + if (index == ExpandoObject.AmbiguousMatchFound) { + throw Error.AmbiguousMatchInExpandoObject(name); + } + } + + if (index == ExpandoObject.NoMatch) { + value = null; + return false; + } + + // Capture the value into a temp, so it doesn't get mutated after we check + // for Uninitialized. + object temp = data[index]; + if (temp == Uninitialized) { + value = null; + return false; + } + + // index is now known to be correct + value = temp; + return true; + } + + /// <summary> + /// Sets the data for the specified class at the specified index. If the class has + /// changed then a full look for the slot will be performed. If the new class does + /// not have the provided slot then the Expando's class will change. Only case sensitive + /// setter is supported in ExpandoObject. + /// </summary> + internal void TrySetValue(object indexClass, int index, object value, string name, bool ignoreCase, bool add) { + ExpandoData data; + object oldValue; + + lock (LockObject) { + data = _data; + + if (data.Class != indexClass || ignoreCase) { + // The class has changed or we are doing a case-insensitive search, + // we need to get the correct index and set the value there. If we + // don't have the value then we need to promote the class - that + // should only happen when we have multiple concurrent writers. + index = data.Class.GetValueIndex(name, ignoreCase, this); + if (index == ExpandoObject.AmbiguousMatchFound) { + throw Error.AmbiguousMatchInExpandoObject(name); + } + if (index == ExpandoObject.NoMatch) { + // Before creating a new class with the new member, need to check + // if there is the exact same member but is deleted. We should reuse + // the class if there is such a member. + int exactMatch = ignoreCase ? + data.Class.GetValueIndexCaseSensitive(name) : + index; + if (exactMatch != ExpandoObject.NoMatch) { + Debug.Assert(data[exactMatch] == Uninitialized); + index = exactMatch; + } else { + ExpandoClass newClass = data.Class.FindNewClass(name); + data = PromoteClassCore(data.Class, newClass); + // After the class promotion, there must be an exact match, + // so we can do case-sensitive search here. + index = data.Class.GetValueIndexCaseSensitive(name); + Debug.Assert(index != ExpandoObject.NoMatch); + } + } + } + + // Setting an uninitialized member increases the count of available members + oldValue = data[index]; + if (oldValue == Uninitialized) { + _count++; + } else if (add) { + throw Error.SameKeyExistsInExpando(name); + } + + data[index] = value; + } + + // Notify property changed, outside of the lock. + var propertyChanged = _propertyChanged; + if (propertyChanged != null && value != oldValue) { + // Use the canonical case for the key. + propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[index])); + } + } + + /// <summary> + /// Deletes the data stored for the specified class at the specified index. + /// </summary> + internal bool TryDeleteValue(object indexClass, int index, string name, bool ignoreCase, object deleteValue) { + ExpandoData data; + lock (LockObject) { + data = _data; + + if (data.Class != indexClass || ignoreCase) { + // the class has changed or we are doing a case-insensitive search, + // we need to get the correct index. If there is no associated index + // we simply can't have the value and we return false. + index = data.Class.GetValueIndex(name, ignoreCase, this); + if (index == ExpandoObject.AmbiguousMatchFound) { + throw Error.AmbiguousMatchInExpandoObject(name); + } + } + if (index == ExpandoObject.NoMatch) { + return false; + } + + object oldValue = data[index]; + if (oldValue == Uninitialized) { + return false; + } + + // Make sure the value matches, if requested. + // + // It's a shame we have to call Equals with the lock held but + // there doesn't seem to be a good way around that, and + // ConcurrentDictionary in mscorlib does the same thing. + if (deleteValue != Uninitialized && !object.Equals(oldValue, deleteValue)) { + return false; + } + + data[index] = Uninitialized; + + // Deleting an available member decreases the count of available members + _count--; + } + + // Notify property changed, outside of the lock. + var propertyChanged = _propertyChanged; + if (propertyChanged != null) { + // Use the canonical case for the key. + propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[index])); + } + + return true; + } + + /// <summary> + /// Returns true if the member at the specified index has been deleted, + /// otherwise false. Call this function holding the lock. + /// </summary> + internal bool IsDeletedMember(int index) { + Debug.Assert(index >= 0 && index <= _data.Length); + + if (index == _data.Length) { + // The member is a newly added by SetMemberBinder and not in data yet + return false; + } + + return _data[index] == ExpandoObject.Uninitialized; + } + + /// <summary> + /// Exposes the ExpandoClass which we've associated with this + /// Expando object. Used for type checks in rules. + /// </summary> + internal ExpandoClass Class { + get { + return _data.Class; + } + } + + /// <summary> + /// Promotes the class from the old type to the new type and returns the new + /// ExpandoData object. + /// </summary> + private ExpandoData PromoteClassCore(ExpandoClass oldClass, ExpandoClass newClass) { + Debug.Assert(oldClass != newClass); + + lock (LockObject) { + if (_data.Class == oldClass) { + _data = _data.UpdateClass(newClass); + } + return _data; + } + } + + /// <summary> + /// Internal helper to promote a class. Called from our RuntimeOps helper. This + /// version simply doesn't expose the ExpandoData object which is a private + /// data structure. + /// </summary> + internal void PromoteClass(object oldClass, object newClass) { + PromoteClassCore((ExpandoClass)oldClass, (ExpandoClass)newClass); + } + + #endregion + + #region IDynamicMetaObjectProvider Members + + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) { + return new MetaExpando(parameter, this); + } + #endregion + + #region Helper methods + private void TryAddMember(string key, object value) { + ContractUtils.RequiresNotNull(key, "key"); + // Pass null to the class, which forces lookup. + TrySetValue(null, -1, value, key, false, true); + } + + private bool TryGetValueForKey(string key, out object value) { + // Pass null to the class, which forces lookup. + return TryGetValue(null, -1, key, false, out value); + } + + private bool ExpandoContainsKey(string key) { + return _data.Class.GetValueIndexCaseSensitive(key) >= 0; + } + + // We create a non-generic type for the debug view for each different collection type + // that uses DebuggerTypeProxy, instead of defining a generic debug view type and + // using different instantiations. The reason for this is that support for generics + // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only + // open types (from MSDN http://msdn.microsoft.com/en-us/library/d8eyd8zc.aspx). + private sealed class KeyCollectionDebugView { + private ICollection<string> collection; + public KeyCollectionDebugView(ICollection<string> collection) { + Debug.Assert(collection != null); + this.collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public string[] Items { + get { + string[] items = new string[collection.Count]; + collection.CopyTo(items, 0); + return items; + } + } + } + + [DebuggerTypeProxy(typeof(KeyCollectionDebugView))] + [DebuggerDisplay("Count = {Count}")] + private class KeyCollection : ICollection<string> { + private readonly ExpandoObject _expando; + private readonly int _expandoVersion; + private readonly int _expandoCount; + private readonly ExpandoData _expandoData; + + internal KeyCollection(ExpandoObject expando) { + lock (expando.LockObject) { + _expando = expando; + _expandoVersion = expando._data.Version; + _expandoCount = expando._count; + _expandoData = expando._data; + } + } + + private void CheckVersion() { + if (_expando._data.Version != _expandoVersion || _expandoData != _expando._data) { + //the underlying expando object has changed + throw Error.CollectionModifiedWhileEnumerating(); + } + } + + #region ICollection<string> Members + + public void Add(string item) { + throw Error.CollectionReadOnly(); + } + + public void Clear() { + throw Error.CollectionReadOnly(); + } + + public bool Contains(string item) { + lock (_expando.LockObject) { + CheckVersion(); + return _expando.ExpandoContainsKey(item); + } + } + + public void CopyTo(string[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresArrayRange(array, arrayIndex, _expandoCount, "arrayIndex", "Count"); + lock (_expando.LockObject) { + CheckVersion(); + ExpandoData data = _expando._data; + for (int i = 0; i < data.Class.Keys.Length; i++) { + if (data[i] != Uninitialized) { + array[arrayIndex++] = data.Class.Keys[i]; + } + } + } + } + + public int Count { + get { + CheckVersion(); + return _expandoCount; + } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(string item) { + throw Error.CollectionReadOnly(); + } + + #endregion + + #region IEnumerable<string> Members + + public IEnumerator<string> GetEnumerator() { + for (int i = 0, n = _expandoData.Class.Keys.Length; i < n; i++) { + CheckVersion(); + if (_expandoData[i] != Uninitialized) { + yield return _expandoData.Class.Keys[i]; + } + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } + + // We create a non-generic type for the debug view for each different collection type + // that uses DebuggerTypeProxy, instead of defining a generic debug view type and + // using different instantiations. The reason for this is that support for generics + // with using DebuggerTypeProxy is limited. For C#, DebuggerTypeProxy supports only + // open types (from MSDN http://msdn.microsoft.com/en-us/library/d8eyd8zc.aspx). + private sealed class ValueCollectionDebugView { + private ICollection<object> collection; + public ValueCollectionDebugView(ICollection<object> collection) { + Debug.Assert(collection != null); + this.collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public object[] Items { + get { + object[] items = new object[collection.Count]; + collection.CopyTo(items, 0); + return items; + } + } + } + + [DebuggerTypeProxy(typeof(ValueCollectionDebugView))] + [DebuggerDisplay("Count = {Count}")] + private class ValueCollection : ICollection<object> { + private readonly ExpandoObject _expando; + private readonly int _expandoVersion; + private readonly int _expandoCount; + private readonly ExpandoData _expandoData; + + internal ValueCollection(ExpandoObject expando) { + lock (expando.LockObject) { + _expando = expando; + _expandoVersion = expando._data.Version; + _expandoCount = expando._count; + _expandoData = expando._data; + } + } + + private void CheckVersion() { + if (_expando._data.Version != _expandoVersion || _expandoData != _expando._data) { + //the underlying expando object has changed + throw Error.CollectionModifiedWhileEnumerating(); + } + } + + #region ICollection<string> Members + + public void Add(object item) { + throw Error.CollectionReadOnly(); + } + + public void Clear() { + throw Error.CollectionReadOnly(); + } + + public bool Contains(object item) { + lock (_expando.LockObject) { + CheckVersion(); + + ExpandoData data = _expando._data; + for (int i = 0; i < data.Class.Keys.Length; i++) { + + // See comment in TryDeleteValue; it's okay to call + // object.Equals with the lock held. + if (object.Equals(data[i], item)) { + return true; + } + } + return false; + } + } + + public void CopyTo(object[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresArrayRange(array, arrayIndex, _expandoCount, "arrayIndex", "Count"); + lock (_expando.LockObject) { + CheckVersion(); + ExpandoData data = _expando._data; + for (int i = 0; i < data.Class.Keys.Length; i++) { + if (data[i] != Uninitialized) { + array[arrayIndex++] = data[i]; + } + } + } + } + + public int Count { + get { + CheckVersion(); + return _expandoCount; + } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(object item) { + throw Error.CollectionReadOnly(); + } + + #endregion + + #region IEnumerable<string> Members + + public IEnumerator<object> GetEnumerator() { + ExpandoData data = _expando._data; + for (int i = 0; i < data.Class.Keys.Length; i++) { + CheckVersion(); + // Capture the value into a temp so we don't inadvertently + // return Uninitialized. + object temp = data[i]; + if (temp != Uninitialized) { + yield return temp; + } + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } + + #endregion + + #region IDictionary<string, object> Members + ICollection<string> IDictionary<string, object>.Keys { + get { + return new KeyCollection(this); + } + } + + ICollection<object> IDictionary<string, object>.Values { + get { + return new ValueCollection(this); + } + } + + object IDictionary<string, object>.this[string key] { + get { + object value; + if (!TryGetValueForKey(key, out value)) { + throw Error.KeyDoesNotExistInExpando(key); + } + return value; + } + set { + ContractUtils.RequiresNotNull(key, "key"); + // Pass null to the class, which forces lookup. + TrySetValue(null, -1, value, key, false, false); + } + } + + void IDictionary<string, object>.Add(string key, object value) { + this.TryAddMember(key, value); + } + + bool IDictionary<string, object>.ContainsKey(string key) { + ContractUtils.RequiresNotNull(key, "key"); + + ExpandoData data = _data; + int index = data.Class.GetValueIndexCaseSensitive(key); + return index >= 0 && data[index] != Uninitialized; + } + + bool IDictionary<string, object>.Remove(string key) { + ContractUtils.RequiresNotNull(key, "key"); + // Pass null to the class, which forces lookup. + return TryDeleteValue(null, -1, key, false, Uninitialized); + } + + bool IDictionary<string, object>.TryGetValue(string key, out object value) { + return TryGetValueForKey(key, out value); + } + + #endregion + + #region ICollection<KeyValuePair<string, object>> Members + int ICollection<KeyValuePair<string, object>>.Count { + get { + return _count; + } + } + + bool ICollection<KeyValuePair<string, object>>.IsReadOnly { + get { return false; } + } + + void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) { + TryAddMember(item.Key, item.Value); + } + + void ICollection<KeyValuePair<string, object>>.Clear() { + // We remove both class and data! + ExpandoData data; + lock (LockObject) { + data = _data; + _data = ExpandoData.Empty; + _count = 0; + } + + // Notify property changed for all properties. + var propertyChanged = _propertyChanged; + if (propertyChanged != null) { + for (int i = 0, n = data.Class.Keys.Length; i < n; i++) { + if (data[i] != Uninitialized) { + propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[i])); + } + } + } + } + + bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) { + object value; + if (!TryGetValueForKey(item.Key, out value)) { + return false; + } + + return object.Equals(value, item.Value); + } + + void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) { + ContractUtils.RequiresNotNull(array, "array"); + ContractUtils.RequiresArrayRange(array, arrayIndex, _count, "arrayIndex", "Count"); + + // We want this to be atomic and not throw + lock (LockObject) { + foreach (KeyValuePair<string, object> item in this) { + array[arrayIndex++] = item; + } + } + } + + bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) { + return TryDeleteValue(null, -1, item.Key, false, item.Value); + } + #endregion + + #region IEnumerable<KeyValuePair<string, object>> Member + + IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() { + ExpandoData data = _data; + return GetExpandoEnumerator(data, data.Version); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + ExpandoData data = _data; + return GetExpandoEnumerator(data, data.Version); + } + + // Note: takes the data and version as parameters so they will be + // captured before the first call to MoveNext(). + private IEnumerator<KeyValuePair<string, object>> GetExpandoEnumerator(ExpandoData data, int version) { + for (int i = 0; i < data.Class.Keys.Length; i++) { + if (_data.Version != version || data != _data) { + // The underlying expando object has changed: + // 1) the version of the expando data changed + // 2) the data object is changed + throw Error.CollectionModifiedWhileEnumerating(); + } + // Capture the value into a temp so we don't inadvertently + // return Uninitialized. + object temp = data[i]; + if (temp != Uninitialized) { + yield return new KeyValuePair<string,object>(data.Class.Keys[i], temp); + } + } + } + #endregion + + #region MetaExpando + + private class MetaExpando : DynamicMetaObject { + public MetaExpando(Expression expression, ExpandoObject value) + : base(expression, BindingRestrictions.Empty, value) { + } + + private DynamicMetaObject BindGetOrInvokeMember(DynamicMetaObjectBinder binder, string name, bool ignoreCase, DynamicMetaObject fallback, Func<DynamicMetaObject, DynamicMetaObject> fallbackInvoke) { + ExpandoClass klass = Value.Class; + + //try to find the member, including the deleted members + int index = klass.GetValueIndex(name, ignoreCase, Value); + + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + + Expression tryGetValue = Expression.Call( + typeof(RuntimeOps).GetMethod("ExpandoTryGetValue"), + GetLimitedSelf(), + Expression.Constant(klass, typeof(object)), + Expression.Constant(index), + Expression.Constant(name), + Expression.Constant(ignoreCase), + value + ); + + var result = new DynamicMetaObject(value, BindingRestrictions.Empty); + if (fallbackInvoke != null) { + result = fallbackInvoke(result); + } + + result = new DynamicMetaObject( + Expression.Block( + new[] { value }, + Expression.Condition( + tryGetValue, + result.Expression, + fallback.Expression, + typeof(object) + ) + ), + result.Restrictions.Merge(fallback.Restrictions) + ); + + return AddDynamicTestAndDefer(binder, Value.Class, null, result); + } + + public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + return BindGetOrInvokeMember( + binder, + binder.Name, + binder.IgnoreCase, + binder.FallbackGetMember(this), + null + ); + } + + public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) { + ContractUtils.RequiresNotNull(binder, "binder"); + return BindGetOrInvokeMember( + binder, + binder.Name, + binder.IgnoreCase, + binder.FallbackInvokeMember(this, args), + value => binder.FallbackInvoke(value, args, null) + ); + } + + public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(value, "value"); + + ExpandoClass klass; + int index; + + ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, Value, out klass, out index); + + return AddDynamicTestAndDefer( + binder, + klass, + originalClass, + new DynamicMetaObject( + Expression.Call( + typeof(RuntimeOps).GetMethod("ExpandoTrySetValue"), + GetLimitedSelf(), + Expression.Constant(klass, typeof(object)), + Expression.Constant(index), + Expression.Convert(value.Expression, typeof(object)), + Expression.Constant(binder.Name), + Expression.Constant(binder.IgnoreCase) + ), + BindingRestrictions.Empty + ) + ); + } + + public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) { + ContractUtils.RequiresNotNull(binder, "binder"); + + int index = Value.Class.GetValueIndex(binder.Name, binder.IgnoreCase, Value); + + Expression tryDelete = Expression.Call( + typeof(RuntimeOps).GetMethod("ExpandoTryDeleteValue"), + GetLimitedSelf(), + Expression.Constant(Value.Class, typeof(object)), + Expression.Constant(index), + Expression.Constant(binder.Name), + Expression.Constant(binder.IgnoreCase) + ); + DynamicMetaObject fallback = binder.FallbackDeleteMember(this); + + DynamicMetaObject target = new DynamicMetaObject( + Expression.IfThen(Expression.Not(tryDelete), fallback.Expression), + fallback.Restrictions + ); + + return AddDynamicTestAndDefer(binder, Value.Class, null, target); + } + + public override IEnumerable<string> GetDynamicMemberNames() { + var expandoData = Value._data; + var klass = expandoData.Class; + for (int i = 0; i < klass.Keys.Length; i++) { + object val = expandoData[i]; + if (val != ExpandoObject.Uninitialized) { + yield return klass.Keys[i]; + } + } + } + + /// <summary> + /// Adds a dynamic test which checks if the version has changed. The test is only necessary for + /// performance as the methods will do the correct thing if called with an incorrect version. + /// </summary> + private DynamicMetaObject AddDynamicTestAndDefer(DynamicMetaObjectBinder binder, ExpandoClass klass, ExpandoClass originalClass, DynamicMetaObject succeeds) { + + Expression ifTestSucceeds = succeeds.Expression; + if (originalClass != null) { + // we are accessing a member which has not yet been defined on this class. + // We force a class promotion after the type check. If the class changes the + // promotion will fail and the set/delete will do a full lookup using the new + // class to discover the name. + Debug.Assert(originalClass != klass); + + ifTestSucceeds = Expression.Block( + Expression.Call( + null, + typeof(RuntimeOps).GetMethod("ExpandoPromoteClass"), + GetLimitedSelf(), + Expression.Constant(originalClass, typeof(object)), + Expression.Constant(klass, typeof(object)) + ), + succeeds.Expression + ); + } + + return new DynamicMetaObject( + Expression.Condition( + Expression.Call( + null, + typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"), + GetLimitedSelf(), + Expression.Constant(originalClass ?? klass, typeof(object)) + ), + ifTestSucceeds, + binder.GetUpdateExpression(ifTestSucceeds.Type) + ), + GetRestrictions().Merge(succeeds.Restrictions) + ); + } + + /// <summary> + /// Gets the class and the index associated with the given name. Does not update the expando object. Instead + /// this returns both the original and desired new class. A rule is created which includes the test for the + /// original class, the promotion to the new class, and the set/delete based on the class post-promotion. + /// </summary> + private ExpandoClass GetClassEnsureIndex(string name, bool caseInsensitive, ExpandoObject obj, out ExpandoClass klass, out int index) { + ExpandoClass originalClass = Value.Class; + + index = originalClass.GetValueIndex(name, caseInsensitive, obj) ; + if (index == ExpandoObject.AmbiguousMatchFound) { + klass = originalClass; + return null; + } + if (index == ExpandoObject.NoMatch) { + // go ahead and find a new class now... + ExpandoClass newClass = originalClass.FindNewClass(name); + + klass = newClass; + index = newClass.GetValueIndexCaseSensitive(name); + + Debug.Assert(index != ExpandoObject.NoMatch); + return originalClass; + } else { + klass = originalClass; + return null; + } + } + + /// <summary> + /// Returns our Expression converted to our known LimitType + /// </summary> + private Expression GetLimitedSelf() { + if (TypeUtils.AreEquivalent(Expression.Type, LimitType)) { + return Expression; + } + return Expression.Convert(Expression, LimitType); + } + + /// <summary> + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// </summary> + private BindingRestrictions GetRestrictions() { + Debug.Assert(Restrictions == BindingRestrictions.Empty, "We don't merge, restrictions are always empty"); + + return BindingRestrictions.GetTypeRestriction(this); + } + + public new ExpandoObject Value { + get { + return (ExpandoObject)base.Value; + } + } + } + + #endregion + + #region ExpandoData + + /// <summary> + /// Stores the class and the data associated with the class as one atomic + /// pair. This enables us to do a class check in a thread safe manner w/o + /// requiring locks. + /// </summary> + private class ExpandoData { + internal static ExpandoData Empty = new ExpandoData(); + + /// <summary> + /// the dynamically assigned class associated with the Expando object + /// </summary> + internal readonly ExpandoClass Class; + + /// <summary> + /// data stored in the expando object, key names are stored in the class. + /// + /// Expando._data must be locked when mutating the value. Otherwise a copy of it + /// could be made and lose values. + /// </summary> + private readonly object[] _dataArray; + + /// <summary> + /// Indexer for getting/setting the data + /// </summary> + internal object this[int index] { + get { + return _dataArray[index]; + } + set { + //when the array is updated, version increases, even the new value is the same + //as previous. Dictionary type has the same behavior. + _version++; + _dataArray[index] = value; + } + } + + internal int Version { + get { return _version; } + } + + internal int Length { + get { return _dataArray.Length; } + } + + /// <summary> + /// Constructs an empty ExpandoData object with the empty class and no data. + /// </summary> + private ExpandoData() { + Class = ExpandoClass.Empty; + _dataArray = new object[0]; + } + + /// <summary> + /// the version of the ExpandoObject that tracks set and delete operations + /// </summary> + private int _version; + + /// <summary> + /// Constructs a new ExpandoData object with the specified class and data. + /// </summary> + internal ExpandoData(ExpandoClass klass, object[] data, int version) { + Class = klass; + _dataArray = data; + _version = version; + } + + /// <summary> + /// Update the associated class and increases the storage for the data array if needed. + /// </summary> + /// <returns></returns> + internal ExpandoData UpdateClass(ExpandoClass newClass) { + if (_dataArray.Length >= newClass.Keys.Length) { + // we have extra space in our buffer, just initialize it to Uninitialized. + this[newClass.Keys.Length - 1] = ExpandoObject.Uninitialized; + return new ExpandoData(newClass, this._dataArray, this._version); + } else { + // we've grown too much - we need a new object array + int oldLength = _dataArray.Length; + object[] arr = new object[GetAlignedSize(newClass.Keys.Length)]; + Array.Copy(_dataArray, arr, _dataArray.Length); + ExpandoData newData = new ExpandoData(newClass, arr, this._version); + newData[oldLength] = ExpandoObject.Uninitialized; + return newData; + } + } + + private static int GetAlignedSize(int len) { + // the alignment of the array for storage of values (must be a power of two) + const int DataArrayAlignment = 8; + + // round up and then mask off lower bits + return (len + (DataArrayAlignment - 1)) & (~(DataArrayAlignment - 1)); + } + } + + #endregion + + #region INotifyPropertyChanged Members + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { + add { _propertyChanged += value; } + remove { _propertyChanged -= value; } + } + + #endregion + } +} + +namespace System.Runtime.CompilerServices { + + // + // Note: these helpers are kept as simple wrappers so they have a better + // chance of being inlined. + // + public static partial class RuntimeOps { + + /// <summary> + /// Gets the value of an item in an expando object. + /// </summary> + /// <param name="expando">The expando object.</param> + /// <param name="indexClass">The class of the expando object.</param> + /// <param name="index">The index of the member.</param> + /// <param name="name">The name of the member.</param> + /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param> + /// <param name="value">The out parameter containing the value of the member.</param> + /// <returns>True if the member exists in the expando object, otherwise false.</returns> + [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)] + public static bool ExpandoTryGetValue(ExpandoObject expando, object indexClass, int index, string name, bool ignoreCase, out object value) { + return expando.TryGetValue(indexClass, index, name, ignoreCase, out value); + } + + /// <summary> + /// Sets the value of an item in an expando object. + /// </summary> + /// <param name="expando">The expando object.</param> + /// <param name="indexClass">The class of the expando object.</param> + /// <param name="index">The index of the member.</param> + /// <param name="value">The value of the member.</param> + /// <param name="name">The name of the member.</param> + /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param> + /// <returns> + /// Returns the index for the set member. + /// </returns> + [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)] + public static object ExpandoTrySetValue(ExpandoObject expando, object indexClass, int index, object value, string name, bool ignoreCase) { + expando.TrySetValue(indexClass, index, value, name, ignoreCase, false); + return value; + } + + /// <summary> + /// Deletes the value of an item in an expando object. + /// </summary> + /// <param name="expando">The expando object.</param> + /// <param name="indexClass">The class of the expando object.</param> + /// <param name="index">The index of the member.</param> + /// <param name="name">The name of the member.</param> + /// <param name="ignoreCase">true if the name should be matched ignoring case; false otherwise.</param> + /// <returns>true if the item was successfully removed; otherwise, false.</returns> + [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)] + public static bool ExpandoTryDeleteValue(ExpandoObject expando, object indexClass, int index, string name, bool ignoreCase) { + return expando.TryDeleteValue(indexClass, index, name, ignoreCase, ExpandoObject.Uninitialized); + } + + /// <summary> + /// Checks the version of the expando object. + /// </summary> + /// <param name="expando">The expando object.</param> + /// <param name="version">The version to check.</param> + /// <returns>true if the version is equal; otherwise, false.</returns> + [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)] + public static bool ExpandoCheckVersion(ExpandoObject expando, object version) { + return expando.Class == version; + } + + /// <summary> + /// Promotes an expando object from one class to a new class. + /// </summary> + /// <param name="expando">The expando object.</param> + /// <param name="oldClass">The old class of the expando object.</param> + /// <param name="newClass">The new class of the expando object.</param> + [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)] + public static void ExpandoPromoteClass(ExpandoObject expando, object oldClass, object newClass) { + expando.PromoteClass(oldClass, newClass); + } + } +} + diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/.gitattributes b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/.gitattributes deleted file mode 100644 index 1dede25a279..00000000000 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -/Expression.cs -crlf -/ListInitExpression.cs -crlf diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/Expression.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/Expression.cs index b2ef4fc3917..045844fac73 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/Expression.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/Expression.cs @@ -1,419 +1,419 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Dynamic.Utils;
-using System.Globalization;
-using System.IO;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-#if SILVERLIGHT
-using System.Core;
-#endif
-
-#if CLR2
-namespace Microsoft.Scripting.Ast {
- using Microsoft.Scripting.Utils;
-#else
-namespace System.Linq.Expressions {
-#endif
- /// <summary>
- /// The base type for all nodes in Expression Trees.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
- public abstract partial class Expression {
- private delegate LambdaExpression LambdaFactory(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters);
-
- private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40);
- private static CacheDict<Type, LambdaFactory> _LambdaFactories;
-
- // LINQ protected ctor from 3.5
-
-#if !CLR2 // needs ConditionWeakTable in 4.0
-
- // For 4.0, many frequently used Expression nodes have had their memory
- // footprint reduced by removing the Type and NodeType fields. This has
- // large performance benefits to all users of Expression Trees.
- //
- // To support the 3.5 protected constructor, we store the fields that
- // used to be here in a ConditionalWeakTable.
-
- private class ExtensionInfo {
- public ExtensionInfo(ExpressionType nodeType, Type type) {
- NodeType = nodeType;
- Type = type;
- }
-
- internal readonly ExpressionType NodeType;
- internal readonly Type Type;
- }
-
- private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable;
-
- /// <summary>
- /// Constructs a new instance of <see cref="Expression"/>.
- /// </summary>
- /// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param>
- /// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param>
- [Obsolete("use a different constructor that does not take ExpressionType. Then override NodeType and Type properties to provide the values that would be specified to this constructor.")]
- protected Expression(ExpressionType nodeType, Type type) {
- // Can't enforce anything that V1 didn't
- if (_legacyCtorSupportTable == null) {
- Interlocked.CompareExchange(
- ref _legacyCtorSupportTable,
- new ConditionalWeakTable<Expression, ExtensionInfo>(),
- null
- );
- }
-
- _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type));
- }
-#endif
-
- /// <summary>
- /// Constructs a new instance of <see cref="Expression"/>.
- /// </summary>
- protected Expression() {
- }
-
- /// <summary>
- /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>.
- /// </summary>
- public virtual ExpressionType NodeType {
- get {
-#if !CLR2
- ExtensionInfo extInfo;
- if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
- return extInfo.NodeType;
- }
-#endif
- // the extension expression failed to override NodeType
- throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType");
- }
- }
-
-
- /// <summary>
- /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
- public virtual Type Type {
- get {
-#if !CLR2
- ExtensionInfo extInfo;
- if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
- return extInfo.Type;
- }
-#endif
- // the extension expression failed to override Type
- throw Error.ExtensionNodeMustOverrideProperty("Expression.Type");
- }
- }
-
- /// <summary>
- /// Indicates that the node can be reduced to a simpler node. If this
- /// returns true, Reduce() can be called to produce the reduced form.
- /// </summary>
- public virtual bool CanReduce {
- get { return false; }
- }
-
- /// <summary>
- /// Reduces this node to a simpler expression. If CanReduce returns
- /// true, this should return a valid expression. This method is
- /// allowed to return another node which itself must be reduced.
- /// </summary>
- /// <returns>The reduced expression.</returns>
- public virtual Expression Reduce() {
- if (CanReduce) throw Error.ReducibleMustOverrideReduce();
- return this;
- }
-
- /// <summary>
- /// Reduces the node and then calls the visitor delegate on the reduced expression.
- /// Throws an exception if the node isn't reducible.
- /// </summary>
- /// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param>
- /// <returns>The expression being visited, or an expression which should replace it in the tree.</returns>
- /// <remarks>
- /// Override this method to provide logic to walk the node's children.
- /// A typical implementation will call visitor.Visit on each of its
- /// children, and if any of them change, should return a new copy of
- /// itself with the modified children.
- /// </remarks>
- protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) {
- if (!CanReduce) throw Error.MustBeReducible();
- return visitor.Visit(ReduceAndCheck());
- }
-
- /// <summary>
- /// Dispatches to the specific visit method for this node type. For
- /// example, <see cref="MethodCallExpression" /> will call into
- /// <see cref="ExpressionVisitor.VisitMethodCall" />.
- /// </summary>
- /// <param name="visitor">The visitor to visit this node with.</param>
- /// <returns>The result of visiting this node.</returns>
- /// <remarks>
- /// This default implementation for <see cref="ExpressionType.Extension" />
- /// nodes will call <see cref="ExpressionVisitor.VisitExtension" />.
- /// Override this method to call into a more specific method on a derived
- /// visitor class of ExprressionVisitor. However, it should still
- /// support unknown visitors by calling VisitExtension.
- /// </remarks>
- protected internal virtual Expression Accept(ExpressionVisitor visitor) {
- return visitor.VisitExtension(this);
- }
-
- /// <summary>
- /// Reduces this node to a simpler expression. If CanReduce returns
- /// true, this should return a valid expression. This method is
- /// allowed to return another node which itself must be reduced.
- /// </summary>
- /// <returns>The reduced expression.</returns>
- /// <remarks >
- /// Unlike Reduce, this method checks that the reduced node satisfies
- /// certain invariants.
- /// </remarks>
- public Expression ReduceAndCheck() {
- if (!CanReduce) throw Error.MustBeReducible();
-
- var newNode = Reduce();
-
- // 1. Reduction must return a new, non-null node
- // 2. Reduction must return a new node whose result type can be assigned to the type of the original node
- if (newNode == null || newNode == this) throw Error.MustReduceToDifferent();
- if (!TypeUtils.AreReferenceAssignable(Type, newNode.Type)) throw Error.ReducedNotCompatible();
- return newNode;
- }
-
- /// <summary>
- /// Reduces the expression to a known node type (i.e. not an Extension node)
- /// or simply returns the expression if it is already a known type.
- /// </summary>
- /// <returns>The reduced expression.</returns>
- public Expression ReduceExtensions() {
- var node = this;
- while (node.NodeType == ExpressionType.Extension) {
- node = node.ReduceAndCheck();
- }
- return node;
- }
-
-
- /// <summary>
- /// Creates a <see cref="String"/> representation of the Expression.
- /// </summary>
- /// <returns>A <see cref="String"/> representation of the Expression.</returns>
- public override string ToString() {
- return ExpressionStringBuilder.ExpressionToString(this);
- }
-
-#if CLR2
- /// <summary>
- /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>.
- /// </summary>
- /// <param name="writer">A <see cref="TextWriter"/> that will be used to build the string representation.</param>
- public void DumpExpression(TextWriter writer) {
- DebugViewWriter.WriteTo(this, writer);
- }
-
- /// <summary>
- /// Creates a <see cref="String"/> representation of the Expression.
- /// </summary>
- /// <returns>A <see cref="String"/> representation of the Expression.</returns>
- public string DebugView {
-#else
- private string DebugView {
-#endif
- get {
- using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) {
- DebugViewWriter.WriteTo(this, writer);
- return writer.ToString();
- }
- }
- }
-
- /// <summary>
- /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
- ///
- /// This is called from various methods where we internally hold onto an IList of T
- /// or a readonly collection of T. We check to see if we've already returned a
- /// readonly collection of T and if so simply return the other one. Otherwise we do
- /// a thread-safe replacement of the list w/ a readonly collection which wraps it.
- ///
- /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our
- /// data types because the compiler is capable of going directly to the IList of T.
- /// </summary>
- internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) {
- IList<T> value = collection;
-
- // if it's already read-only just return it.
- ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>;
- if (res != null) {
- return res;
- }
-
- // otherwise make sure only readonly collection every gets exposed
- Interlocked.CompareExchange<IList<T>>(
- ref collection,
- value.ToReadOnly(),
- value
- );
-
- // and return it
- return (ReadOnlyCollection<T>)collection;
- }
-
- /// <summary>
- /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
- ///
- /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold
- /// onto multiple Expressions where one is typed to object. That object field holds either
- /// an expression or a ReadOnlyCollection of Expressions. When it holds a ReadOnlyCollection
- /// the IList which backs it is a ListArgumentProvider which uses the Expression which
- /// implements IArgumentProvider to get 2nd and additional values. The ListArgumentProvider
- /// continues to hold onto the 1st expression.
- ///
- /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if
- /// it was just an array. Meanwhile The DLR internally avoids accessing which would force
- /// the readonly collection to be created resulting in a typical memory savings.
- /// </summary>
- internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) {
- Expression tObj = collection as Expression;
- if (tObj != null) {
- // otherwise make sure only one readonly collection ever gets exposed
- Interlocked.CompareExchange(
- ref collection,
- new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)),
- tObj
- );
- }
-
- // and return what is not guaranteed to be a readonly collection
- return (ReadOnlyCollection<Expression>)collection;
- }
-
- /// <summary>
- /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...).
- /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider.
- ///
- /// This is used to return the 1st argument. The 1st argument is typed as object and either
- /// contains a ReadOnlyCollection or the Expression. We check for the Expression and if it's
- /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection.
- /// </summary>
- internal static T ReturnObject<T>(object collectionOrT) where T : class {
- T t = collectionOrT as T;
- if (t != null) {
- return t;
- }
-
- return ((ReadOnlyCollection<T>)collectionOrT)[0];
- }
-
-#if SILVERLIGHT
-#if !CLR2
- // Quirks mode for Expression Trees as they existed in Silverlight 2 and 3
- internal readonly static bool SilverlightQuirks =
- AppDomain.CurrentDomain.IsCompatibilitySwitchSet("APP_EARLIER_THAN_SL4.0").GetValueOrDefault();
-#else
- internal readonly static bool SilverlightQuirks = true;
-#endif
-#endif
-
- private static void RequiresCanRead(Expression expression, string paramName) {
- if (expression == null) {
- throw new ArgumentNullException(paramName);
- }
-
- // validate that we can read the node
- switch (expression.NodeType) {
- case ExpressionType.Index:
- IndexExpression index = (IndexExpression)expression;
- if (index.Indexer != null && !index.Indexer.CanRead) {
- throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
- }
- break;
- case ExpressionType.MemberAccess:
- MemberExpression member = (MemberExpression)expression;
- MemberInfo memberInfo = member.Member;
- if (memberInfo.MemberType == MemberTypes.Property) {
- PropertyInfo prop = (PropertyInfo)memberInfo;
- if (!prop.CanRead) {
- throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
- }
- }
- break;
- }
- }
-
- private static void RequiresCanRead(IEnumerable<Expression> items, string paramName) {
- if (items != null) {
- // this is called a lot, avoid allocating an enumerator if we can...
- IList<Expression> listItems = items as IList<Expression>;
- if (listItems != null) {
- for (int i = 0; i < listItems.Count; i++) {
- RequiresCanRead(listItems[i], paramName);
- }
- return;
- }
-
- foreach (var i in items) {
- RequiresCanRead(i, paramName);
- }
- }
- }
- private static void RequiresCanWrite(Expression expression, string paramName) {
- if (expression == null) {
- throw new ArgumentNullException(paramName);
- }
-
- bool canWrite = false;
- switch (expression.NodeType) {
- case ExpressionType.Index:
- IndexExpression index = (IndexExpression)expression;
- if (index.Indexer != null) {
- canWrite = index.Indexer.CanWrite;
- } else {
- canWrite = true;
- }
- break;
- case ExpressionType.MemberAccess:
- MemberExpression member = (MemberExpression)expression;
- switch (member.Member.MemberType) {
- case MemberTypes.Property:
- PropertyInfo prop = (PropertyInfo)member.Member;
- canWrite = prop.CanWrite;
- break;
- case MemberTypes.Field:
- FieldInfo field = (FieldInfo)member.Member;
- canWrite = !(field.IsInitOnly || field.IsLiteral);
- break;
- }
- break;
- case ExpressionType.Parameter:
- canWrite = true;
- break;
- }
-
- if (!canWrite) {
- throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName);
- }
- }
- }
-}
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic.Utils; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; + +#if SILVERLIGHT +using System.Core; +#endif + +#if CLR2 +namespace Microsoft.Scripting.Ast { + using Microsoft.Scripting.Utils; +#else +namespace System.Linq.Expressions { +#endif + /// <summary> + /// The base type for all nodes in Expression Trees. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public abstract partial class Expression { + private delegate LambdaExpression LambdaFactory(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters); + + private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40); + private static CacheDict<Type, LambdaFactory> _LambdaFactories; + + // LINQ protected ctor from 3.5 + +#if !CLR2 // needs ConditionWeakTable in 4.0 + + // For 4.0, many frequently used Expression nodes have had their memory + // footprint reduced by removing the Type and NodeType fields. This has + // large performance benefits to all users of Expression Trees. + // + // To support the 3.5 protected constructor, we store the fields that + // used to be here in a ConditionalWeakTable. + + private class ExtensionInfo { + public ExtensionInfo(ExpressionType nodeType, Type type) { + NodeType = nodeType; + Type = type; + } + + internal readonly ExpressionType NodeType; + internal readonly Type Type; + } + + private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable; + + /// <summary> + /// Constructs a new instance of <see cref="Expression"/>. + /// </summary> + /// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param> + /// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param> + [Obsolete("use a different constructor that does not take ExpressionType. Then override NodeType and Type properties to provide the values that would be specified to this constructor.")] + protected Expression(ExpressionType nodeType, Type type) { + // Can't enforce anything that V1 didn't + if (_legacyCtorSupportTable == null) { + Interlocked.CompareExchange( + ref _legacyCtorSupportTable, + new ConditionalWeakTable<Expression, ExtensionInfo>(), + null + ); + } + + _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type)); + } +#endif + + /// <summary> + /// Constructs a new instance of <see cref="Expression"/>. + /// </summary> + protected Expression() { + } + + /// <summary> + /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>. + /// </summary> + public virtual ExpressionType NodeType { + get { +#if !CLR2 + ExtensionInfo extInfo; + if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) { + return extInfo.NodeType; + } +#endif + // the extension expression failed to override NodeType + throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType"); + } + } + + + /// <summary> + /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public virtual Type Type { + get { +#if !CLR2 + ExtensionInfo extInfo; + if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) { + return extInfo.Type; + } +#endif + // the extension expression failed to override Type + throw Error.ExtensionNodeMustOverrideProperty("Expression.Type"); + } + } + + /// <summary> + /// Indicates that the node can be reduced to a simpler node. If this + /// returns true, Reduce() can be called to produce the reduced form. + /// </summary> + public virtual bool CanReduce { + get { return false; } + } + + /// <summary> + /// Reduces this node to a simpler expression. If CanReduce returns + /// true, this should return a valid expression. This method is + /// allowed to return another node which itself must be reduced. + /// </summary> + /// <returns>The reduced expression.</returns> + public virtual Expression Reduce() { + if (CanReduce) throw Error.ReducibleMustOverrideReduce(); + return this; + } + + /// <summary> + /// Reduces the node and then calls the visitor delegate on the reduced expression. + /// Throws an exception if the node isn't reducible. + /// </summary> + /// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param> + /// <returns>The expression being visited, or an expression which should replace it in the tree.</returns> + /// <remarks> + /// Override this method to provide logic to walk the node's children. + /// A typical implementation will call visitor.Visit on each of its + /// children, and if any of them change, should return a new copy of + /// itself with the modified children. + /// </remarks> + protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) { + if (!CanReduce) throw Error.MustBeReducible(); + return visitor.Visit(ReduceAndCheck()); + } + + /// <summary> + /// Dispatches to the specific visit method for this node type. For + /// example, <see cref="MethodCallExpression" /> will call into + /// <see cref="ExpressionVisitor.VisitMethodCall" />. + /// </summary> + /// <param name="visitor">The visitor to visit this node with.</param> + /// <returns>The result of visiting this node.</returns> + /// <remarks> + /// This default implementation for <see cref="ExpressionType.Extension" /> + /// nodes will call <see cref="ExpressionVisitor.VisitExtension" />. + /// Override this method to call into a more specific method on a derived + /// visitor class of ExprressionVisitor. However, it should still + /// support unknown visitors by calling VisitExtension. + /// </remarks> + protected internal virtual Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitExtension(this); + } + + /// <summary> + /// Reduces this node to a simpler expression. If CanReduce returns + /// true, this should return a valid expression. This method is + /// allowed to return another node which itself must be reduced. + /// </summary> + /// <returns>The reduced expression.</returns> + /// <remarks > + /// Unlike Reduce, this method checks that the reduced node satisfies + /// certain invariants. + /// </remarks> + public Expression ReduceAndCheck() { + if (!CanReduce) throw Error.MustBeReducible(); + + var newNode = Reduce(); + + // 1. Reduction must return a new, non-null node + // 2. Reduction must return a new node whose result type can be assigned to the type of the original node + if (newNode == null || newNode == this) throw Error.MustReduceToDifferent(); + if (!TypeUtils.AreReferenceAssignable(Type, newNode.Type)) throw Error.ReducedNotCompatible(); + return newNode; + } + + /// <summary> + /// Reduces the expression to a known node type (i.e. not an Extension node) + /// or simply returns the expression if it is already a known type. + /// </summary> + /// <returns>The reduced expression.</returns> + public Expression ReduceExtensions() { + var node = this; + while (node.NodeType == ExpressionType.Extension) { + node = node.ReduceAndCheck(); + } + return node; + } + + + /// <summary> + /// Creates a <see cref="String"/> representation of the Expression. + /// </summary> + /// <returns>A <see cref="String"/> representation of the Expression.</returns> + public override string ToString() { + return ExpressionStringBuilder.ExpressionToString(this); + } + +#if CLR2 + /// <summary> + /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>. + /// </summary> + /// <param name="writer">A <see cref="TextWriter"/> that will be used to build the string representation.</param> + public void DumpExpression(TextWriter writer) { + DebugViewWriter.WriteTo(this, writer); + } + + /// <summary> + /// Creates a <see cref="String"/> representation of the Expression. + /// </summary> + /// <returns>A <see cref="String"/> representation of the Expression.</returns> + public string DebugView { +#else + private string DebugView { +#endif + get { + using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) { + DebugViewWriter.WriteTo(this, writer); + return writer.ToString(); + } + } + } + + /// <summary> + /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T. + /// + /// This is called from various methods where we internally hold onto an IList of T + /// or a readonly collection of T. We check to see if we've already returned a + /// readonly collection of T and if so simply return the other one. Otherwise we do + /// a thread-safe replacement of the list w/ a readonly collection which wraps it. + /// + /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our + /// data types because the compiler is capable of going directly to the IList of T. + /// </summary> + internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) { + IList<T> value = collection; + + // if it's already read-only just return it. + ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>; + if (res != null) { + return res; + } + + // otherwise make sure only readonly collection every gets exposed + Interlocked.CompareExchange<IList<T>>( + ref collection, + value.ToReadOnly(), + value + ); + + // and return it + return (ReadOnlyCollection<T>)collection; + } + + /// <summary> + /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T. + /// + /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold + /// onto multiple Expressions where one is typed to object. That object field holds either + /// an expression or a ReadOnlyCollection of Expressions. When it holds a ReadOnlyCollection + /// the IList which backs it is a ListArgumentProvider which uses the Expression which + /// implements IArgumentProvider to get 2nd and additional values. The ListArgumentProvider + /// continues to hold onto the 1st expression. + /// + /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if + /// it was just an array. Meanwhile The DLR internally avoids accessing which would force + /// the readonly collection to be created resulting in a typical memory savings. + /// </summary> + internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) { + Expression tObj = collection as Expression; + if (tObj != null) { + // otherwise make sure only one readonly collection ever gets exposed + Interlocked.CompareExchange( + ref collection, + new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)), + tObj + ); + } + + // and return what is not guaranteed to be a readonly collection + return (ReadOnlyCollection<Expression>)collection; + } + + /// <summary> + /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...). + /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider. + /// + /// This is used to return the 1st argument. The 1st argument is typed as object and either + /// contains a ReadOnlyCollection or the Expression. We check for the Expression and if it's + /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection. + /// </summary> + internal static T ReturnObject<T>(object collectionOrT) where T : class { + T t = collectionOrT as T; + if (t != null) { + return t; + } + + return ((ReadOnlyCollection<T>)collectionOrT)[0]; + } + +#if SILVERLIGHT +#if !CLR2 + // Quirks mode for Expression Trees as they existed in Silverlight 2 and 3 + internal readonly static bool SilverlightQuirks = + AppDomain.CurrentDomain.IsCompatibilitySwitchSet("APP_EARLIER_THAN_SL4.0").GetValueOrDefault(); +#else + internal readonly static bool SilverlightQuirks = true; +#endif +#endif + + private static void RequiresCanRead(Expression expression, string paramName) { + if (expression == null) { + throw new ArgumentNullException(paramName); + } + + // validate that we can read the node + switch (expression.NodeType) { + case ExpressionType.Index: + IndexExpression index = (IndexExpression)expression; + if (index.Indexer != null && !index.Indexer.CanRead) { + throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName); + } + break; + case ExpressionType.MemberAccess: + MemberExpression member = (MemberExpression)expression; + MemberInfo memberInfo = member.Member; + if (memberInfo.MemberType == MemberTypes.Property) { + PropertyInfo prop = (PropertyInfo)memberInfo; + if (!prop.CanRead) { + throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName); + } + } + break; + } + } + + private static void RequiresCanRead(IEnumerable<Expression> items, string paramName) { + if (items != null) { + // this is called a lot, avoid allocating an enumerator if we can... + IList<Expression> listItems = items as IList<Expression>; + if (listItems != null) { + for (int i = 0; i < listItems.Count; i++) { + RequiresCanRead(listItems[i], paramName); + } + return; + } + + foreach (var i in items) { + RequiresCanRead(i, paramName); + } + } + } + private static void RequiresCanWrite(Expression expression, string paramName) { + if (expression == null) { + throw new ArgumentNullException(paramName); + } + + bool canWrite = false; + switch (expression.NodeType) { + case ExpressionType.Index: + IndexExpression index = (IndexExpression)expression; + if (index.Indexer != null) { + canWrite = index.Indexer.CanWrite; + } else { + canWrite = true; + } + break; + case ExpressionType.MemberAccess: + MemberExpression member = (MemberExpression)expression; + switch (member.Member.MemberType) { + case MemberTypes.Property: + PropertyInfo prop = (PropertyInfo)member.Member; + canWrite = prop.CanWrite; + break; + case MemberTypes.Field: + FieldInfo field = (FieldInfo)member.Member; + canWrite = !(field.IsInitOnly || field.IsLiteral); + break; + } + break; + case ExpressionType.Parameter: + canWrite = true; + break; + } + + if (!canWrite) { + throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName); + } + } + } +} diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/ListInitExpression.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/ListInitExpression.cs index 71bd0f6c1c4..0b8125a7086 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/ListInitExpression.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Ast/ListInitExpression.cs @@ -1,237 +1,237 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Dynamic.Utils;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-#if SILVERLIGHT
-using System.Core;
-#endif
-
-#if CLR2
-namespace Microsoft.Scripting.Ast {
-#else
-namespace System.Linq.Expressions {
-#endif
- /// <summary>
- /// Represents a constructor call that has a collection initializer.
- /// </summary>
- /// <remarks>
- /// Use the <see cref="M:ListInit"/> factory methods to create a ListInitExpression.
- /// The value of the NodeType property of a ListInitExpression is ListInit.
- /// </remarks>
-#if !SILVERLIGHT
- [DebuggerTypeProxy(typeof(Expression.ListInitExpressionProxy))]
-#endif
- public sealed class ListInitExpression : Expression {
- private readonly NewExpression _newExpression;
- private readonly ReadOnlyCollection<ElementInit> _initializers;
-
- internal ListInitExpression(NewExpression newExpression, ReadOnlyCollection<ElementInit> initializers) {
- _newExpression = newExpression;
- _initializers = initializers;
- }
-
- /// <summary>
- /// Returns the node type of this <see cref="Expression"/>. (Inherited from <see cref="Expression" />.)
- /// </summary>
- /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
- public sealed override ExpressionType NodeType {
- get { return ExpressionType.ListInit; }
- }
-
- /// <summary>
- /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
- /// </summary>
- /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
- public sealed override Type Type {
- get { return _newExpression.Type; }
- }
-
- /// <summary>
- /// Gets a value that indicates whether the expression tree node can be reduced.
- /// </summary>
- public override bool CanReduce {
- get {
- return true;
- }
- }
-
- /// <summary>
- /// Gets the expression that contains a call to the constructor of a collection type.
- /// </summary>
- public NewExpression NewExpression {
- get { return _newExpression; }
- }
-
- /// <summary>
- /// Gets the element initializers that are used to initialize a collection.
- /// </summary>
- public ReadOnlyCollection<ElementInit> Initializers {
- get { return _initializers; }
- }
-
- /// <summary>
- /// Dispatches to the specific visit method for this node type.
- /// </summary>
- protected internal override Expression Accept(ExpressionVisitor visitor) {
- return visitor.VisitListInit(this);
- }
-
- /// <summary>
- /// Reduces the binary expression node to a simpler expression.
- /// If CanReduce returns true, this should return a valid expression.
- /// This method is allowed to return another node which itself
- /// must be reduced.
- /// </summary>
- /// <returns>The reduced expression.</returns>
- public override Expression Reduce() {
- return MemberInitExpression.ReduceListInit(_newExpression, _initializers, true);
- }
-
- /// <summary>
- /// Creates a new expression that is like this one, but using the
- /// supplied children. If all of the children are the same, it will
- /// return this expression.
- /// </summary>
- /// <param name="newExpression">The <see cref="NewExpression" /> property of the result.</param>
- /// <param name="initializers">The <see cref="Initializers" /> property of the result.</param>
- /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
- public ListInitExpression Update(NewExpression newExpression, IEnumerable<ElementInit> initializers) {
- if (newExpression == NewExpression && initializers == Initializers) {
- return this;
- }
- return Expression.ListInit(newExpression, initializers);
- }
- }
-
-
- public partial class Expression {
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses a method named "Add" to add elements to a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="initializers">An array of <see cref="Expression"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param>
- /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns>
- public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers) {
- ContractUtils.RequiresNotNull(newExpression, "newExpression");
- ContractUtils.RequiresNotNull(initializers, "initializers");
- return ListInit(newExpression, initializers as IEnumerable<Expression>);
- }
-
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses a method named "Add" to add elements to a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param>
- /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns>
- public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers) {
- ContractUtils.RequiresNotNull(newExpression, "newExpression");
- ContractUtils.RequiresNotNull(initializers, "initializers");
-
- var initializerlist = initializers.ToReadOnly();
- if (initializerlist.Count == 0) {
- throw Error.ListInitializerWithZeroMembers();
- }
-
- MethodInfo addMethod = FindMethod(newExpression.Type, "Add", null, new Expression[] { initializerlist[0] }, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- return ListInit(newExpression, addMethod, initializers);
- }
-
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses a specified method to add elements to a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="addMethod">A <see cref="MethodInfo"/> that represents an instance method named "Add" (case insensitive), that adds an element to a collection. </param>
- /// <param name="initializers">An array of <see cref="Expression"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param>
- /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns>
- public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, params Expression[] initializers) {
- if (addMethod == null) {
- return ListInit(newExpression, initializers as IEnumerable<Expression>);
- }
- ContractUtils.RequiresNotNull(newExpression, "newExpression");
- ContractUtils.RequiresNotNull(initializers, "initializers");
- return ListInit(newExpression, addMethod, initializers as IEnumerable<Expression>);
- }
-
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses a specified method to add elements to a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="addMethod">A <see cref="MethodInfo"/> that represents an instance method named "Add" (case insensitive), that adds an element to a collection. </param>
- /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="Expression"/> objects to use to populate the Initializers collection.</param>
- /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns>
- public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, IEnumerable<Expression> initializers) {
- if (addMethod == null) {
- return ListInit(newExpression, initializers);
- }
- ContractUtils.RequiresNotNull(newExpression, "newExpression");
- ContractUtils.RequiresNotNull(initializers, "initializers");
-
- var initializerlist = initializers.ToReadOnly();
- if (initializerlist.Count == 0) {
- throw Error.ListInitializerWithZeroMembers();
- }
- ElementInit[] initList = new ElementInit[initializerlist.Count];
- for (int i = 0; i < initializerlist.Count; i++) {
- initList[i] = ElementInit(addMethod, initializerlist[i]);
- }
- return ListInit(newExpression, new TrueReadOnlyCollection<ElementInit>(initList));
- }
-
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses specified <see cref="M:ElementInit"/> objects to initialize a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="initializers">An array that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param>
- /// <returns>
- /// A <see cref="ListInitExpression"/> that has the <see cref="P:Expressions.NodeType"/> property equal to ListInit
- /// and the <see cref="P:ListInitExpression.NewExpression"/> and <see cref="P:ListInitExpression.Initializers"/> properties set to the specified values.
- /// </returns>
- /// <remarks>
- /// The <see cref="P:Expressions.Type"/> property of <paramref name="newExpression"/> must represent a type that implements <see cref="System.Collections.IEnumerable"/>.
- /// The <see cref="P:Expressions.Type"/> property of the resulting <see cref="ListInitExpression"/> is equal to newExpression.Type.
- /// </remarks>
- public static ListInitExpression ListInit(NewExpression newExpression, params ElementInit[] initializers) {
- return ListInit(newExpression, (IEnumerable<ElementInit>)initializers);
- }
-
- /// <summary>
- /// Creates a <see cref="ListInitExpression"/> that uses specified <see cref="M:ElementInit"/> objects to initialize a collection.
- /// </summary>
- /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param>
- /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param>
- /// <returns>An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</returns>
- /// <remarks>
- /// The <see cref="P:Expressions.Type"/> property of <paramref name="newExpression"/> must represent a type that implements <see cref="System.Collections.IEnumerable"/>.
- /// The <see cref="P:Expressions.Type"/> property of the resulting <see cref="ListInitExpression"/> is equal to newExpression.Type.
- /// </remarks>
- public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<ElementInit> initializers) {
- ContractUtils.RequiresNotNull(newExpression, "newExpression");
- ContractUtils.RequiresNotNull(initializers, "initializers");
- var initializerlist = initializers.ToReadOnly();
- if (initializerlist.Count == 0) {
- throw Error.ListInitializerWithZeroMembers();
- }
- ValidateListInitArgs(newExpression.Type, initializerlist);
- return new ListInitExpression(newExpression, initializerlist);
- }
- }
-}
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Dynamic.Utils; +using System.Reflection; +using System.Runtime.CompilerServices; + +#if SILVERLIGHT +using System.Core; +#endif + +#if CLR2 +namespace Microsoft.Scripting.Ast { +#else +namespace System.Linq.Expressions { +#endif + /// <summary> + /// Represents a constructor call that has a collection initializer. + /// </summary> + /// <remarks> + /// Use the <see cref="M:ListInit"/> factory methods to create a ListInitExpression. + /// The value of the NodeType property of a ListInitExpression is ListInit. + /// </remarks> +#if !SILVERLIGHT + [DebuggerTypeProxy(typeof(Expression.ListInitExpressionProxy))] +#endif + public sealed class ListInitExpression : Expression { + private readonly NewExpression _newExpression; + private readonly ReadOnlyCollection<ElementInit> _initializers; + + internal ListInitExpression(NewExpression newExpression, ReadOnlyCollection<ElementInit> initializers) { + _newExpression = newExpression; + _initializers = initializers; + } + + /// <summary> + /// Returns the node type of this <see cref="Expression"/>. (Inherited from <see cref="Expression" />.) + /// </summary> + /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns> + public sealed override ExpressionType NodeType { + get { return ExpressionType.ListInit; } + } + + /// <summary> + /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.) + /// </summary> + /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns> + public sealed override Type Type { + get { return _newExpression.Type; } + } + + /// <summary> + /// Gets a value that indicates whether the expression tree node can be reduced. + /// </summary> + public override bool CanReduce { + get { + return true; + } + } + + /// <summary> + /// Gets the expression that contains a call to the constructor of a collection type. + /// </summary> + public NewExpression NewExpression { + get { return _newExpression; } + } + + /// <summary> + /// Gets the element initializers that are used to initialize a collection. + /// </summary> + public ReadOnlyCollection<ElementInit> Initializers { + get { return _initializers; } + } + + /// <summary> + /// Dispatches to the specific visit method for this node type. + /// </summary> + protected internal override Expression Accept(ExpressionVisitor visitor) { + return visitor.VisitListInit(this); + } + + /// <summary> + /// Reduces the binary expression node to a simpler expression. + /// If CanReduce returns true, this should return a valid expression. + /// This method is allowed to return another node which itself + /// must be reduced. + /// </summary> + /// <returns>The reduced expression.</returns> + public override Expression Reduce() { + return MemberInitExpression.ReduceListInit(_newExpression, _initializers, true); + } + + /// <summary> + /// Creates a new expression that is like this one, but using the + /// supplied children. If all of the children are the same, it will + /// return this expression. + /// </summary> + /// <param name="newExpression">The <see cref="NewExpression" /> property of the result.</param> + /// <param name="initializers">The <see cref="Initializers" /> property of the result.</param> + /// <returns>This expression if no children changed, or an expression with the updated children.</returns> + public ListInitExpression Update(NewExpression newExpression, IEnumerable<ElementInit> initializers) { + if (newExpression == NewExpression && initializers == Initializers) { + return this; + } + return Expression.ListInit(newExpression, initializers); + } + } + + + public partial class Expression { + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses a method named "Add" to add elements to a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="initializers">An array of <see cref="Expression"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param> + /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns> + public static ListInitExpression ListInit(NewExpression newExpression, params Expression[] initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListInit(newExpression, initializers as IEnumerable<Expression>); + } + + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses a method named "Add" to add elements to a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param> + /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns> + public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<Expression> initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + + var initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + + MethodInfo addMethod = FindMethod(newExpression.Type, "Add", null, new Expression[] { initializerlist[0] }, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return ListInit(newExpression, addMethod, initializers); + } + + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses a specified method to add elements to a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="addMethod">A <see cref="MethodInfo"/> that represents an instance method named "Add" (case insensitive), that adds an element to a collection. </param> + /// <param name="initializers">An array of <see cref="Expression"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param> + /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns> + public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, params Expression[] initializers) { + if (addMethod == null) { + return ListInit(newExpression, initializers as IEnumerable<Expression>); + } + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + return ListInit(newExpression, addMethod, initializers as IEnumerable<Expression>); + } + + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses a specified method to add elements to a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="addMethod">A <see cref="MethodInfo"/> that represents an instance method named "Add" (case insensitive), that adds an element to a collection. </param> + /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="Expression"/> objects to use to populate the Initializers collection.</param> + /// <returns>A <see cref="ListInitExpression"/> that has the <see cref="P:ListInitExpression.NodeType"/> property equal to ListInit and the <see cref="P:ListInitExpression.NewExpression"/> property set to the specified value.</returns> + public static ListInitExpression ListInit(NewExpression newExpression, MethodInfo addMethod, IEnumerable<Expression> initializers) { + if (addMethod == null) { + return ListInit(newExpression, initializers); + } + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + + var initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + ElementInit[] initList = new ElementInit[initializerlist.Count]; + for (int i = 0; i < initializerlist.Count; i++) { + initList[i] = ElementInit(addMethod, initializerlist[i]); + } + return ListInit(newExpression, new TrueReadOnlyCollection<ElementInit>(initList)); + } + + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses specified <see cref="M:ElementInit"/> objects to initialize a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="initializers">An array that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param> + /// <returns> + /// A <see cref="ListInitExpression"/> that has the <see cref="P:Expressions.NodeType"/> property equal to ListInit + /// and the <see cref="P:ListInitExpression.NewExpression"/> and <see cref="P:ListInitExpression.Initializers"/> properties set to the specified values. + /// </returns> + /// <remarks> + /// The <see cref="P:Expressions.Type"/> property of <paramref name="newExpression"/> must represent a type that implements <see cref="System.Collections.IEnumerable"/>. + /// The <see cref="P:Expressions.Type"/> property of the resulting <see cref="ListInitExpression"/> is equal to newExpression.Type. + /// </remarks> + public static ListInitExpression ListInit(NewExpression newExpression, params ElementInit[] initializers) { + return ListInit(newExpression, (IEnumerable<ElementInit>)initializers); + } + + /// <summary> + /// Creates a <see cref="ListInitExpression"/> that uses specified <see cref="M:ElementInit"/> objects to initialize a collection. + /// </summary> + /// <param name="newExpression">A <see cref="NewExpression"/> to set the <see cref="P:ListInitExpression.NewExpression"/> property equal to.</param> + /// <param name="initializers">An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</param> + /// <returns>An <see cref="IEnumerable{T}"/> that contains <see cref="M:ElementInit"/> objects to use to populate the <see cref="ListInitExpression.Initializers"/> collection.</returns> + /// <remarks> + /// The <see cref="P:Expressions.Type"/> property of <paramref name="newExpression"/> must represent a type that implements <see cref="System.Collections.IEnumerable"/>. + /// The <see cref="P:Expressions.Type"/> property of the resulting <see cref="ListInitExpression"/> is equal to newExpression.Type. + /// </remarks> + public static ListInitExpression ListInit(NewExpression newExpression, IEnumerable<ElementInit> initializers) { + ContractUtils.RequiresNotNull(newExpression, "newExpression"); + ContractUtils.RequiresNotNull(initializers, "initializers"); + var initializerlist = initializers.ToReadOnly(); + if (initializerlist.Count == 0) { + throw Error.ListInitializerWithZeroMembers(); + } + ValidateListInitArgs(newExpression.Type, initializerlist); + return new ListInitExpression(newExpression, initializerlist); + } + } +} diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/.gitattributes b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/.gitattributes deleted file mode 100644 index 9b2b3f88b17..00000000000 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/LambdaCompiler.Lambda.cs -crlf diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/LambdaCompiler.Lambda.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/LambdaCompiler.Lambda.cs index a2e58076f08..ed668bceca2 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/LambdaCompiler.Lambda.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/LambdaCompiler.Lambda.cs @@ -1,193 +1,193 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-using System;
-using System.Diagnostics;
-using System.Dynamic.Utils;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Runtime.CompilerServices;
-using System.Threading;
-
-#if CLR2
-namespace Microsoft.Scripting.Ast.Compiler {
-#else
-namespace System.Linq.Expressions.Compiler {
-#endif
-
- /// <summary>
- /// Dynamic Language Runtime Compiler.
- /// This part compiles lambdas.
- /// </summary>
- partial class LambdaCompiler {
- private static int _Counter;
-
- internal void EmitConstantArray<T>(T[] array) {
- // Emit as runtime constant if possible
- // if not, emit into IL
- if (_method is DynamicMethod) {
- EmitConstant(array, typeof(T[]));
- } else if(_typeBuilder != null) {
- // store into field in our type builder, we will initialize
- // the value only once.
- FieldBuilder fb = CreateStaticField("ConstantArray", typeof(T[]));
- Label l = _ilg.DefineLabel();
- _ilg.Emit(OpCodes.Ldsfld, fb);
- _ilg.Emit(OpCodes.Ldnull);
- _ilg.Emit(OpCodes.Bne_Un, l);
- _ilg.EmitArray(array);
- _ilg.Emit(OpCodes.Stsfld, fb);
- _ilg.MarkLabel(l);
- _ilg.Emit(OpCodes.Ldsfld, fb);
- } else {
- _ilg.EmitArray(array);
- }
- }
-
- private void EmitClosureCreation(LambdaCompiler inner) {
- bool closure = inner._scope.NeedsClosure;
- bool boundConstants = inner._boundConstants.Count > 0;
-
- if (!closure && !boundConstants) {
- _ilg.EmitNull();
- return;
- }
-
- // new Closure(constantPool, currentHoistedLocals)
- if (boundConstants) {
- _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[]));
- } else {
- _ilg.EmitNull();
- }
- if (closure) {
- _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
- } else {
- _ilg.EmitNull();
- }
- _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) }));
- }
-
- /// <summary>
- /// Emits code which creates new instance of the delegateType delegate.
- ///
- /// Since the delegate is getting closed over the "Closure" argument, this
- /// cannot be used with virtual/instance methods (inner must be static method)
- /// </summary>
- private void EmitDelegateConstruction(LambdaCompiler inner) {
- Type delegateType = inner._lambda.Type;
- DynamicMethod dynamicMethod = inner._method as DynamicMethod;
- if (dynamicMethod != null) {
- // dynamicMethod.CreateDelegate(delegateType, closure)
- _boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod));
- _ilg.EmitType(delegateType);
- EmitClosureCreation(inner);
- _ilg.Emit(OpCodes.Callvirt, typeof(DynamicMethod).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) }));
- _ilg.Emit(OpCodes.Castclass, delegateType);
- } else {
- // new DelegateType(closure)
- EmitClosureCreation(inner);
- _ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method);
- _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0]));
- }
- }
-
- /// <summary>
- /// Emits a delegate to the method generated for the LambdaExpression.
- /// May end up creating a wrapper to match the requested delegate type.
- /// </summary>
- /// <param name="lambda">Lambda for which to generate a delegate</param>
- ///
- private void EmitDelegateConstruction(LambdaExpression lambda) {
- // 1. Create the new compiler
- LambdaCompiler impl;
- if (_method is DynamicMethod) {
- impl = new LambdaCompiler(_tree, lambda);
- } else {
- // When the lambda does not have a name or the name is empty, generate a unique name for it.
- string name = String.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name;
- MethodBuilder mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static);
- impl = new LambdaCompiler(_tree, lambda, mb);
- }
-
- // 2. emit the lambda
- // Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization.
- impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail);
-
- // 3. emit the delegate creation in the outer lambda
- EmitDelegateConstruction(impl);
- }
-
- private static Type[] GetParameterTypes(LambdaExpression lambda) {
- return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
- }
-
- private static string GetUniqueMethodName() {
- return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method";
- }
-
- private void EmitLambdaBody() {
- // The lambda body is the "last" expression of the lambda
- CompilationFlags tailCallFlag = _lambda.TailCall ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
- EmitLambdaBody(null, false, tailCallFlag);
- }
-
- /// <summary>
- /// Emits the lambda body. If inlined, the parameters should already be
- /// pushed onto the IL stack.
- /// </summary>
- /// <param name="parent">The parent scope.</param>
- /// <param name="inlined">true if the lambda is inlined; false otherwise.</param>
- /// <param name="flags">
- /// The emum to specify if the lambda is compiled with the tail call optimization.
- /// </param>
- private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) {
- _scope.Enter(this, parent);
-
- if (inlined) {
- // The arguments were already pushed onto the IL stack.
- // Store them into locals, popping in reverse order.
- //
- // If any arguments were ByRef, the address is on the stack and
- // we'll be storing it into the variable, which has a ref type.
- for (int i = _lambda.Parameters.Count - 1; i >= 0; i--) {
- _scope.EmitSet(_lambda.Parameters[i]);
- }
- }
-
- // Need to emit the expression start for the lambda body
- flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
- if (_lambda.ReturnType == typeof(void)) {
- EmitExpressionAsVoid(_lambda.Body, flags);
- } else {
- EmitExpression(_lambda.Body, flags);
- }
-
- // Return must be the last instruction in a CLI method.
- // But if we're inlining the lambda, we want to leave the return
- // value on the IL stack.
- if (!inlined) {
- _ilg.Emit(OpCodes.Ret);
- }
-
- _scope.Exit();
-
- // Validate labels
- Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);
- foreach (LabelInfo label in _labelInfo.Values) {
- label.ValidateFinish();
- }
- }
- }
-}
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Dynamic.Utils; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; + +#if CLR2 +namespace Microsoft.Scripting.Ast.Compiler { +#else +namespace System.Linq.Expressions.Compiler { +#endif + + /// <summary> + /// Dynamic Language Runtime Compiler. + /// This part compiles lambdas. + /// </summary> + partial class LambdaCompiler { + private static int _Counter; + + internal void EmitConstantArray<T>(T[] array) { + // Emit as runtime constant if possible + // if not, emit into IL + if (_method is DynamicMethod) { + EmitConstant(array, typeof(T[])); + } else if(_typeBuilder != null) { + // store into field in our type builder, we will initialize + // the value only once. + FieldBuilder fb = CreateStaticField("ConstantArray", typeof(T[])); + Label l = _ilg.DefineLabel(); + _ilg.Emit(OpCodes.Ldsfld, fb); + _ilg.Emit(OpCodes.Ldnull); + _ilg.Emit(OpCodes.Bne_Un, l); + _ilg.EmitArray(array); + _ilg.Emit(OpCodes.Stsfld, fb); + _ilg.MarkLabel(l); + _ilg.Emit(OpCodes.Ldsfld, fb); + } else { + _ilg.EmitArray(array); + } + } + + private void EmitClosureCreation(LambdaCompiler inner) { + bool closure = inner._scope.NeedsClosure; + bool boundConstants = inner._boundConstants.Count > 0; + + if (!closure && !boundConstants) { + _ilg.EmitNull(); + return; + } + + // new Closure(constantPool, currentHoistedLocals) + if (boundConstants) { + _boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[])); + } else { + _ilg.EmitNull(); + } + if (closure) { + _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable); + } else { + _ilg.EmitNull(); + } + _ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) })); + } + + /// <summary> + /// Emits code which creates new instance of the delegateType delegate. + /// + /// Since the delegate is getting closed over the "Closure" argument, this + /// cannot be used with virtual/instance methods (inner must be static method) + /// </summary> + private void EmitDelegateConstruction(LambdaCompiler inner) { + Type delegateType = inner._lambda.Type; + DynamicMethod dynamicMethod = inner._method as DynamicMethod; + if (dynamicMethod != null) { + // dynamicMethod.CreateDelegate(delegateType, closure) + _boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod)); + _ilg.EmitType(delegateType); + EmitClosureCreation(inner); + _ilg.Emit(OpCodes.Callvirt, typeof(DynamicMethod).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) })); + _ilg.Emit(OpCodes.Castclass, delegateType); + } else { + // new DelegateType(closure) + EmitClosureCreation(inner); + _ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method); + _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0])); + } + } + + /// <summary> + /// Emits a delegate to the method generated for the LambdaExpression. + /// May end up creating a wrapper to match the requested delegate type. + /// </summary> + /// <param name="lambda">Lambda for which to generate a delegate</param> + /// + private void EmitDelegateConstruction(LambdaExpression lambda) { + // 1. Create the new compiler + LambdaCompiler impl; + if (_method is DynamicMethod) { + impl = new LambdaCompiler(_tree, lambda); + } else { + // When the lambda does not have a name or the name is empty, generate a unique name for it. + string name = String.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name; + MethodBuilder mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static); + impl = new LambdaCompiler(_tree, lambda, mb); + } + + // 2. emit the lambda + // Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization. + impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail); + + // 3. emit the delegate creation in the outer lambda + EmitDelegateConstruction(impl); + } + + private static Type[] GetParameterTypes(LambdaExpression lambda) { + return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type); + } + + private static string GetUniqueMethodName() { + return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method"; + } + + private void EmitLambdaBody() { + // The lambda body is the "last" expression of the lambda + CompilationFlags tailCallFlag = _lambda.TailCall ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail; + EmitLambdaBody(null, false, tailCallFlag); + } + + /// <summary> + /// Emits the lambda body. If inlined, the parameters should already be + /// pushed onto the IL stack. + /// </summary> + /// <param name="parent">The parent scope.</param> + /// <param name="inlined">true if the lambda is inlined; false otherwise.</param> + /// <param name="flags"> + /// The emum to specify if the lambda is compiled with the tail call optimization. + /// </param> + private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) { + _scope.Enter(this, parent); + + if (inlined) { + // The arguments were already pushed onto the IL stack. + // Store them into locals, popping in reverse order. + // + // If any arguments were ByRef, the address is on the stack and + // we'll be storing it into the variable, which has a ref type. + for (int i = _lambda.Parameters.Count - 1; i >= 0; i--) { + _scope.EmitSet(_lambda.Parameters[i]); + } + } + + // Need to emit the expression start for the lambda body + flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart); + if (_lambda.ReturnType == typeof(void)) { + EmitExpressionAsVoid(_lambda.Body, flags); + } else { + EmitExpression(_lambda.Body, flags); + } + + // Return must be the last instruction in a CLI method. + // But if we're inlining the lambda, we want to leave the return + // value on the IL stack. + if (!inlined) { + _ilg.Emit(OpCodes.Ret); + } + + _scope.Exit(); + + // Validate labels + Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda); + foreach (LabelInfo label in _labelInfo.Values) { + label.ValidateFinish(); + } + } + } +} diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/.gitattributes b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/.gitattributes deleted file mode 100644 index 31e93e4e9e3..00000000000 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/ExtensionAssemblyInfo.cs -crlf diff --git a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/ExtensionAssemblyInfo.cs b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/ExtensionAssemblyInfo.cs index bc47e001f31..2a2c1c0f76c 100644 --- a/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/ExtensionAssemblyInfo.cs +++ b/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Properties/ExtensionAssemblyInfo.cs @@ -1,62 +1,62 @@ -/* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
-using System;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Security;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Microsoft.Scripting.ExtensionAttribute")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Microsoft")]
-[assembly: AssemblyProduct("Microsoft.Scripting.ExtensionAttribute")]
-[assembly: AssemblyCopyright("� Microsoft Corporation. All rights reserved.")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-[assembly: CLSCompliant(true)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("b828a36d-f568-48a7-9bdd-412b0a1bfa32")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Revision and Build Numbers
-// by using the '*' as shown below:
-[assembly: SecurityTransparent]
-
-[assembly: System.Resources.NeutralResourcesLanguage("en-US")]
-
-#if !SILVERLIGHT
-[assembly: AssemblyVersion("2.0.0.0")] // shouldn't change, this assembly is unchanged. This is the version it originally shipped as.
-[assembly: AssemblyFileVersion("1.0.1.00")]
-[assembly: AssemblyInformationalVersion("1.0.1")]
-[assembly: AllowPartiallyTrustedCallers]
-#endif
+/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.Scripting.ExtensionAttribute")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Microsoft.Scripting.ExtensionAttribute")] +[assembly: AssemblyCopyright("� Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +[assembly: CLSCompliant(true)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b828a36d-f568-48a7-9bdd-412b0a1bfa32")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: SecurityTransparent] + +[assembly: System.Resources.NeutralResourcesLanguage("en-US")] + +#if !SILVERLIGHT +[assembly: AssemblyVersion("2.0.0.0")] // shouldn't change, this assembly is unchanged. This is the version it originally shipped as. +[assembly: AssemblyFileVersion("1.0.1.00")] +[assembly: AssemblyInformationalVersion("1.0.1")] +[assembly: AllowPartiallyTrustedCallers] +#endif |