Example #1
0
        /// <inheritdoc/>
        protected override Tuple <IAssembly, IEnumerable <IAssembly> > RewriteAssemblies(
            Tuple <IAssembly, IEnumerable <IAssembly> > MainAndOtherAssemblies,
            IBinder Binder,
            ICompilerLog Log)
        {
            // In addition to emitting LLVM IR from managed code, flame-llvm must also
            // set up an environment in which managed code can run. Part of this
            // environment is the 'main' function: managed code expects an entry point
            // to look like this: `void|int Main(|string[])`, whereas a C 'main' function
            // must have the following signature: `int main(int, byte**)`.
            //
            // To bridge this divide, we'll generate a 'main' function and use that to
            // call the entry point.

            var originalAsm        = MainAndOtherAssemblies.Item1;
            var originalEntryPoint = originalAsm.GetEntryPoint();

            if (originalEntryPoint == null)
            {
                // We can't rewrite the entry point of an assembly that doesn't
                // have an entry point.
                return(MainAndOtherAssemblies);
            }

            // Generate the following class:
            //
            // public static class __entry_point
            // {
            //     [#builtin_abi("C")]
            //     [#builtin_llvm_linkage(external)]
            //     public static int main(int argc, byte** argv)
            //     {
            //         try
            //         {
            //             var parsedArgs = Environment.Initialize(argc, argv);
            //             return actual_entry_point(...);
            //             // --or--
            //             actual_entry_point(...);
            //             return 0;
            //         }
            //         catch (Exception ex)
            //         {
            //             Environment.HandleFatalException(ex);
            //             return 1;
            //         }
            //     }
            // }

            var mainAsm = new DescribedAssembly(
                originalAsm.Name,
                originalAsm.AssemblyVersion,
                Binder.Environment);

            var epType = new DescribedType(new SimpleName("__entry_point"), mainAsm);

            epType.AddAttribute(PrimitiveAttributes.Instance.StaticTypeAttribute);
            var mainThunk = new DescribedBodyMethod(
                new SimpleName("main"), epType, PrimitiveTypes.Int32, true);

            mainThunk.AddAttribute(new LLVMLinkageAttribute(LLVMLinkage.LLVMExternalLinkage));
            mainThunk.AddAttribute(LLVMAttributes.CreateAbiAttribute("C"));
            mainThunk.AddParameter(new DescribedParameter("argc", PrimitiveTypes.Int32));
            mainThunk.AddParameter(
                new DescribedParameter(
                    "argv",
                    PrimitiveTypes.UInt8
                    .MakePointerType(PointerKind.TransientPointer)
                    .MakePointerType(PointerKind.TransientPointer)));

            if (originalEntryPoint.HasSameSignature(mainThunk))
            {
                // We don't have to rewrite the entry point if the existing entry point
                // already has the expected form.
                return(MainAndOtherAssemblies);
            }

            var epCall = CreateEntryPointCall(originalEntryPoint, mainThunk, Binder);

            mainThunk.Body = epCall.Type.GetIsInteger()
                ? (IStatement) new ReturnStatement(
                new StaticCastExpression(epCall, PrimitiveTypes.Int32).Simplify())
                : new BlockStatement(new IStatement[]
            {
                new ExpressionStatement(epCall),
                new ReturnStatement(new IntegerExpression(0))
            });

            var handleExceptionMethod = BindEnvironmentHandleFatalExceptionMethod(Binder);

            if (handleExceptionMethod != null)
            {
                var exceptionParam = handleExceptionMethod.Parameters.Single <IParameter>();
                var catchClause    = new CatchClause(
                    new DescribedVariableMember(
                        exceptionParam.Name,
                        exceptionParam.ParameterType));

                catchClause.Body = new BlockStatement(new IStatement[]
                {
                    new ExpressionStatement(
                        new InvocationExpression(
                            handleExceptionMethod,
                            null,
                            new IExpression[]
                    {
                        catchClause.ExceptionVariable.CreateGetExpression()
                    })),

                    new ReturnStatement(new IntegerExpression(1))
                });

                mainThunk.Body = new TryStatement(
                    mainThunk.Body,
                    new CatchClause[] { catchClause });
            }

            epType.AddMethod(mainThunk);
            mainAsm.AddType(epType);
            mainAsm.EntryPoint = mainThunk;

            return(new Tuple <IAssembly, IEnumerable <IAssembly> >(
                       mainAsm,
                       new IAssembly[] { originalAsm }.Concat <IAssembly>(MainAndOtherAssemblies.Item2)));
        }