예제 #1
0
        /// <summary>
        /// Prepares a closure for the the specified variables.
        /// </summary>
        /// <param name="variables">The variables with their required storage kind in the resulting closure.</param>
        /// <returns>An object containing information about the closure, usable to emit variable storage operations.</returns>
        public static ClosureInfo Create(IList <KeyValuePair <ParameterExpression, StorageKind> > variables)
        {
            var count = variables.Count;

            //
            // First, create an array of types for the locals that need to be stored in
            // the closure. Boxed locals will be wrapped in a StrongBox<T>.
            //
            var types = new Type[count];

            for (var i = 0; i < count; i++)
            {
                var variable = variables[i];
                var type     = variable.Key.Type;

                if ((variable.Value & StorageKind.Boxed) != 0)
                {
                    types[i] = typeof(StrongBox <>).MakeGenericType(type);
                }
                else
                {
                    Debug.Assert(variable.Value != StorageKind.Local, "Only expected hoisted locals.");
                    types[i] = type;
                }
            }

            //
            // Next, create a map to associate the original hoisted locals to their
            // closure field storage.
            //
            var closureType = ClosureFactory.GetClosureType(types);
            var fieldMap    = new Dictionary <ParameterExpression, ClosureFieldInfo>(count);

            for (var i = 0; i < count; i++)
            {
                var variable = variables[i];

                var field = closureType.GetField("Item" + (i + 1));

                var info = new ClosureFieldInfo
                {
                    Field = field,
                    Kind  = variable.Value,
                };

                fieldMap.Add(variable.Key, info);
            }

            //
            // Return an object holding the gathered information, providing means to
            // emit variable storage operations for the hoisted locals provided.
            //
            return(new ClosureInfo(closureType, fieldMap));
        }
예제 #2
0
        public void GetClosureType_Runtime()
        {
            //
            // Check creation of closure types of various arities.
            //
            foreach (var i in new[] { 17, 18, 31, 65 })
            {
                var types1 = Enumerable.Repeat(typeof(char), i).ToArray();
                var types2 = Enumerable.Repeat(typeof(bool), i).ToArray();

                //
                // Should reuse the same generic closure types.
                //
                var c1 = ClosureFactory.GetClosureType(types1);
                var c2 = ClosureFactory.GetClosureType(types1);
                Assert.AreSame(c1, c2);

                //
                // Generic type definitions should be reused across type instantiations.
                //
                var c3 = ClosureFactory.GetClosureType(types2);
                Assert.AreSame(c1.GetGenericTypeDefinition(), c3.GetGenericTypeDefinition());
            }
        }
예제 #3
0
        private static void AssertClosureType(Type[] types, Dictionary <Type, object[]> values)
        {
            //
            // Create closure type through factory.
            //
            var c = ClosureFactory.GetClosureType(types);

            //
            // Assert some type system properties.
            //
            Assert.IsTrue(c.IsPublic);
            Assert.IsTrue(c.IsSealed);
            Assert.IsTrue(c.IsClass);
            Assert.AreEqual(typeof(object), c.BaseType);
            Assert.IsTrue(typeof(IRuntimeVariables).IsAssignableFrom(c));

            //
            // Ensure existence of a default constructor.
            //
            var ctor = c.GetConstructor(Type.EmptyTypes);

            Assert.IsNotNull(ctor);

            //
            // Create an instance to test behavior.
            //
            var o = Activator.CreateInstance(c);
            var r = (IRuntimeVariables)o;

            //
            // Assert that Count returns the right value.
            //
            Assert.AreEqual(types.Length, r.Count);

            //
            // Assert indexer exception behavior.
            //
            var n = types.Length;

            Assert.ThrowsException <ArgumentOutOfRangeException>(() => r[-1]);
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => r[-1] = null);
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => r[n]);
            Assert.ThrowsException <ArgumentOutOfRangeException>(() => r[n] = null);

            //
            // Find fields according to the Item* naming pattern.
            //
            for (var i = 0; i < types.Length; i++)
            {
                var type  = types[i];
                var value = values[type][0];

                var field = c.GetField("Item" + (i + 1));
                Assert.IsNotNull(field);
                Assert.AreEqual(type, field.FieldType);
                Assert.IsTrue(field.IsPublic);
                Assert.IsFalse(field.IsStatic);
                Assert.IsFalse(field.IsLiteral);
                Assert.IsFalse(field.IsInitOnly);

                //
                // Set value of the field.
                //
                field.SetValue(o, value);

                //
                // Check behavior of indexer getter.
                //
                Assert.AreEqual(value, r[i]);

                //
                // Assert regular behavior of indexer setter.
                //
                var newValue = values[type][1];
                r[i] = newValue;
                Assert.AreEqual(newValue, field.GetValue(o));
                Assert.AreEqual(newValue, r[i]);
                r[i] = value;
                Assert.AreEqual(value, field.GetValue(o));
                Assert.AreEqual(value, r[i]);

                //
                // Assert cast behavior of indexer setter.
                //
                Assert.ThrowsException <InvalidCastException>(() => { r[i] = typeof(int); });
            }
        }