Esempio n. 1
0
        private static void InternalConvertSetupExecutionFrame(
            CodeTextWriter tw,
            IExtractContextHost extractContext,
            PreparedMethodInformation preparedMethod,
            ILocalVariableInformation[] objRefEntries,
            ILocalVariableInformation[] valueEntries,
            DebugInformationWriteController debugInformationController)
        {
            tw.WriteLine("//-------------------");
            tw.WriteLine("// [3-5] Setup execution frame:");
            tw.SplitLine();

            // Important NULL assigner (p = NULL):
            //   Because these variables are pointer (of object reference 'O' type).
            //   So GC will traverse these variables just setup the stack frame.
            debugInformationController.WriteInformationBeforeCode(tw);
            tw.WriteLine(
                "{0}_EXECUTION_FRAME__ frame__ =",
                preparedMethod.Method.CLanguageFunctionName);
            using (var __ = tw.Shift())
            {
                if (valueEntries.Length >= 1)
                {
                    tw.WriteLine(
                        "{{ NULL, {0}, {1}, {2} }};",
                        objRefEntries.Length,
                        valueEntries.Length,
                        string.Join(
                            ", ",
                            objRefEntries.Select(___ => "NULL").
                            Concat(valueEntries.
                                   Select(valueEntry =>
                                          string.Format(
                                              "il2c_typeof({0}), NULL",
                                              valueEntry.TargetType.MangledUniqueName)))));
                }
                else
                {
                    // Make short initializer expression if value type not included,
                    // maybe C compiler makes better code.
                    tw.WriteLine(
                        "{{ NULL, {0} }};",
                        objRefEntries.Length);
                }
            }

            foreach (var valueEntry in valueEntries)
            {
                debugInformationController.WriteInformationBeforeCode(tw);
                tw.WriteLine(
                    "frame__.{0}_value_ptr__ = &{0};",
                    extractContext.GetSymbolName(valueEntry));
            }

            debugInformationController.WriteInformationBeforeCode(tw);
            tw.WriteLine("il2c_link_execution_frame(&frame__);");
            tw.SplitLine();
        }
Esempio n. 2
0
        private static void InternalConvertFromFunction(
            CodeTextWriter tw,
            IExtractContextHost extractContext,
            PreparedMethodInformation preparedMethod,
            DebugInformationOptions debugInformationOption)
        {
            var locals = preparedMethod.Method.LocalVariables;

            tw.WriteLine("///////////////////////////////////////");
            tw.WriteLine(
                "// [3] {0}{1}",
                preparedMethod.Method.IsVirtual ? "Virtual: " : string.Empty,
                preparedMethod.Method.FriendlyName);
            tw.SplitLine();

            var codeStream    = preparedMethod.Method.CodeStream;
            var objRefEntries = locals.
                                Concat(preparedMethod.Stacks).
                                Where(v => v.TargetType.IsReferenceType). // Only objref
                                ToArray();
            var valueEntries = locals.
                               Concat(preparedMethod.Stacks).
                               Where(v => v.TargetType.IsValueType && v.TargetType.IsRequiredTraverse).
                               ToArray();

            // Write declaring exception handlers
            if (codeStream.ExceptionHandlers.Length >= 1)
            {
                InternalConvertExceptionFilter(
                    tw,
                    extractContext,
                    preparedMethod,
                    codeStream);
            }

            // Write declaring execution frame
            if ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1))
            {
                InternalConvertExecutionFrame(
                    tw,
                    extractContext,
                    preparedMethod,
                    objRefEntries,
                    valueEntries);
            }

            // Make readable debugging comment, it was splitting and reducing same informations.
            var debugInformationController =
                new DebugInformationWriteController(
                    preparedMethod.Method,
                    debugInformationOption);

            // Start function:
            tw.WriteLine("//-------------------");
            tw.WriteLine("// [3-2] Function body:");
            tw.SplitLine();
            tw.WriteLine(preparedMethod.Method.CLanguageFunctionPrototype);
            tw.WriteLine("{");

            using (var _ = tw.Shift())
            {
                if (!preparedMethod.Method.IsStatic)
                {
                    debugInformationController.WriteInformationBeforeCode(tw);
                    tw.WriteLine("il2c_assert(this__ != NULL);");
                    tw.SplitLine();
                }

                var localDefinitions = preparedMethod.Method.LocalVariables.
                                       Where(local => !local.TargetType.IsReferenceType).
                                       ToArray();
                if (localDefinitions.Length >= 1)
                {
                    tw.WriteLine("//-------------------");
                    tw.WriteLine("// [3-3] Local variables (!objref):");
                    tw.SplitLine();

                    foreach (var local in localDefinitions)
                    {
                        var name = extractContext.GetSymbolName(local);

                        // HACK: The local variables mark to "volatile."
                        //   Because the gcc misread these variables calculated statically (or maybe assigned to the registers)
                        //   at compile time with optimization.
                        //   It will cause the strange results for exception handling (with sjlj.)

                        // We have to initialize the local variables.
                        if (local.TargetType.IsPrimitive ||
                            local.TargetType.IsPointer ||
                            local.TargetType.IsByReference)
                        {
                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "{0}{1} {2} = {3};",
                                (codeStream.ExceptionHandlers.Length >= 1) ? "volatile " : string.Empty,
                                local.TargetType.CLanguageTypeName,
                                name,
                                Utilities.GetCLanguageExpression(local.TargetType.InternalStaticEmptyValue));
                        }
                        else
                        {
                            Debug.Assert(local.TargetType.IsValueType);

                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "{0}{1} {2};",
                                (codeStream.ExceptionHandlers.Length >= 1) ? "volatile " : string.Empty,
                                local.TargetType.CLanguageTypeName,
                                name);

                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "memset({0}&{1}, 0x00, sizeof {1});",
                                (codeStream.ExceptionHandlers.Length >= 1) ? "(void*)" : string.Empty,
                                name);
                        }
                    }

                    tw.SplitLine();
                }

                var stackDefinitions = preparedMethod.Stacks.
                                       Where(stack => !stack.TargetType.IsReferenceType).
                                       ToArray();
                if (stackDefinitions.Length >= 1)
                {
                    tw.WriteLine("//-------------------");
                    tw.WriteLine("// [3-4] Evaluation stacks (!objref):");
                    tw.SplitLine();

                    foreach (var stack in stackDefinitions)
                    {
                        var name = extractContext.GetSymbolName(stack);

                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.WriteLine(
                            "{0} {1};",
                            stack.TargetType.CLanguageTypeName,
                            name);

                        // Note: We often don't have to initalize the evaluation stack variables.
                        //   Because these variables push value at first usage.
                        //   But the value type may contains objref field,
                        //   so we have to initialize for value type.
                        if (stack.TargetType.IsRequiredTraverse)
                        {
                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "memset(&{0}, 0, sizeof {0});",
                                name);
                        }
                    }

                    tw.SplitLine();
                }

                // Write doing setup execution frame
                if ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1))
                {
                    InternalConvertSetupExecutionFrame(
                        tw,
                        extractContext,
                        preparedMethod,
                        objRefEntries,
                        valueEntries,
                        debugInformationController);
                }

                tw.WriteLine("//-------------------");
                tw.WriteLine("// [3-6] IL body:");
                tw.SplitLine();

                // Set symbol prefix to make valid access variables.
                using (var __ = extractContext.BeginLocalVariablePrefix(
                           local => local.TargetType.IsReferenceType ? "frame__." : null))
                {
                    // Construct exception handler controller.
                    var exceptionHandlerController = new ExceptionHandlerController(
                        codeStream.ExceptionHandlers,
                        (handler, handlerIndex, nestedIndex) =>
                    {
                        var nestedIndexName = string.Format("nest{0}", nestedIndex);
                        extractContext.SetNestedExceptionFrameIndexName(nestedIndexName);

                        // Reached try block:
                        var filterName = string.Format(
                            "{0}_ExceptionFilter{1}__",
                            preparedMethod.Method.CLanguageFunctionName,
                            handlerIndex);

                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.WriteLine("il2c_try({0}, {1})", nestedIndexName, filterName);

                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.WriteLine("{");
                        tw.Shift();
                    },
                        (handler, handlerIndex, nestedIndex) =>
                    {
                        // Reached try end block:
                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.Shift(-1);
                        tw.WriteLine("}");
                    },
                        (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) =>
                    {
                        var nestedIndexName = extractContext.GetExceptionNestedFrameIndexName();
                        switch (catchHandler.CatchHandlerType)
                        {
                        case ExceptionCatchHandlerTypes.Catch:
                            // Reached catch block:
                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "il2c_catch({0}, {1}, {2})  // catch ({3})",
                                nestedIndexName,
                                catchHandlerIndex + 1,
                                extractContext.GetSymbolName(preparedMethod.CatchVariables[catchHandler.CatchStart]),
                                catchHandler.CatchType.MangledUniqueName);
                            break;

                        case ExceptionCatchHandlerTypes.Finally:
                            // Reached finally block:
                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine("il2c_finally({0})", nestedIndexName);
                            break;
                        }
                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.WriteLine("{");
                        tw.Shift();
                    },
                        (handler, handlerIndex, nestedIndex, catchHandler, catchHandlerIndex) =>
                    {
                        // Reached catch end block:
                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.Shift(-1);
                        tw.WriteLine("}");
                    },
                        (handler, handlerIndex, nestedIndex, parentHandler, parentHandlerIndex, parentNestedIndex) =>
                    {
                        var nestedIndexName       = extractContext.GetExceptionNestedFrameIndexName();
                        var parentNestedIndexName = (parentNestedIndex >= 0) ? string.Format("nest{0}", parentNestedIndex) : null;

                        // Write leave bind expressions if needed.
                        // Extract the continuation fromOffset inside at mostly inner exception handler.
                        var bindEntries =
                            preparedMethod.LeaveContinuations.
                            SelectMany(entry => codeStream.ExceptionHandlers.
                                       // nested exception handlers: inner --> outer
                                       Reverse().
                                       // Is this handler contains leave continuation target?
                                       Where(h => entry.Value.fromOffsets.Any(offset => h.ContainsOffset(offset))).
                                       // Found.
                                       Select(h => new { handler = h, continuationIndex = entry.Key, entry.Value.targetOffset })).
                            // ... is current handler?
                            Where(entry => entry.handler.Equals(handler)).
                            ToArray();
                        if (bindEntries.Length >= 1)
                        {
                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine("il2c_leave_to({0})", nestedIndexName);

                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine("{");

                            using (var ___ = tw.Shift())
                            {
                                foreach (var bind in bindEntries)
                                {
                                    if ((parentNestedIndex < 0) ||
                                        codeStream.ExceptionHandlers[parentNestedIndex].ContainsOffset(bind.targetOffset))
                                    {
                                        var labelName = preparedMethod.LabelNames[bind.targetOffset];

                                        debugInformationController.WriteInformationBeforeCode(tw);
                                        tw.WriteLine(
                                            "il2c_leave_bind({0}, {1}, {2});",
                                            nestedIndexName,
                                            bind.continuationIndex,
                                            labelName);
                                    }
                                    else
                                    {
                                        debugInformationController.WriteInformationBeforeCode(tw);
                                        tw.WriteLine(
                                            "il2c_leave_through({0}, {1}, {2});",
                                            nestedIndexName, bind.continuationIndex, parentNestedIndexName);
                                    }
                                }
                            }

                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine("}");
                        }

                        // Reached end of entire try block.
                        debugInformationController.WriteInformationBeforeCode(tw);
                        tw.WriteLine("il2c_end_try({0});", nestedIndexName);

                        extractContext.SetNestedExceptionFrameIndexName(parentNestedIndexName);
                    });

                    // Traverse code fragments.
                    foreach (var ci in codeStream)
                    {
                        debugInformationController.SetNextCode(ci);

                        // 1: Update the exception handler controller.
                        //    (Will write exception related sentences.)
                        exceptionHandlerController.Update(ci);

                        // 2: Write label if available and used.
                        if (preparedMethod.LabelNames.TryGetValue(ci.Offset, out var labelName))
                        {
                            using (var ___ = tw.Shift(-1))
                            {
                                tw.WriteLine("{0}:", labelName);
                            }
                        }

                        // 3: Write source code comment.
                        debugInformationController.WriteCodeComment(tw);

                        // 4: Generate source code fragments and write.
                        var sourceCodes = preparedMethod.Generators[ci.Offset](extractContext);
                        foreach (var sourceCode in sourceCodes)
                        {
                            // DIRTY HACK:
                            //   Write unlink execution frame code if cause exiting method.
                            if (sourceCode.StartsWith("return") &&
                                ((objRefEntries.Length >= 1) || (valueEntries.Length >= 1)))
                            {
                                debugInformationController.WriteInformationBeforeCode(tw);
                                tw.WriteLine(
                                    "il2c_unlink_execution_frame(&frame__);");
                            }

                            debugInformationController.WriteInformationBeforeCode(tw);
                            tw.WriteLine(
                                "{0};",
                                sourceCode);
                        }
                    }

                    // If last opcode is 'endfinally' and not emitted 'ret',
                    // can't finished for exceptionHandlerController.
                    // We can check and force update the TryFinish method for this situation.
                    exceptionHandlerController.TryFinish();

                    if (!exceptionHandlerController.IsFinished)
                    {
                        throw new InvalidProgramSequenceException(
                                  "Invalid exception handler range. MethodName={0}, ExceptionHandlers=[{1}]",
                                  preparedMethod.Method.FriendlyName,
                                  string.Join(
                                      ",",
                                      codeStream.ExceptionHandlers.
                                      Select(handler => string.Format("[{0}]", handler))));
                    }
                }
            }

            debugInformationController.WriteInformationBeforeCode(tw);
            tw.WriteLine("}");
            tw.SplitLine();
        }