private void GenerateMethodBodies() { ClrCodegen codegen = new ClrCodegen(); _program.Accept(codegen); MainFunc = FindMain(); }
private bool EmitBranchable(IRppNode expr, Label targetLabel, bool onTrue) { ClrCodegen nestedCodegen = new ClrCodegen(_body, targetLabel, onTrue) {ClosureContext = ClosureContext}; expr.Accept(nestedCodegen); return nestedCodegen.Jumped; }
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(); }
public override void Visit(RppFuncCall node) { Console.WriteLine("Generating func call"); // TODO we should keep references to functions by making another pass of code gen before // real code generation if (node.Name == "ctor()") { _body.Emit(OpCodes.Ldarg_0); ConstructorInfo constructor = typeof(object).GetConstructor(Type.EmptyTypes); Debug.Assert(constructor != null, "constructor != null"); _body.Emit(OpCodes.Call, constructor); } else { // TODO Probably makes more sense to make RppConstructorCall ast, instead of boolean RppMethodInfo rppMethodInfo = node.Function; if (node.IsConstructorCall) { _body.Emit(OpCodes.Ldarg_0); node.Args.ForEach(arg => arg.Accept(this)); var constructor = rppMethodInfo.Native as ConstructorInfo; _body.Emit(OpCodes.Call, constructor); } else { if (!_inSelector) { if (rppMethodInfo.IsStatic) { var instanceField = rppMethodInfo.DeclaringType.Fields.First(f => f.Name == "_instance"); _body.Emit(OpCodes.Ldsfld, instanceField.Native); } else { if (node.IsFromClosure) { _body.Emit(OpCodes.Ldarg_0); Debug.Assert(ClosureContext?.CapturedThis != null, "CapturedThis != null"); _body.Emit(OpCodes.Ldfld, ClosureContext.CapturedThis); } else { _body.Emit(OpCodes.Ldarg_0); // load 'this' } } } // Create own code generator for arguments because they can have RppSelectors which may interfer with already existing RppSelector // myField.CallFunc(anotherInstanceFunc()) would set _inSelector to true and no 'this' will be loaded ClrCodegen codegen = new ClrCodegen(_typeBuilder, _body) {ClosureContext = ClosureContext}; node.Args.ForEach(arg => arg.Accept(codegen)); MethodInfo method = rppMethodInfo.Native as MethodInfo; if (method == null) // This is a stub, so generate code for it { CodegenForStub(rppMethodInfo); return; } Debug.Assert(method != null, "method != null"); if (method.IsGenericMethod) { Type[] methodTypeArgs = node.TypeArgs.Select(t => t.Value.NativeType).ToArray(); if (methodTypeArgs.Length != 0) { method = method.MakeGenericMethod(methodTypeArgs); } } _body.Emit(OpCodes.Callvirt, method); } } }