Example #1
0
        public override void Visit(RppClosure node)
        {
            // TODO actually we can use ast classes to create closure, RType with generic types
            // which already has generics manipulation so this is quite bad way of doing it
            TypeBuilder closureClass = _typeBuilder.DefineNestedType("c__Closure" + (_closureId++),
                TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.NestedPrivate);

            var rppGenericParameters = node.ClosureType.GenericParameters.ToArray();
            string[] closureGenericArgumentsNames = rppGenericParameters.Select(arg => "T" + arg.Name).ToArray();
            GenericTypeParameterBuilder[] gpBuilders = {};
            if (closureGenericArgumentsNames.Length > 0)
            {
                gpBuilders = closureClass.DefineGenericParameters(closureGenericArgumentsNames);
                for (int i = 0; i < gpBuilders.Length; i++)
                {
                    rppGenericParameters[i].SetGenericTypeParameterBuilder(gpBuilders[i]);
                }
            }

            Type[] argTypes = node.Bindings.Select(p => p.Type.Value.NativeType).ToArray();
            Type parentType = node.Type.Value.NativeType;

            var capturedVars = CreateFieldsForCapturedVars(closureClass, node.CapturedVars);
            var capturedParams = CreateFieldsForCapturedParams(closureClass, node.CapturedParams);
            var capturedThis = node.Context.IsCaptureThis ? CreatedCapturedThis(closureClass) : null;
            Type returnType = node.ReturnType.Value.NativeType;

            // We need to create closure's own generic parameters because we can't reuse function's ones
            /*
                def myFunc[A] = {
                    val k = (x : A) = x
                }

                will be expanded into
                class _Closure[_A] extends Function1[_A, _A]
                {
                    def apply(x: _A) = x
                }

                def myFunc[A] = {
                    val k: _Closure[A] = new _Closure[A]()
                }

                So _Closure needs to have it's own generic parameter _A which will be substituted with A

                So we make an array with params:
                [T1, T2, ..., TRes] then find generic params, then define generic params for closure, then
                replace them with [_T1, _T2, ...., _TRes] (excluding non generic params)
            */
            Type[] closureSignature = argTypes.Concat(returnType).ToArray();

            if (closureGenericArgumentsNames.Length > 0)
            {
                var targetSignature = closureSignature.Select(t =>
                    {
                        if (t.IsGenericParameter)
                        {
                            Type closureGenericArgument = gpBuilders[t.GenericParameterPosition];
                            return closureGenericArgument;
                        }
                        return t;
                    }).ToArray();

                returnType = targetSignature.Last();
                argTypes = targetSignature.Take(targetSignature.Length - 1).ToArray();

                if (parentType.IsGenericType) // Parent is not generic if it's Action0
                {
                    Type genericTypeDef = parentType.GetGenericTypeDefinition();
                    if (returnType == typeof(void))
                    {
                        parentType = genericTypeDef.MakeGenericType(argTypes); // don't include 'void'
                    }
                    else
                    {
                        parentType = genericTypeDef.MakeGenericType(targetSignature);
                    }
                }
            }

            closureClass.AddInterfaceImplementation(parentType);
            MethodBuilder applyMethod = closureClass.DefineMethod("apply",
                MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.Public,
                CallingConventions.Standard);

            applyMethod.SetParameters(argTypes);
            applyMethod.SetReturnType(returnType);

            int index = 1;
            foreach (var param in node.Bindings)
            {
                applyMethod.DefineParameter(index, ParameterAttributes.None, param.Name);
                param.Index = index++;
            }

            ILGenerator body = applyMethod.GetILGenerator();
            ClrClosureContext closureContext = new ClrClosureContext
            {
                CapturedVars = capturedVars,
                CapturedParams = capturedParams,
                CapturedThis = capturedThis
            };
            ClrCodegen codegen = new ClrCodegen(body, closureContext);
            node.Expr.Accept(codegen);
            body.Emit(OpCodes.Ret);

            Type[] typeArguments = node.OriginalGenericArguments.Select(arg => arg.Type.NativeType).ToArray();
            Type specializedClosureClass = typeArguments.NonEmpty() ? closureClass.MakeGenericType(typeArguments) : closureClass;
            ConstructorInfo defaultClosureConstructor = closureClass.DefineDefaultConstructor(MethodAttributes.Public);

            if (closureGenericArgumentsNames.Length > 0)
            {
                defaultClosureConstructor = TypeBuilder.GetConstructor(specializedClosureClass, defaultClosureConstructor);
            }

            Debug.Assert(defaultClosureConstructor != null, "defaultClosureConstructor != null");
            _body.Emit(OpCodes.Newobj, defaultClosureConstructor);
            LocalBuilder closureClassInstance = _body.DeclareLocal(specializedClosureClass);
            _body.Emit(OpCodes.Stloc, closureClassInstance);

            // Initialize captured local vars
            capturedVars.ForEach(pair =>
                {
                    _body.Emit(OpCodes.Ldloc, closureClassInstance);
                    LocalBuilder capturedVariable = pair.Key;
                    _body.Emit(OpCodes.Ldloc, capturedVariable);
                    FieldInfo field = pair.Value.Item1;
                    if (specializedClosureClass.IsGenericType)
                        field = TypeBuilder.GetField(specializedClosureClass, field);
                    _body.Emit(OpCodes.Stfld, field);
                });

            // Initialize captured this
            if (capturedThis != null)
            {
                _body.Emit(OpCodes.Ldloc, closureClassInstance);
                _body.Emit(OpCodes.Ldarg_0);
                _body.Emit(OpCodes.Stfld, specializedClosureClass.IsGenericType ? TypeBuilder.GetField(specializedClosureClass, capturedThis) : capturedThis);
            }

            // Initialize captured params
            capturedParams.ForEach(pair =>
                {
                    _body.Emit(OpCodes.Ldloc, closureClassInstance);
                    int argIndex = pair.Key;
                    LoadArg(argIndex);
                    FieldInfo capturedParam = pair.Value;

                    capturedParam = specializedClosureClass.IsGenericType ? TypeBuilder.GetField(specializedClosureClass, capturedParam) : capturedParam;
                    _body.Emit(OpCodes.Stfld, capturedParam);
                });

            _body.Emit(OpCodes.Ldloc, closureClassInstance);
            closureClass.CreateType();
        }
Example #2
0
 public ClrCodegen(ILGenerator body, ClrClosureContext closureContext) : this(body)
 {
     ClosureContext = closureContext;
 }