Esempio n. 1
0
        /// <summary>
        /// Create the body of the values() method.
        /// </summary>
        private AstBlock CreateValuesBody()
        {
            var fields = XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)).ToList();

            // Allocate array
            var arrVar = new AstGeneratedVariable("array", "array")
            {
                Type = new XArrayType(XType)
            };
            var createArr = new AstExpression(AstNode.NoSource, AstCode.Stloc, arrVar,
                                              new AstExpression(AstNode.NoSource, AstCode.Newarr, XType,
                                                                new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, fields.Count)));
            var ast = AstBlock.CreateOptimizedForTarget(createArr);

            // Initialize array
            var index = 0;

            foreach (var field in fields)
            {
                ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stelem_Any, null,
                                               new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar),
                                               new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, index),
                                               new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field)));
                index++;
            }

            // Return array
            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null,
                                           new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar)));
            return(ast);
        }
Esempio n. 2
0
        private static void ConvertAsNativeIFormattable(AstExpression node, XTypeSystem typeSystem)
        {
            var method = (XMethodReference)node.Operand;
            var type   = method.ReturnType;

            if (method.Name == "AsNativeIFormattable" &&
                method.DeclaringType.Name == InternalConstants.CompilerHelperName &&
                type.FullName == "System.IFormattable")
            {
                // make sure we don't evaluate the expression twice.
                var tempVar = new AstGeneratedVariable("temp$$", null)
                {
                    Type = typeSystem.Object
                };
                var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0])
                {
                    ExpectedType = typeSystem.Object
                };
                var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(typeSystem.Object);

                // Convert to "(x instanceof T) ? (T)x : null"

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);
                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);
                // null
                var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(type);
                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type,
                                                    instanceofExpr, txExpr, nullExpr).SetType(type);
                node.CopyFrom(conditional);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Convert a nullable .GetValueOrDefault()
        /// </summary>
        private static void ConvertGetValueOrDefault(AstExpression node, XTypeReference type, XModule module)
        {
            var defExpr = node.Arguments.Count == 1
                            ? new AstExpression(node.SourceLocation, AstCode.DefaultValue, type).SetType(type)
                            : node.Arguments[1];

            defExpr.ExpectedType = type;

            if (type.IsPrimitive)
            {
                // replace with obj != null ? unbox(obj) : defExpr
                AstExpression compareExpr, valueExpr;

                var loadExpr = node.Arguments[0];
                ConvertLoad(loadExpr);
                loadExpr.InferredType = module.TypeSystem.Object;
                loadExpr.ExpectedType = module.TypeSystem.Object;

                if (loadExpr.Code != AstCode.Ldloc)
                {
                    // TODO: how can we get the backend to remove/combine these variables again?
                    var tmpVar = new AstGeneratedVariable("tmp$", null)
                    {
                        Type = module.TypeSystem.Object
                    };
                    compareExpr = new AstExpression(node.SourceLocation, AstCode.Stloc, tmpVar, loadExpr).SetType(module.TypeSystem.Object);
                    valueExpr   = new AstExpression(node.SourceLocation, AstCode.Ldloc, tmpVar).SetType(module.TypeSystem.Object);
                }
                else
                {
                    compareExpr = loadExpr;
                    valueExpr   = loadExpr;
                }
                valueExpr = new AstExpression(node.SourceLocation, AstCode.Unbox, type, valueExpr).SetType(type);

                var newNode = new AstExpression(node.SourceLocation, AstCode.Conditional, type,
                                                compareExpr, valueExpr, defExpr)
                              .SetType(type);
                node.CopyFrom(newNode);
            }
            else
            {
                // replace with obj ?? defExpr

                var loadExpr = node.Arguments[0];
                ConvertLoad(loadExpr);

                if (!type.IsSame(loadExpr.InferredType))
                {
                    //loadExpr.ExpectedType = type;
                    // todo: how to get the cast inserted automatically?
                    loadExpr = new AstExpression(loadExpr.SourceLocation, AstCode.SimpleCastclass, type, loadExpr);
                }

                var nullCoalescing = new AstExpression(node.SourceLocation, AstCode.NullCoalescing, null, loadExpr, defExpr);
                nullCoalescing.InferredType = type;
                node.CopyFrom(nullCoalescing);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Convert the given set of bytecodes into a list of Ast nodes.
        /// </summary>
        private static IEnumerable <AstNode> ConvertRangeToAst(IEnumerable <ByteCode> range)
        {
            var ast = new List <AstNode>();

            // Convert stack-based java bytecode code to Ast tree
            foreach (var byteCode in range)
            {
                var ilRange = new InstructionRange(byteCode.Offset, byteCode.EndOffset);
                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                var expr = new AstExpression(byteCode.SourceLocation, byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);

                // Label for this instruction
                if (byteCode.Label(false) != null)
                {
                    ast.Add(byteCode.Label(false));
                }

                // Reference arguments using temporary variables
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new AstExpression(byteCode.SourceLocation, AstCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    var tmpVar = new AstGeneratedVariable("expr_" + byteCode.Offset.ToString("X2"), byteCode.StoreTo.Select(x => x.OriginalName).FirstOrDefault());
                    ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, tmpVar, expr));
                    foreach (var storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(byteCode.SourceLocation, AstCode.Ldloc, tmpVar)));
                    }
                }
            }

            return(ast);
        }
Esempio n. 5
0
        /// <summary>
        /// Runs a very simple form of copy propagation.
        /// Copy propagation is used in two cases:
        /// 1) assignments from arguments to local variables
        ///    If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg),
        ///    then we can replace the variable with the argument.
        /// 2) assignments of address-loading instructions to local variables
        /// </summary>
        public void CopyPropagation()
        {
            foreach (AstBlock block in method.GetSelfAndChildrenRecursive <AstBlock>())
            {
                for (int i = 0; i < block.Body.Count; i++)
                {
                    AstVariable   v;
                    AstExpression copiedExpr;
                    if (block.Body[i].Match(AstCode.Stloc, out v, out copiedExpr) &&
                        !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 &&
                        CanPerformCopyPropagation(copiedExpr, v))
                    {
                        // un-inline the arguments of the ldArg instruction
                        var uninlinedArgs = new AstVariable[copiedExpr.Arguments.Count];
                        for (int j = 0; j < uninlinedArgs.Length; j++)
                        {
                            uninlinedArgs[j] = new AstGeneratedVariable(v.Name + "_cp_" + j, v.OriginalName);
                            block.Body.Insert(i++, new AstExpression(copiedExpr.SourceLocation, AstCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j]));
                        }

                        // perform copy propagation:
                        foreach (var expr in method.GetSelfAndChildrenRecursive <AstExpression>())
                        {
                            if (expr.Code == AstCode.Ldloc && expr.Operand == v)
                            {
                                expr.Code    = copiedExpr.Code;
                                expr.Operand = copiedExpr.Operand;
                                for (int j = 0; j < uninlinedArgs.Length; j++)
                                {
                                    expr.Arguments.Add(new AstExpression(copiedExpr.SourceLocation, AstCode.Ldloc, uninlinedArgs[j]));
                                }
                            }
                        }

                        block.Body.RemoveAt(i);
                        if (uninlinedArgs.Length > 0)
                        {
                            // if we un-inlined stuff; we need to update the usage counters
                            AnalyzeMethod();
                        }
                        InlineInto(block.Body, i, aggressive: false);                         // maybe inlining gets possible after the removal of block.Body[i]
                        i -= uninlinedArgs.Length + 1;
                    }
                }
            }
        }
Esempio n. 6
0
        private static void ConvertUnboxStruct(AstExpression node, XTypeReference resultType, XTypeSystem typeSystem)
        {
            // Structs must never be null. We have to handle structs here, since a newobj
            // might need generic arguments. These would be difficult to provide at "UnboxFromGeneric",
            // but will be automatically filled in by the GenericInstanceConverter

            // convert to (temp$ = (T)x) != null ? temp$ : default(T)

            // replace any unbox, but keep if otherwise.
            var clone = node.Code == AstCode.Unbox ? new AstExpression(node.Arguments[0]) : new AstExpression(node);

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$", "")
            {
                Type = typeSystem.Object
            };

            // T(x)
            var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, resultType, clone)
                         .SetType(resultType);

            // temporary storage
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, txExpr)
            {
                ExpectedType = resultType
            };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar)
                              .SetType(resultType);

            // default (T)
            var defaultT    = new AstExpression(node.SourceLocation, AstCode.DefaultValue, resultType).SetType(resultType);
            var constructor = StructCallConverter.GetDefaultValueCtor(resultType.Resolve());

            StructCallConverter.ConvertDefaultValue(defaultT, constructor);

            // Combine
            var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, resultType,
                                                storeTempVar, loadTempVar, defaultT)
                              .SetType(resultType);

            node.CopyFrom(conditional);
        }
Esempio n. 7
0
        private static void AddJumpInstruction(List <AstNode> body, ISourceLocation currentLoc,
                                               AstGeneratedVariable setInstructionTarget, ref int idx,
                                               ref AstLabel label, int firstValidExpression, AstExpression initExpr,
                                               ref int labelCount)
        {
            if (label == null)
            {
                label = new AstLabel(AstNode.NoSource, "setInstructionTarget_" + (++labelCount));
                body.Insert(firstValidExpression, label);
                body.Insert(firstValidExpression + 1, initExpr);
                idx += 2;
            }

            var branch = new AstExpression(currentLoc, AstCode.Brtrue, label,
                                           new AstExpression(currentLoc, AstCode.Ldloc, setInstructionTarget)
            {
                InferredType = setInstructionTarget.Type
            });

            body.Insert(idx, branch);
            idx += 1;
        }
Esempio n. 8
0
        /// <summary>
        /// Convert node with code Cast.
        /// </summary>
        private static void ConvertCastclass(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference)node.Operand;

            if (type.IsSystemArray())
            {
                // Call cast method
                var arrayHelper     = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var castToArray     = arrayHelper.Methods.First(x => x.Name == "CastToArray");
                var castToArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, node.Arguments).SetType(type);
                node.CopyFrom(castToArrayExpr);
                return;
            }

            string castMethod = null;

            if (type.IsSystemCollectionsIEnumerable() ||
                type.IsSystemCollectionsIEnumerableT())
            {
                castMethod = "CastToEnumerable";
            }
            else if (type.IsSystemCollectionsICollection() ||
                     type.IsSystemCollectionsICollectionT())
            {
                castMethod = "CastToCollection";
            }
            else if (type.IsSystemCollectionsIList() ||
                     type.IsSystemCollectionsIListT())
            {
                castMethod = "CastToList";
            }
            else if (type.IsSystemIFormattable())
            {
                castMethod = "CastToFormattable";
            }

            if (castMethod != null)
            {
                // make sure we don't evaluate the expression twice.
                var tempVar = new AstGeneratedVariable("temp$$", null)
                {
                    Type = compiler.Module.TypeSystem.Object
                };
                var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0])
                {
                    ExpectedType = compiler.Module.TypeSystem.Object
                };
                var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);

                // Call cast method
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var castToArray = arrayHelper.Methods.First(x => x.Name == castMethod);

                // Call "(x instanceof T) ? (T)x : asMethod(x)"

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // CastX(x)
                var castXExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, loadTempVar).SetType(typeSystem.Object);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, castXExpr).SetType(type);

                node.CopyFrom(conditional);
                return;
            }

            // Normal castclass
            node.Code = AstCode.SimpleCastclass;
        }
Esempio n. 9
0
        /// <summary>
        /// Create the body of the class ctor.
        /// </summary>
        private AstBlock CreateClassCtorBody(bool isWide, XFieldDefinition enumInfoField, XFieldDefinition defaultField, XMethodReference enumInfoCtor, XTypeReference valueType, XTypeSystem typeSystem)
        {
            var internalEnumType     = Compiler.GetDot42InternalType("Enum");
            var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo");
            var valueToFieldMap      = new Dictionary <object, XFieldDefinition>();
            var ldc = isWide ? AstCode.Ldc_I8 : AstCode.Ldc_I4;

            var nameVar = new AstGeneratedVariable("enumName", null)
            {
                Type = typeSystem.String
            };
            var enumInfoVar = new AstGeneratedVariable("enumInfo", null)
            {
                Type = internalEnumInfoType
            };
            var valVar = new AstGeneratedVariable("val", null)
            {
                Type = enumInfoField.FieldType
            };

            var ast = AstBlock.CreateOptimizedForTarget(
                // Instantiate enum info field
                new AstExpression(AstNode.NoSource, AstCode.Stsfld, enumInfoField,
                                  new AstExpression(AstNode.NoSource, AstCode.Stloc, enumInfoVar,
                                                    new AstExpression(AstNode.NoSource, AstCode.Newobj, enumInfoCtor))));

            // Instantiate values for each field
            var ordinal = 0;

            foreach (var field in XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)))
            {
                // Find dex field
                object value;
                if (!field.TryGetEnumValue(out value))
                {
                    throw new CompilerException(string.Format("Cannot get enum value from field {0}", field.FullName));
                }
                value = isWide ? (object)XConvert.ToLong(value) : (object)XConvert.ToInt(value);
                XFieldDefinition existingField;
                AstExpression    valueExpr;
                if (valueToFieldMap.TryGetValue(value, out existingField))
                {
                    // Re-use instance of existing field
                    valueExpr = new AstExpression(AstNode.NoSource, AstCode.Ldsfld, existingField);
                }
                else
                {
                    // Record
                    valueToFieldMap[value] = field;

                    // Call ctor
                    valueExpr = new AstExpression(AstNode.NoSource, AstCode.Newobj, ctor,
                                                  new AstExpression(AstNode.NoSource, AstCode.Stloc, nameVar,
                                                                    new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name)),
                                                  new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, ordinal),
                                                  new AstExpression(AstNode.NoSource, AstCode.Stloc, valVar,
                                                                    new AstExpression(AstNode.NoSource, ldc, value)));
                }

                // Initialize static field
                var storeExpression = new AstExpression(AstNode.NoSource, AstCode.Stsfld, field, valueExpr);

                // Add to info
                var addMethod = new XMethodReference.Simple("Add", true, typeSystem.Void, internalEnumInfoType,
                                                            XParameter.Create("value", valueType),
                                                            XParameter.Create("name", typeSystem.String),
                                                            XParameter.Create("instance", internalEnumType));
                ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Call, addMethod,
                                               new AstExpression(AstNode.NoSource, AstCode.Ldloc, enumInfoVar),
                                               new AstExpression(AstNode.NoSource, AstCode.Ldloc, valVar),
                                               new AstExpression(AstNode.NoSource, AstCode.Ldloc, nameVar),
                                               storeExpression));

                // Increment ordinal
                ordinal++;
            }

            // Initialize default field
            var getValueMethod = new XMethodReference.Simple("GetValue", true, internalEnumType, internalEnumInfoType,
                                                             XParameter.Create("value", valueType));

            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, defaultField,
                                           new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, XType,
                                                             new AstExpression(AstNode.NoSource, AstCode.Call, getValueMethod,
                                                                               new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField),
                                                                               new AstExpression(AstNode.NoSource, ldc, 0)))));

            // Return
            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null));
            return(ast);
        }
Esempio n. 10
0
        List<AstNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs)
        {
            var ast = new List<AstNode>();

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.TryStart.Offset);
                var tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++;
                    ast.AddRange(ConvertToAst(CollectionExtensions.CutRange(body, 0, tryStartIdx)));
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet<ExceptionHandler>(
                        ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++;
                    var converted = ConvertToAst(CollectionExtensions.CutRange(body, 0, tryEndIdx), nestedEHs);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var eh in handlers)
                {
                    var handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
                    var startIdx = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++;
                    var endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++;
                    var nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    var handlerAst = ConvertToAst(CollectionExtensions.CutRange(body, startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        var catchType = eh.CatchType.IsSystemObject() ? module.TypeSystem.Exception : XBuilder.AsTypeReference(module, eh.CatchType);
                        var catchBlock = new AstTryCatchBlock.CatchBlock(handlerAst.Select(x => x.SourceLocation).FirstOrDefault(), tryCatchBlock)
                        {
                            ExceptionType = catchType,
                            Body = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        var ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                        {
                            // Exception is not used
                            catchBlock.ExceptionVariable = null;
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            var first = catchBlock.Body[0] as AstExpression;
                            if (first != null &&
                                first.Code == AstCode.Pop &&
                                first.Arguments[0].Code == AstCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
                                    catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                                else
                                    catchBlock.ExceptionVariable = null;
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (var storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new AstBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new AstBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return ast;
        }
Esempio n. 11
0
        private List <AstNode> ConvertToAst(IEnumerable <ByteCode> body)
        {
            var ast = new List <AstNode>();

            // Convert stack-based IL code to ILAst tree
            foreach (var byteCode in body)
            {
                var ilRange = new InstructionRange(byteCode.Offset, byteCode.EndOffset);

                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                var expr = new AstExpression(byteCode.SequencePoint, byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);
                if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0)
                {
                    var prefixes = new AstExpressionPrefix[byteCode.Prefixes.Length];
                    for (var i = 0; i < prefixes.Length; i++)
                    {
                        var operand = byteCode.Prefixes[i].Operand;
                        if (operand is FieldReference)
                        {
                            operand = XBuilder.AsFieldReference(module, (FieldReference)operand);
                        }
                        else if (operand is MethodReference)
                        {
                            operand = XBuilder.AsMethodReference(module, (MethodReference)operand);
                        }
                        else if (operand is TypeReference)
                        {
                            operand = XBuilder.AsTypeReference(module, (TypeReference)operand);
                        }
                        prefixes[i] = new AstExpressionPrefix((AstCode)byteCode.Prefixes[i].OpCode.Code, operand);
                    }
                    expr.Prefixes = prefixes;
                }

                // Label for this instruction
                if (byteCode.Label != null)
                {
                    ast.Add(byteCode.Label);
                }

                // Reference arguments using temporary variables
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    var tmpVar = new AstGeneratedVariable("expr_" + byteCode.Offset.ToString("X2"), byteCode.StoreTo.Select(x => x.OriginalName).FirstOrDefault());
                    ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, tmpVar, expr));
                    foreach (var storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, storeTo, new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, tmpVar)));
                    }
                }
            }

            return(ast);
        }
Esempio n. 12
0
		/// <summary>
		/// Runs a very simple form of copy propagation.
		/// Copy propagation is used in two cases:
		/// 1) assignments from arguments to local variables
		///    If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg),
		///    then we can replace the variable with the argument.
		/// 2) assignments of address-loading instructions to local variables
		/// </summary>
		public void CopyPropagation()
		{
			foreach (AstBlock block in method.GetSelfAndChildrenRecursive<AstBlock>()) {
				for (int i = 0; i < block.Body.Count; i++) {
					AstVariable v;
					AstExpression copiedExpr;
					if (block.Body[i].Match(AstCode.Stloc, out v, out copiedExpr)
					    && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0
					    && CanPerformCopyPropagation(copiedExpr, v))
					{
						// un-inline the arguments of the ldArg instruction
						var uninlinedArgs = new AstVariable[copiedExpr.Arguments.Count];
						for (int j = 0; j < uninlinedArgs.Length; j++) {
                            uninlinedArgs[j] = new AstGeneratedVariable(v.Name + "_cp_" + j, v.OriginalName);
							block.Body.Insert(i++, new AstExpression(copiedExpr.SourceLocation, AstCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j]));
						}
						
						// perform copy propagation:
						foreach (var expr in method.GetSelfAndChildrenRecursive<AstExpression>()) {
							if (expr.Code == AstCode.Ldloc && expr.Operand == v) {
								expr.Code = copiedExpr.Code;
								expr.Operand = copiedExpr.Operand;
								for (int j = 0; j < uninlinedArgs.Length; j++) {
									expr.Arguments.Add(new AstExpression(copiedExpr.SourceLocation, AstCode.Ldloc, uninlinedArgs[j]));
								}
							}
						}
						
						block.Body.RemoveAt(i);
						if (uninlinedArgs.Length > 0) {
							// if we un-inlined stuff; we need to update the usage counters
							AnalyzeMethod();
						}
						InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
						i -= uninlinedArgs.Length + 1;
					}
				}
			}
		}
Esempio n. 13
0
        /// <summary>
        /// Convert a nullable .GetValueOrDefault()
        /// </summary>
        private static void ConvertGetValueOrDefault(AstExpression node,XTypeReference type, XModule module)
        {
            var defExpr = node.Arguments.Count == 1
                            ? new AstExpression(node.SourceLocation, AstCode.DefaultValue, type).SetType(type)
                            : node.Arguments[1];
            defExpr.ExpectedType = type;

            if (type.IsPrimitive)
            {
                // replace with obj != null ? unbox(obj) : defExpr
                AstExpression compareExpr, valueExpr;

                var loadExpr = node.Arguments[0];
                ConvertLoad(loadExpr);
                loadExpr.InferredType = module.TypeSystem.Object;
                loadExpr.ExpectedType = module.TypeSystem.Object;

                if (loadExpr.Code != AstCode.Ldloc)
                {
                    // TODO: how can we get the backend to remove/combine these variables again?
                    var tmpVar = new AstGeneratedVariable("tmp$", null) { Type = module.TypeSystem.Object };
                    compareExpr = new AstExpression(node.SourceLocation, AstCode.Stloc, tmpVar, loadExpr).SetType(module.TypeSystem.Object);
                    valueExpr = new AstExpression(node.SourceLocation, AstCode.Ldloc, tmpVar).SetType(module.TypeSystem.Object);
                }
                else
                {
                    compareExpr = loadExpr;
                    valueExpr = loadExpr;
                }
                valueExpr = new AstExpression(node.SourceLocation, AstCode.Unbox, type, valueExpr).SetType(type);

                var newNode = new AstExpression(node.SourceLocation, AstCode.Conditional, type, 
                                                compareExpr, valueExpr, defExpr)
                                 .SetType(type);
                node.CopyFrom(newNode);

            }
            else
            {
                // replace with obj ?? defExpr

                var loadExpr = node.Arguments[0];
                ConvertLoad(loadExpr);

                if(!type.IsSame(loadExpr.InferredType))
                {
                    //loadExpr.ExpectedType = type;
                    // todo: how to get the cast inserted automatically?
                    loadExpr = new AstExpression(loadExpr.SourceLocation, AstCode.SimpleCastclass, type, loadExpr);
                }

                var nullCoalescing = new AstExpression(node.SourceLocation, AstCode.NullCoalescing, null, loadExpr, defExpr);
                nullCoalescing.InferredType = type; 
                node.CopyFrom(nullCoalescing);
            }
        }
        private static void AddJumpInstruction(List<AstNode> body, ISourceLocation currentLoc, 
                                               AstGeneratedVariable setInstructionTarget, ref int idx,
                                               ref AstLabel label, int firstValidExpression, AstExpression initExpr, 
                                               ref int labelCount)
        {
            if (label == null)
            {
                label = new AstLabel(AstNode.NoSource, "setInstructionTarget_" + (++labelCount));
                body.Insert(firstValidExpression, label);
                body.Insert(firstValidExpression + 1, initExpr);
                idx += 2;
            }

            var branch = new AstExpression(currentLoc, AstCode.Brtrue, label,
                                new AstExpression(currentLoc, AstCode.Ldloc, setInstructionTarget) { InferredType = setInstructionTarget.Type });
            body.Insert(idx, branch);
            idx += 1;
        }
Esempio n. 15
0
        /// <summary>
        /// Convert the given set of bytecodes to an Ast node list.
        /// Split exception handlers into Ast try/catch blocks.
        /// </summary>
        private List<AstNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs, List<ByteCodeBlock> blockStarts, int nestingLevel, Dictionary<int, ByteCode> offset2ByteCode)
        {
            var ast = new List<AstNode>();

            // Split body in blocks

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.StartPc);
                var tryEnd = ehs.Where(eh => eh.StartPc == tryStart).Max(eh => eh.EndPc);
                var handlers = ehs.Where(eh => (eh.StartPc == tryStart) && (eh.EndPc == tryEnd)).OrderBy(eh => eh.HandlerPc).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while ((tryStartIdx < body.Count) && (body[tryStartIdx].Offset < tryStart))
                    {
                        tryStartIdx++;
                    }
                    if (tryStartIdx > 0)
                    {
                        ast.AddRange(ConvertRangeToAst(body.CutRange(0, tryStartIdx)));
                        // Make sure the block before the try block ends with an unconditional control flow
                        AddUnconditionalBranchToNext(ast, body, 0);
                    }
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet<ExceptionHandler>(
                        ehs.Where(eh => ((tryStart <= eh.StartPc) && (eh.EndPc < tryEnd)) || ((tryStart < eh.StartPc) && (eh.EndPc <= tryEnd))));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while ((tryEndIdx < body.Count) && (body[tryEndIdx].Offset < tryEnd))
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs, blockStarts, nestingLevel + 1, offset2ByteCode);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                    // Make sure the try block ends with an unconditional control flow
                    AddUnconditionalBranchToNext(tryCatchBlock.TryBlock.Body, body, 0);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var iterator in handlers)
                {
                    var eh = iterator;
                    var handler = offset2ByteCode[eh.HandlerPc]; // body.First(x => x.Offset == eh.HandlerPc);
                    var catchType = eh.IsCatchAll ? typeSystem.Object : AsTypeReference(eh.CatchType, XTypeUsageFlags.CatchType);
                    var catchBlock = new AstTryCatchBlock.CatchBlock(handler.SourceLocation, tryCatchBlock)
                    {
                        ExceptionType = catchType,
                        Body = new List<AstNode>()
                    };

                    // Create catch "body" (actually a jump to the handler)
                    // Handle the automatically pushed exception on the stack
                    var ldexception = ldexceptions[eh];
                    catchBlock.Body.Add(new AstExpression(handler.SourceLocation, AstCode.Br, handler.Label(true)));
                    if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                    {
                        // Exception is not used
                        catchBlock.ExceptionVariable = null;
                    }
                    else if (ldexception.StoreTo.Count == 1)
                    {
                        /*var first = catchBlock.Body.FirstOrDefault() as AstExpression;
                        if (first != null &&
                            first.Code == AstCode.Pop &&
                            first.Arguments[0].Code == AstCode.Ldloc &&
                            first.Arguments[0].Operand == ldexception.StoreTo[0])
                        {
                            // The exception is just poped - optimize it all away;
                            catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"));
                            catchBlock.Body.RemoveAt(0);
                        }
                        else*/
                        {
                            catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                        }
                    }
                    else
                    {
                        var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"), null);
                        catchBlock.ExceptionVariable = exTemp;
                        foreach (var storeTo in ldexception.StoreTo)
                        {
                            catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                        }
                    }
                    tryCatchBlock.CatchBlocks.Add(catchBlock);
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertRangeToAst(body));

            return ast;
        }
Esempio n. 16
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List<ByteCode> StackAnalysis()
        {
            // Map from instruction to bytecode.
            var instrToByteCode = new Dictionary<Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List<ByteCode>(codeAttr.Code.Length);
            foreach (var inst in codeAttr.Instructions)
            {
                var first = true;
                foreach (var byteCode in Create(inst, module))
                {
                    if (first)
                    {
                        instrToByteCode[inst] = byteCode;
                        first = false;
                    }
                    body.Add(byteCode);
                }
            }
            // Connect bytecodes to the next
            for (var i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda = new Stack<ByteCode>();
            var localVarsCount = codeAttr.MaxLocals; // methodDef.GetParametersLocalVariableSlots();

            // All bytecodes that are the start of an exception handler.
            var exceptionHandlerStarts = new HashSet<ByteCode>(validExceptionHandlers.Select(eh => instrToByteCode[eh.Handler]));
            var exceptionTryStarts = new DefaultDictionary<ByteCode, List<ExceptionHandler>>(x => new List<ExceptionHandler>());
            foreach (var eh in validExceptionHandlers)
            {
                exceptionTryStarts[instrToByteCode[eh.Start]].Add(eh);
            }

            // Add known states
            var ldExceptionByHandlerPc = new Dictionary<int, ByteCode>();
            foreach (var ex in validExceptionHandlers)
            {
                ByteCode ldexception;
                if (ldExceptionByHandlerPc.TryGetValue(ex.HandlerPc, out ldexception))
                {
                    // Re-use ldexception (that we've created for the same handler PC for another exception handler before)
                }
                else
                {
                    // No handler at handlerPc processed before, do that now
                    var handlerStart = instrToByteCode[ex.Handler];
                    handlerStart.StackBefore = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
                    {
                        // Catch handlers start with the exeption on the stack
                        ldexception = new ByteCode {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1,
                            Offset = handlerStart.Offset,
                            Next = handlerStart,
                            StackBefore = new StackSlot[0],
                            VariablesBefore = handlerStart.VariablesBefore
                        };
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    ldExceptionByHandlerPc[ex.HandlerPc] = ldexception;
                    agenda.Push(handlerStart);
                }
                // Store ldexception by exception handler
                ldexceptions[ex] = ldexception;
            }

            // At the start of the method the stack is empty and all local variables have unknown state
            body[0].StackBefore = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = byteCode.CreateNewStack();

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((LocalVariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUnknownState(localVarsCount);
                }

                // Find all successors
                var branchTargets = FindBranchTargets(byteCode, instrToByteCode, exceptionHandlerStarts);

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    UpdateBranchTarget(byteCode, branchTarget, (branchTargets.Count == 1), newStack, newVariableState, agenda);
                }

                // Apply state to handlers when a branch target is the start of an exception handler
                foreach (var branchTarget in branchTargets.Where(exceptionTryStarts.ContainsKey))
                {
                    // The branch target is the start of a try block.
                    UpdateTryStartBranchTarget(branchTarget, exceptionTryStarts[branchTarget], instrToByteCode, newVariableState, agenda);
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I believe it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Generate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                var argIdx = 0;
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (var pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List<AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possible (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if ((byteCode.StoreTo == null) || (byteCode.StoreTo.Count <= 1)) 
                    continue;

                var locVars = byteCode.StoreTo;
                // For each of the variables, find the location where it is loaded - there should be preciesly one
                var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                // We now know that all the variables have a single load,
                // Let's make sure that they have also a single store - us
                if (loadedBy.All(slot => (slot.Definitions.Length == 1) && (slot.Definitions[0] == byteCode)))
                {
                    // Great - we can reduce everything into single variable
                    var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                    byteCode.StoreTo = new List<AstVariable> { tmpVar };
                    foreach (var bc in body)
                    {
                        for (var i = 0; i < bc.StackBefore.Length; i++)
                        {
                            // Is it one of the variable to be merged?
                            if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                            {
                                // Replace with the new temp variable
                                bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label(true)).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label(true);
                }
                else if (byteCode.Operand is LookupSwitchData)
                {
                    var data = (LookupSwitchData) byteCode.Operand;
                    byteCode.Operand = data.Pairs.Select(x => new AstLabelKeyPair(instrToByteCode[x.Target].Label(true), x.Match)).ToArray();
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            // Replace temporary opcodes
            foreach (var byteCode in body)
            {
                switch (byteCode.Code)
                {
                    case AstCode.Dup_x1:
                    case AstCode.Dup_x2:
                    case AstCode.Dup2:
                    case AstCode.Dup2_x1:
                    case AstCode.Dup2_x2:
                    case AstCode.Swap:
                        byteCode.Code = AstCode.Dup;
                        break;
                    case AstCode.Pop2:
                        byteCode.Code = AstCode.Pop;
                        break;
                }
            }

            return body;
        }
Esempio n. 17
0
        /// <summary>
        /// Create the body of the values() method.
        /// </summary>
        private AstBlock CreateValuesBody()
        {
            var fields = XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)).ToList();

            // Allocate array
            var arrVar = new AstGeneratedVariable("array", "array") { Type = new XArrayType(XType) };
            var createArr = new AstExpression(AstNode.NoSource, AstCode.Stloc, arrVar,
                new AstExpression(AstNode.NoSource, AstCode.Newarr, XType,
                    new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, fields.Count)));
            var ast = AstBlock.CreateOptimizedForTarget(createArr);

            // Initialize array
            var index = 0;
            foreach (var field in fields)
            {
                ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stelem_Any, null,
                    new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar),
                    new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, index),
                    new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field)));
                index++;
            }

            // Return array
            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null,
                new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar)));
            return ast;
        }
Esempio n. 18
0
        /// <summary>
        /// Convert the given set of bytecodes to an Ast node list.
        /// Split exception handlers into Ast try/catch blocks.
        /// </summary>
        private List <AstNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs, List <ByteCodeBlock> blockStarts, int nestingLevel, Dictionary <int, ByteCode> offset2ByteCode)
        {
            var ast = new List <AstNode>();

            // Split body in blocks

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.StartPc);
                var tryEnd   = ehs.Where(eh => eh.StartPc == tryStart).Max(eh => eh.EndPc);
                var handlers = ehs.Where(eh => (eh.StartPc == tryStart) && (eh.EndPc == tryEnd)).OrderBy(eh => eh.HandlerPc).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while ((tryStartIdx < body.Count) && (body[tryStartIdx].Offset < tryStart))
                    {
                        tryStartIdx++;
                    }
                    if (tryStartIdx > 0)
                    {
                        ast.AddRange(ConvertRangeToAst(body.CutRange(0, tryStartIdx)));
                        // Make sure the block before the try block ends with an unconditional control flow
                        AddUnconditionalBranchToNext(ast, body, 0);
                    }
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet <ExceptionHandler>(
                        ehs.Where(eh => ((tryStart <= eh.StartPc) && (eh.EndPc < tryEnd)) || ((tryStart < eh.StartPc) && (eh.EndPc <= tryEnd))));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while ((tryEndIdx < body.Count) && (body[tryEndIdx].Offset < tryEnd))
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs, blockStarts, nestingLevel + 1, offset2ByteCode);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                    // Make sure the try block ends with an unconditional control flow
                    AddUnconditionalBranchToNext(tryCatchBlock.TryBlock.Body, body, 0);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var iterator in handlers)
                {
                    var eh         = iterator;
                    var handler    = offset2ByteCode[eh.HandlerPc]; // body.First(x => x.Offset == eh.HandlerPc);
                    var catchType  = eh.IsCatchAll ? typeSystem.Object : AsTypeReference(eh.CatchType, XTypeUsageFlags.CatchType);
                    var catchBlock = new AstTryCatchBlock.CatchBlock(handler.SourceLocation, tryCatchBlock)
                    {
                        ExceptionType = catchType,
                        Body          = new List <AstNode>()
                    };

                    // Create catch "body" (actually a jump to the handler)
                    // Handle the automatically pushed exception on the stack
                    var ldexception = ldexceptions[eh];
                    catchBlock.Body.Add(new AstExpression(handler.SourceLocation, AstCode.Br, handler.Label(true)));
                    if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                    {
                        // Exception is not used
                        catchBlock.ExceptionVariable = null;
                    }
                    else if (ldexception.StoreTo.Count == 1)
                    {
                        /*var first = catchBlock.Body.FirstOrDefault() as AstExpression;
                         * if (first != null &&
                         *  first.Code == AstCode.Pop &&
                         *  first.Arguments[0].Code == AstCode.Ldloc &&
                         *  first.Arguments[0].Operand == ldexception.StoreTo[0])
                         * {
                         *  // The exception is just poped - optimize it all away;
                         *  catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"));
                         *  catchBlock.Body.RemoveAt(0);
                         * }
                         * else*/
                        {
                            catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                        }
                    }
                    else
                    {
                        var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"), null);
                        catchBlock.ExceptionVariable = exTemp;
                        foreach (var storeTo in ldexception.StoreTo)
                        {
                            catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                        }
                    }
                    tryCatchBlock.CatchBlocks.Add(catchBlock);
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertRangeToAst(body));

            return(ast);
        }
Esempio n. 19
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List <ByteCode> StackAnalysis()
        {
            // Map from instruction to bytecode.
            var instrToByteCode = new Dictionary <Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List <ByteCode>(codeAttr.Code.Length);

            foreach (var inst in codeAttr.Instructions)
            {
                var first = true;
                foreach (var byteCode in Create(inst, module))
                {
                    if (first)
                    {
                        instrToByteCode[inst] = byteCode;
                        first = false;
                    }
                    body.Add(byteCode);
                }
            }
            // Connect bytecodes to the next
            for (var i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda         = new Stack <ByteCode>();
            var localVarsCount = codeAttr.MaxLocals; // methodDef.GetParametersLocalVariableSlots();

            // All bytecodes that are the start of an exception handler.
            var exceptionHandlerStarts = new HashSet <ByteCode>(validExceptionHandlers.Select(eh => instrToByteCode[eh.Handler]));
            var exceptionTryStarts     = new DefaultDictionary <ByteCode, List <ExceptionHandler> >(x => new List <ExceptionHandler>());

            foreach (var eh in validExceptionHandlers)
            {
                exceptionTryStarts[instrToByteCode[eh.Start]].Add(eh);
            }

            // Add known states
            var ldExceptionByHandlerPc = new Dictionary <int, ByteCode>();

            foreach (var ex in validExceptionHandlers)
            {
                ByteCode ldexception;
                if (ldExceptionByHandlerPc.TryGetValue(ex.HandlerPc, out ldexception))
                {
                    // Re-use ldexception (that we've created for the same handler PC for another exception handler before)
                }
                else
                {
                    // No handler at handlerPc processed before, do that now
                    var handlerStart = instrToByteCode[ex.Handler];
                    handlerStart.StackBefore     = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
                    {
                        // Catch handlers start with the exeption on the stack
                        ldexception = new ByteCode {
                            Code            = AstCode.Ldexception,
                            Operand         = ex.CatchType,
                            PopCount        = 0,
                            PushCount       = 1,
                            Offset          = handlerStart.Offset,
                            Next            = handlerStart,
                            StackBefore     = new StackSlot[0],
                            VariablesBefore = handlerStart.VariablesBefore
                        };
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    ldExceptionByHandlerPc[ex.HandlerPc] = ldexception;
                    agenda.Push(handlerStart);
                }
                // Store ldexception by exception handler
                ldexceptions[ex] = ldexception;
            }

            // At the start of the method the stack is empty and all local variables have unknown state
            body[0].StackBefore     = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = byteCode.CreateNewStack();

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((LocalVariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUnknownState(localVarsCount);
                }

                // Find all successors
                var branchTargets = FindBranchTargets(byteCode, instrToByteCode, exceptionHandlerStarts);

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    UpdateBranchTarget(byteCode, branchTarget, (branchTargets.Count == 1), newStack, newVariableState, agenda);
                }

                // Apply state to handlers when a branch target is the start of an exception handler
                foreach (var branchTarget in branchTargets.Where(exceptionTryStarts.ContainsKey))
                {
                    // The branch target is the start of a try block.
                    UpdateTryStartBranchTarget(branchTarget, exceptionTryStarts[branchTarget], instrToByteCode, newVariableState, agenda);
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I believe it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Generate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                var argIdx   = 0;
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (var pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List <AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possible (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if ((byteCode.StoreTo == null) || (byteCode.StoreTo.Count <= 1))
                {
                    continue;
                }

                var locVars = byteCode.StoreTo;
                // For each of the variables, find the location where it is loaded - there should be preciesly one
                var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                // We now know that all the variables have a single load,
                // Let's make sure that they have also a single store - us
                if (loadedBy.All(slot => (slot.Definitions.Length == 1) && (slot.Definitions[0] == byteCode)))
                {
                    // Great - we can reduce everything into single variable
                    var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                    byteCode.StoreTo = new List <AstVariable> {
                        tmpVar
                    };
                    foreach (var bc in body)
                    {
                        for (var i = 0; i < bc.StackBefore.Length; i++)
                        {
                            // Is it one of the variable to be merged?
                            if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                            {
                                // Replace with the new temp variable
                                bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label(true)).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label(true);
                }
                else if (byteCode.Operand is LookupSwitchData)
                {
                    var data = (LookupSwitchData)byteCode.Operand;
                    byteCode.Operand = data.Pairs.Select(x => new AstLabelKeyPair(instrToByteCode[x.Target].Label(true), x.Match)).ToArray();
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            // Replace temporary opcodes
            foreach (var byteCode in body)
            {
                switch (byteCode.Code)
                {
                case AstCode.Dup_x1:
                case AstCode.Dup_x2:
                case AstCode.Dup2:
                case AstCode.Dup2_x1:
                case AstCode.Dup2_x2:
                case AstCode.Swap:
                    byteCode.Code = AstCode.Dup;
                    break;

                case AstCode.Pop2:
                    byteCode.Code = AstCode.Pop;
                    break;
                }
            }

            return(body);
        }
Esempio n. 20
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List <ByteCode> StackAnalysis()
        {
            var instrToByteCode = new Dictionary <Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List <ByteCode>(methodDef.Body.Instructions.Count);
            List <Instruction> prefixes = null;

            foreach (var inst in methodDef.Body.Instructions)
            {
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                    {
                        prefixes = new List <Instruction>(1);
                    }
                    prefixes.Add(inst);
                    continue;
                }
                var code    = (AstCode)inst.OpCode.Code;
                var operand = inst.Operand;
                AstCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
                var byteCode = new ByteCode
                {
                    Offset        = inst.Offset,
                    EndOffset     = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    Code          = code,
                    Operand       = operand,
                    PopCount      = inst.GetPopDelta(methodDef),
                    PushCount     = inst.GetPushDelta(),
                    SequencePoint = SequencePointWrapper.Wrap(inst.SequencePoint)
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset   = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes          = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda   = new Stack <ByteCode>();
            var varCount = methodDef.Body.Variables.Count;

            var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (var ex in methodDef.Body.ExceptionHandlers)
                {
                    var handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore     = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        var ldexception = new ByteCode()
                        {
                            Code      = AstCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex]         = ldexception;
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        var filterStart = instrToByteCode[ex.FilterStart];
                        var ldexception = new ByteCode
                        {
                            Code      = AstCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        // TODO: ldexceptions[ex] = ldexception;
                        filterStart.StackBefore     = new[] { new StackSlot(new[] { ldexception }, null) };
                        filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore     = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUknownState(varCount);
                }

                // Find all successors
                var branchTargets = new List <ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    if (exceptionHandlerStarts.Contains(byteCode.Next))
                    {
                        // Do not fall though down to exception handler
                        // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
                    }
                    else
                    {
                        branchTargets.Add(byteCode.Next);
                    }
                }
                if (byteCode.Operand is Instruction[])
                {
                    foreach (var inst in (Instruction[])byteCode.Operand)
                    {
                        var target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new AstLabel(target.SequencePoint, target.Name);
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    var target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new AstLabel(target.SequencePoint, target.Name);
                    }
                }

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore     = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore     = StackSlot.ModifyStack(newStack, 0, 0, null);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Length != newStack.Length)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Length; i++)
                        {
                            var oldDefs = branchTarget.StackBefore[i].Definitions;
                            var newDefs = oldDefs.Union(newStack[i].Definitions);
                            if (newDefs.Length > oldDefs.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            var oldSlot = branchTarget.VariablesBefore[i];
                            var newSlot = newVariableState[i];
                            if (!oldSlot.UnknownDefinition)
                            {
                                if (newSlot.UnknownDefinition)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldDefs = oldSlot.Definitions;
                                    ByteCode[] newDefs = CollectionExtensions.Union(oldDefs, newSlot.Definitions);
                                    if (newDefs.Length > oldDefs.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I belive it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Genertate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                int argIdx   = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List <AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                        byteCode.StoreTo = new List <AstVariable>()
                        {
                            tmpVar
                        };
                        foreach (var bc in body)
                        {
                            for (int i = 0; i < bc.StackBefore.Length; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels and references to xreferences
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
                else if (byteCode.Operand is FieldReference)
                {
                    byteCode.Operand = XBuilder.AsFieldReference(module, (FieldReference)byteCode.Operand);
                }
                else if (byteCode.Operand is MethodReference)
                {
                    byteCode.Operand = XBuilder.AsMethodReference(module, (MethodReference)byteCode.Operand);
                }
                else if (byteCode.Operand is TypeReference)
                {
                    byteCode.Operand = XBuilder.AsTypeReference(module, (TypeReference)byteCode.Operand);
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return(body);
        }
Esempio n. 21
0
        public static void Convert(AstBlock ast, MethodSource currentMethod, AssemblyCompiler compiler)
        {
            var setInstructionTarget = new AstGeneratedVariable(DebuggerConstants.SetNextInstructionVariableName, null, true)
            {
                Type = compiler.Module.TypeSystem.Bool
            };

            int labelCount = 0;
            var initExpr   = new AstExpression(AstNode.NoSource, AstCode.Stloc, setInstructionTarget,
                                               new AstExpression(AstNode.NoSource, AstCode.Ldnull, null)
                                               .SetType(compiler.Module.TypeSystem.Int));

            int lastBaseCtorCall = -1;

            // can't jump before base-class constructor call.
            if (currentMethod.IsCtor)
            {
                lastBaseCtorCall = FindLastCtorCall(ast);
            }

            ISourceLocation currentLoc = null;

            foreach (var block in ast.GetSelfAndChildrenRecursive <AstBlock>())
            {
                if (block.EntryGoto != null) // only handle simple cases atm.
                {
                    return;
                }

                var      body  = block.Body;
                AstLabel label = null;
                int      firstValidExpression = -1;
                bool     lastExprWasCall      = false;

                var startIdx = lastBaseCtorCall == -1?0:lastBaseCtorCall + 1;
                lastBaseCtorCall = -1;

                for (int idx = startIdx; idx < body.Count; ++idx)
                {
                    var expr = body[idx] as AstExpression;
                    if (expr == null)
                    {
                        continue;
                    }

                    if (expr.SourceLocation == null)
                    {
                        continue;
                    }

                    if (expr.SourceLocation.Equals(currentLoc) && !lastExprWasCall)
                    {
                        continue;
                    }

                    currentLoc = expr.SourceLocation;

                    lastExprWasCall = expr.Code.IsCall();

                    if (firstValidExpression == -1)
                    {
                        firstValidExpression = idx;
                        continue;
                    }

                    AddJumpInstruction(body, currentLoc, setInstructionTarget, ref idx,
                                       ref label, firstValidExpression, initExpr, ref labelCount);
                }

                //if (!lastExprWasRet && firstValidExpression != -1)
                //{
                //    int idx = body.Count;
                //    AddJumpInstruction(body, currentLoc, setInstructionTarget, ref idx,
                //                       ref label, firstValidExpression, initExpr, ref labelCount);
                //}
            }
        }
Esempio n. 22
0
        /// <summary>
        /// Convert the given set of bytecodes into a list of Ast nodes.
        /// </summary>
        private static IEnumerable<AstNode> ConvertRangeToAst(IEnumerable<ByteCode> range)
        {
            var ast = new List<AstNode>();

            // Convert stack-based java bytecode code to Ast tree
            foreach (var byteCode in range)
            {
                var ilRange = new InstructionRange(byteCode.Offset, byteCode.EndOffset);
                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                var expr = new AstExpression(byteCode.SourceLocation, byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);

                // Label for this instruction
                if (byteCode.Label(false) != null)
                {
                    ast.Add(byteCode.Label(false));
                }

                // Reference arguments using temporary variables
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new AstExpression(byteCode.SourceLocation, AstCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    var tmpVar = new AstGeneratedVariable("expr_" + byteCode.Offset.ToString("X2"), byteCode.StoreTo.Select(x => x.OriginalName).FirstOrDefault());
                    ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, tmpVar, expr));
                    foreach (var storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new AstExpression(byteCode.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(byteCode.SourceLocation, AstCode.Ldloc, tmpVar)));
                    }
                }
            }

            return ast;
        }
        public static void Convert(AstBlock ast, MethodSource currentMethod, AssemblyCompiler compiler)
        {
            var setInstructionTarget = new AstGeneratedVariable(DebuggerConstants.SetNextInstructionVariableName, null, true)
            {
                Type = compiler.Module.TypeSystem.Bool
            };

            int labelCount = 0;
            var initExpr = new AstExpression(AstNode.NoSource, AstCode.Stloc, setInstructionTarget,
                                                    new AstExpression(AstNode.NoSource, AstCode.Ldnull, null)
                                                             .SetType(compiler.Module.TypeSystem.Int));

            int lastBaseCtorCall = -1;

            // can't jump before base-class constructor call.
            if (currentMethod.IsCtor)
                lastBaseCtorCall = FindLastCtorCall(ast);

            ISourceLocation currentLoc = null;
            
            foreach (var block in ast.GetSelfAndChildrenRecursive<AstBlock>())
            {
                if (block.EntryGoto != null) // only handle simple cases atm.
                    return;

                var body = block.Body;
                AstLabel label = null;
                int firstValidExpression = -1;
                bool lastExprWasCall = false;

                var startIdx = lastBaseCtorCall == -1?0:lastBaseCtorCall + 1;
                lastBaseCtorCall = -1;

                for (int idx = startIdx; idx < body.Count; ++idx)
                {
                    var expr = body[idx] as AstExpression;
                    if(expr == null)
                        continue;

                    if (expr.SourceLocation == null)
                        continue;

                    if (expr.SourceLocation.Equals(currentLoc) && !lastExprWasCall)
                        continue;

                    currentLoc = expr.SourceLocation;

                    lastExprWasCall = expr.Code.IsCall();

                    if (firstValidExpression == -1)
                    {
                        firstValidExpression = idx;
                        continue;
                    }

                    AddJumpInstruction(body, currentLoc, setInstructionTarget, ref idx, 
                                       ref label, firstValidExpression, initExpr, ref labelCount);
                }

                //if (!lastExprWasRet && firstValidExpression != -1)
                //{
                //    int idx = body.Count;
                //    AddJumpInstruction(body, currentLoc, setInstructionTarget, ref idx,
                //                       ref label, firstValidExpression, initExpr, ref labelCount);
                //}
            }
        }
Esempio n. 24
0
        private static void ConvertUnboxStruct(AstExpression node, XTypeReference resultType, XTypeSystem typeSystem)
        {
            // Structs must never be null. We have to handle structs here, since a newobj 
            // might need generic arguments. These would be difficult to provide at "UnboxFromGeneric",
            // but will be automatically filled in by the GenericInstanceConverter

            // convert to (temp$ = (T)x) != null ? temp$ : default(T)

            // replace any unbox, but keep if otherwise.
            var clone = node.Code == AstCode.Unbox ? new AstExpression(node.Arguments[0]) : new AstExpression(node);

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$", "") { Type = typeSystem.Object };

            // T(x)
            var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, resultType, clone)
                                .SetType(resultType);

            // temporary storage
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, txExpr) { ExpectedType = resultType };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar)
                                .SetType(resultType);

            // default (T)
            var defaultT = new AstExpression(node.SourceLocation, AstCode.DefaultValue, resultType).SetType(resultType);
            var constructor = StructCallConverter.GetDefaultValueCtor(resultType.Resolve());
            StructCallConverter.ConvertDefaultValue(defaultT, constructor);

            // Combine
            var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, resultType,
                                                storeTempVar, loadTempVar, defaultT)
                                .SetType(resultType);

            node.CopyFrom(conditional);
        }
Esempio n. 25
0
        /// <summary>
        /// Convert node with code InstanceOf.
        /// </summary>
        private static void ConvertInstanceOf(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference)node.Operand;
            if (type.IsSystemArray()) // "is System.Array"
            {
                // Call ArrayHelper.IsArray
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray");
                var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, node.Arguments).SetType(typeSystem.Bool);
                node.CopyFrom(isArrayExpr);
                return;
            }

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$$", null) { Type = compiler.Module.TypeSystem.Object };
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = compiler.Module.TypeSystem.Object };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);

            if (type.IsSystemCollectionsIEnumerable() ||
                type.IsSystemCollectionsICollection() ||
                type.IsSystemCollectionsIList())
            {
                // Call "(is x) || IsArray(x)"
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isArray = arrayHelper.Methods.First(x => x.Name == "IsArray" && x.Parameters.Count == 1);

                // "is" 
                var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf);

                // Call IsArray
                var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, loadTempVar).SetType(typeSystem.Bool);

                // Combined
                var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isArrayExpr).SetType(typeSystem.Bool);
                node.CopyFrom(combined);
                return;
            }

            if (type.IsSystemCollectionsIEnumerableT() ||
                type.IsSystemCollectionsICollectionT() ||
                type.IsSystemCollectionsIListT())
            {
                // TODO: implement InstanceOf with type check for array types.
                // (is that even possible here?)
            }

            if (type.IsSystemIFormattable())
            {
                // Call "(is x) || IsFormattable(x)"
                var formattable = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isFormattable = formattable.Methods.First(x => x.Name == "IsVirtualFormattable");

                // "is" 
                var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf);

                // Call IsFormattable
                var isFormattableExpr = new AstExpression(node.SourceLocation, AstCode.Call, isFormattable, loadTempVar).SetType(typeSystem.Bool);

                // Combined
                var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isFormattableExpr).SetType(typeSystem.Bool);
                node.CopyFrom(combined);
                return;
            }

            // Normal instanceof
            node.Code = AstCode.SimpleInstanceOf;            
        }
Esempio n. 26
0
        /// <summary>
        /// Convert node with code Cast.
        /// </summary>
        private static void ConvertCastclass(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference) node.Operand;
            if (type.IsSystemArray())
            {
                // Call cast method
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var castToArray = arrayHelper.Methods.First(x => x.Name == "CastToArray");
                var castToArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, node.Arguments).SetType(type);
                node.CopyFrom(castToArrayExpr);
                return;
            }

            string castMethod = null;
            if (type.IsSystemCollectionsIEnumerable() ||
                type.IsSystemCollectionsIEnumerableT())
            {
                castMethod = "CastToEnumerable";
            }
            else if (type.IsSystemCollectionsICollection() ||
                     type.IsSystemCollectionsICollectionT())
            {
                castMethod = "CastToCollection";
            }
            else if (type.IsSystemCollectionsIList() ||
                     type.IsSystemCollectionsIListT())
            {
                castMethod = "CastToList";
            }
            else if (type.IsSystemIFormattable())
            {
                castMethod = "CastToFormattable";
            }

            if (castMethod != null)
            {
                // make sure we don't evaluate the expression twice.
                var tempVar = new AstGeneratedVariable("temp$$", null) { Type = compiler.Module.TypeSystem.Object };
                var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType =compiler.Module.TypeSystem.Object} ;
                var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);

                // Call cast method
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var castToArray = arrayHelper.Methods.First(x => x.Name == castMethod);

                // Call "(x instanceof T) ? (T)x : asMethod(x)"

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // CastX(x)
                var castXExpr = new AstExpression(node.SourceLocation, AstCode.Call, castToArray, loadTempVar).SetType(typeSystem.Object);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, castXExpr).SetType(type);

                node.CopyFrom(conditional);
                return;
            }

            // Normal castclass
            node.Code = AstCode.SimpleCastclass;
        }
Esempio n. 27
0
        private static void FailsafeInterlockedUsingLocking(XFieldReference field, AstExpression expr, 
                                                            AstExpression targetExpr, AstBlock block, int idx, 
                                                            AssemblyCompiler compiler, MethodSource currentMethod)
        {
            if (currentMethod.IsDotNet && !currentMethod.ILMethod.DeclaringType.HasSuppressMessageAttribute("InterlockedFallback"))
            {
                bool isStatic = field != null && field.Resolve().IsStatic;

                DLog.Warning(DContext.CompilerCodeGenerator,
                    "Emulating Interlocked call using failsafe locking mechanism in {0}{1}. Consider using AtomicXXX classes instead. You can suppress this message with an [SuppressMessage(\"dot42\", \"InterlockedFallback\")] attribute on the class.",
                    currentMethod.Method.FullName, !isStatic ? "" : " because a static field is referenced");
            }

            // replace with:
            //    Monitor.Enter();
            //    try { <original expression> } finally { Monitor.Exit(); } 
            //   
            // note that the lock is larger than it has to be, since it also sourrounds
            // the parameter evaluation.
            // It must sourround the storing and loading of the reference parameters though.
            var typeSystem = compiler.Module.TypeSystem;

            var monitorType = compiler.GetDot42InternalType("System.Threading", "Monitor");
            var enterMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Enter");
            var exitMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Exit");

            AstExpression loadLockTarget = null;
            
            if (field != null)
            {
                if (field.Resolve().IsStatic)
                {
                    // lock on the field's class typedef.
                    // but always the element type, not on a generic instance (until Dot42 implements proper generic static field handling)
                    loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, field.DeclaringType.GetElementType())
                        .SetType(typeSystem.Type);
                }
                else
                {
                    // lock on the fields object
                    loadLockTarget = targetExpr.Arguments[0];
                }
            }

            if (loadLockTarget == null)
            {
                // something went wrong. use a global lock.
                DLog.Warning(DContext.CompilerCodeGenerator, "unable to infer target of Interlocked call. using global lock.");
                var interlockedType = compiler.GetDot42InternalType("System.Threading", "Interlocked");
                loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, interlockedType)
                                        .SetType(typeSystem.Type);
            }

            var lockVar = new AstGeneratedVariable("lockTarget$", "") {Type = typeSystem.Object};
            var storeLockVar = new AstExpression(expr.SourceLocation, AstCode.Stloc, lockVar, loadLockTarget);
            var loadLockVar = new AstExpression(expr.SourceLocation, AstCode.Ldloc, lockVar);
            var enterCall = new AstExpression(expr.SourceLocation, AstCode.Call, enterMethod, storeLockVar);

            var replacementBlock = new AstBlock(expr.SourceLocation);
            replacementBlock.Body.Add(enterCall);

            var tryCatch = new AstTryCatchBlock(expr.SourceLocation)
            {
                TryBlock = new AstBlock(expr.SourceLocation, expr),
                FinallyBlock = new AstBlock(expr.SourceLocation,
                    new AstExpression(block.SourceLocation, AstCode.Call, exitMethod, loadLockVar))
            };

            replacementBlock.Body.Add(tryCatch);

            if (block.EntryGoto == expr)
                block.EntryGoto = enterCall;

            block.Body[idx] = replacementBlock;
        }
Esempio n. 28
0
        List <AstNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            var ast = new List <AstNode>();

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.TryStart.Offset);
                var tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart)
                    {
                        tryStartIdx++;
                    }
                    ast.AddRange(ConvertToAst(CollectionExtensions.CutRange(body, 0, tryStartIdx)));
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet <ExceptionHandler>(
                        ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd)
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(CollectionExtensions.CutRange(body, 0, tryEndIdx), nestedEHs);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var eh in handlers)
                {
                    var handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
                    var startIdx         = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset)
                    {
                        startIdx++;
                    }
                    var endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset)
                    {
                        endIdx++;
                    }
                    var nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    var handlerAst = ConvertToAst(CollectionExtensions.CutRange(body, startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        var catchType  = eh.CatchType.IsSystemObject() ? module.TypeSystem.Exception : XBuilder.AsTypeReference(module, eh.CatchType);
                        var catchBlock = new AstTryCatchBlock.CatchBlock(handlerAst.Select(x => x.SourceLocation).FirstOrDefault(), tryCatchBlock)
                        {
                            ExceptionType = catchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        var ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                        {
                            // Exception is not used
                            catchBlock.ExceptionVariable = null;
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            var first = catchBlock.Body[0] as AstExpression;
                            if (first != null &&
                                first.Code == AstCode.Pop &&
                                first.Arguments[0].Code == AstCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
                                {
                                    catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                                }
                                else
                                {
                                    catchBlock.ExceptionVariable = null;
                                }
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (var storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new AstBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new AstBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
Esempio n. 29
0
        /// <summary>
        /// Convert node with code IsInst.
        /// </summary>
        private static void ConvertIsInst(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference)node.Operand;
            if (type.IsSystemArray())
            {
                // Call ArrayHelper.AsArray
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var asArray = arrayHelper.Methods.First(x => x.Name == "AsArray");
                var asArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, node.Arguments).SetType(typeSystem.Bool);
                node.CopyFrom(asArrayExpr);
                return;
            }

            string asMethod = GetCollectionConvertMethodName(type);

            if (asMethod != null)
            {
                asMethod = "As" + asMethod;
            }
            else if (type.IsSystemIFormattable())
            {
                asMethod = "AsFormattable";
            }

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$$", null) { Type = compiler.Module.TypeSystem.Object };
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = compiler.Module.TypeSystem.Object };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);


            if (asMethod != null)
            {
                // Call "(x instanceof T) ? (T)x : asMethod(x)"
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var asArray = arrayHelper.Methods.First(x => x.Name == asMethod);

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // AsX(x)
                var asXExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, loadTempVar).SetType(typeSystem.Object);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, asXExpr).SetType(type);

                node.CopyFrom(conditional);
                return;
            }

            // Normal "as": Convert to (x instanceof T) ? (T)x : null
            if(!type.IsPrimitive)
            {
                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // null
                var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(typeSystem.Object);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, nullExpr).SetType(type);

                node.CopyFrom(conditional);
                return;                
            }
            else
            {
                // treat as "x is T"
                if(!node.ExpectedType.IsBoolean())
                    throw new NotImplementedException(); // can this happen?

                node.Code = AstCode.SimpleInstanceOf;
            }
        }
Esempio n. 30
0
        private static void FailsafeInterlockedUsingLocking(XFieldReference field, AstExpression expr,
                                                            AstExpression targetExpr, AstBlock block, int idx,
                                                            AssemblyCompiler compiler, MethodSource currentMethod)
        {
            if (currentMethod.IsDotNet && !currentMethod.ILMethod.DeclaringType.HasSuppressMessageAttribute("InterlockedFallback"))
            {
                bool isStatic = field != null && field.Resolve().IsStatic;

                DLog.Warning(DContext.CompilerCodeGenerator,
                             "Emulating Interlocked call using failsafe locking mechanism in {0}{1}. Consider using AtomicXXX classes instead. You can suppress this message with an [SuppressMessage(\"dot42\", \"InterlockedFallback\")] attribute on the class.",
                             currentMethod.Method.FullName, !isStatic ? "" : " because a static field is referenced");
            }

            // replace with:
            //    Monitor.Enter();
            //    try { <original expression> } finally { Monitor.Exit(); }
            //
            // note that the lock is larger than it has to be, since it also sourrounds
            // the parameter evaluation.
            // It must sourround the storing and loading of the reference parameters though.
            var typeSystem = compiler.Module.TypeSystem;

            var monitorType = compiler.GetDot42InternalType("System.Threading", "Monitor");
            var enterMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Enter");
            var exitMethod  = monitorType.Resolve().Methods.Single(p => p.Name == "Exit");

            AstExpression loadLockTarget = null;

            if (field != null)
            {
                if (field.Resolve().IsStatic)
                {
                    // lock on the field's class typedef.
                    // but always the element type, not on a generic instance (until Dot42 implements proper generic static field handling)
                    loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, field.DeclaringType.GetElementType())
                                     .SetType(typeSystem.Type);
                }
                else
                {
                    // lock on the fields object
                    loadLockTarget = targetExpr.Arguments[0];
                }
            }

            if (loadLockTarget == null)
            {
                // something went wrong. use a global lock.
                DLog.Warning(DContext.CompilerCodeGenerator, "unable to infer target of Interlocked call. using global lock.");
                var interlockedType = compiler.GetDot42InternalType("System.Threading", "Interlocked");
                loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, interlockedType)
                                 .SetType(typeSystem.Type);
            }

            var lockVar = new AstGeneratedVariable("lockTarget$", "")
            {
                Type = typeSystem.Object
            };
            var storeLockVar = new AstExpression(expr.SourceLocation, AstCode.Stloc, lockVar, loadLockTarget);
            var loadLockVar  = new AstExpression(expr.SourceLocation, AstCode.Ldloc, lockVar);
            var enterCall    = new AstExpression(expr.SourceLocation, AstCode.Call, enterMethod, storeLockVar);

            var replacementBlock = new AstBlock(expr.SourceLocation);

            replacementBlock.Body.Add(enterCall);

            var tryCatch = new AstTryCatchBlock(expr.SourceLocation)
            {
                TryBlock     = new AstBlock(expr.SourceLocation, expr),
                FinallyBlock = new AstBlock(expr.SourceLocation,
                                            new AstExpression(block.SourceLocation, AstCode.Call, exitMethod, loadLockVar))
            };

            replacementBlock.Body.Add(tryCatch);

            if (block.EntryGoto == expr)
            {
                block.EntryGoto = enterCall;
            }

            block.Body[idx] = replacementBlock;
        }
Esempio n. 31
0
        private static void ConvertAsNativeIFormattable(AstExpression node, XTypeSystem typeSystem)
        {
            var method = (XMethodReference)node.Operand;
            var type = method.ReturnType;

            if (method.Name == "AsNativeIFormattable"
                && method.DeclaringType.Name == InternalConstants.CompilerHelperName
                && type.FullName == "System.IFormattable")
            {
                // make sure we don't evaluate the expression twice.
                var tempVar = new AstGeneratedVariable("temp$$", null) { Type = typeSystem.Object };
                var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0]) { ExpectedType = typeSystem.Object };
                var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(typeSystem.Object);

                // Convert to "(x instanceof T) ? (T)x : null"

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);
                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);
                // null
                var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(type);
                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type,
                                        instanceofExpr, txExpr, nullExpr).SetType(type);
                node.CopyFrom(conditional);
            }
        }
Esempio n. 32
0
        /// <summary>
        /// Convert node with code IsInst.
        /// </summary>
        private static void ConvertIsInst(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference)node.Operand;

            if (type.IsSystemArray())
            {
                // Call ArrayHelper.AsArray
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var asArray     = arrayHelper.Methods.First(x => x.Name == "AsArray");
                var asArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, node.Arguments).SetType(typeSystem.Bool);
                node.CopyFrom(asArrayExpr);
                return;
            }

            string asMethod = GetCollectionConvertMethodName(type);

            if (asMethod != null)
            {
                asMethod = "As" + asMethod;
            }
            else if (type.IsSystemIFormattable())
            {
                asMethod = "AsFormattable";
            }

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$$", null)
            {
                Type = compiler.Module.TypeSystem.Object
            };
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0])
            {
                ExpectedType = compiler.Module.TypeSystem.Object
            };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);


            if (asMethod != null)
            {
                // Call "(x instanceof T) ? (T)x : asMethod(x)"
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var asArray     = arrayHelper.Methods.First(x => x.Name == asMethod);

                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // AsX(x)
                var asXExpr = new AstExpression(node.SourceLocation, AstCode.Call, asArray, loadTempVar).SetType(typeSystem.Object);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, asXExpr).SetType(type);

                node.CopyFrom(conditional);
                return;
            }

            // Normal "as": Convert to (x instanceof T) ? (T)x : null
            if (!type.IsPrimitive)
            {
                // "instanceof x"
                var instanceofExpr = new AstExpression(node.SourceLocation, AstCode.SimpleInstanceOf, type, storeTempVar).SetType(typeSystem.Bool);

                // T(x)
                var txExpr = new AstExpression(node.SourceLocation, AstCode.SimpleCastclass, type, loadTempVar).SetType(type);

                // null
                var nullExpr = new AstExpression(node.SourceLocation, AstCode.Ldnull, null).SetType(typeSystem.Object);

                // Combine
                var conditional = new AstExpression(node.SourceLocation, AstCode.Conditional, type, instanceofExpr, txExpr, nullExpr).SetType(type);

                node.CopyFrom(conditional);
                return;
            }
            else
            {
                // treat as "x is T"
                if (!node.ExpectedType.IsBoolean())
                {
                    throw new NotImplementedException(); // can this happen?
                }
                node.Code = AstCode.SimpleInstanceOf;
            }
        }
Esempio n. 33
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List<ByteCode> StackAnalysis()
        {
            var instrToByteCode = new Dictionary<Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List<ByteCode>(methodDef.Body.Instructions.Count);
            List<Instruction> prefixes = null;
            foreach (var inst in methodDef.Body.Instructions)
            {
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                        prefixes = new List<Instruction>(1);
                    prefixes.Add(inst);
                    continue;
                }
                var code = (AstCode)inst.OpCode.Code;
                var operand = inst.Operand;
                AstCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
                var byteCode = new ByteCode
                {
                    Offset = inst.Offset,
                    EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    Code = code,
                    Operand = operand,
                    PopCount = inst.GetPopDelta(methodDef),
                    PushCount = inst.GetPushDelta(),
                    SequencePoint = SequencePointWrapper.Wrap(inst.SequencePoint)
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda = new Stack<ByteCode>();
            var varCount = methodDef.Body.Variables.Count;

            var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (var ex in methodDef.Body.ExceptionHandlers)
                {
                    var handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        var ldexception = new ByteCode()
                        {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex] = ldexception;
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        var filterStart = instrToByteCode[ex.FilterStart];
                        var ldexception = new ByteCode
                        {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1
                        };
                        // TODO: ldexceptions[ex] = ldexception;
                        filterStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                        filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUknownState(varCount);
                }

                // Find all successors
                var branchTargets = new List<ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    if (exceptionHandlerStarts.Contains(byteCode.Next))
                    {
                        // Do not fall though down to exception handler
                        // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
                    }
                    else
                    {
                        branchTargets.Add(byteCode.Next);
                    }
                }
                if (byteCode.Operand is Instruction[])
                {
                    foreach (var inst in (Instruction[])byteCode.Operand)
                    {
                        var target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new AstLabel(target.SequencePoint, target.Name);
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    var target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new AstLabel(target.SequencePoint, target.Name);
                    }
                }

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Length != newStack.Length)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Length; i++)
                        {
                            var oldDefs = branchTarget.StackBefore[i].Definitions;
                            var newDefs = oldDefs.Union(newStack[i].Definitions);
                            if (newDefs.Length > oldDefs.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            var oldSlot = branchTarget.VariablesBefore[i];
                            var newSlot = newVariableState[i];
                            if (!oldSlot.UnknownDefinition)
                            {
                                if (newSlot.UnknownDefinition)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldDefs = oldSlot.Definitions;
                                    ByteCode[] newDefs = CollectionExtensions.Union(oldDefs, newSlot.Definitions);
                                    if (newDefs.Length > oldDefs.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I belive it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Genertate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                int argIdx = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List<AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                        byteCode.StoreTo = new List<AstVariable>() { tmpVar };
                        foreach (var bc in body)
                        {
                            for (int i = 0; i < bc.StackBefore.Length; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels and references to xreferences
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
                else if (byteCode.Operand is FieldReference)
                {
                    byteCode.Operand = XBuilder.AsFieldReference(module, (FieldReference)byteCode.Operand);
                }
                else if (byteCode.Operand is MethodReference)
                {
                    byteCode.Operand = XBuilder.AsMethodReference(module, (MethodReference)byteCode.Operand);
                }
                else if (byteCode.Operand is TypeReference)
                {
                    byteCode.Operand = XBuilder.AsTypeReference(module, (TypeReference)byteCode.Operand);
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return body;
        }
Esempio n. 34
0
        /// <summary>
        /// Convert node with code InstanceOf.
        /// </summary>
        private static void ConvertInstanceOf(AssemblyCompiler compiler, AstExpression node, XTypeSystem typeSystem)
        {
            var type = (XTypeReference)node.Operand;

            if (type.IsSystemArray()) // "is System.Array"
            {
                // Call ArrayHelper.IsArray
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isArray     = arrayHelper.Methods.First(x => x.Name == "IsArray");
                var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, node.Arguments).SetType(typeSystem.Bool);
                node.CopyFrom(isArrayExpr);
                return;
            }

            // make sure we don't evaluate the expression twice.
            var tempVar = new AstGeneratedVariable("temp$$", null)
            {
                Type = compiler.Module.TypeSystem.Object
            };
            var storeTempVar = new AstExpression(node.SourceLocation, AstCode.Stloc, tempVar, node.Arguments[0])
            {
                ExpectedType = compiler.Module.TypeSystem.Object
            };
            var loadTempVar = new AstExpression(node.SourceLocation, AstCode.Ldloc, tempVar).SetType(compiler.Module.TypeSystem.Object);

            if (type.IsSystemCollectionsIEnumerable() ||
                type.IsSystemCollectionsICollection() ||
                type.IsSystemCollectionsIList())
            {
                // Call "(is x) || IsArray(x)"
                var arrayHelper = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isArray     = arrayHelper.Methods.First(x => x.Name == "IsArray" && x.Parameters.Count == 1);

                // "is"
                var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf);

                // Call IsArray
                var isArrayExpr = new AstExpression(node.SourceLocation, AstCode.Call, isArray, loadTempVar).SetType(typeSystem.Bool);

                // Combined
                var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isArrayExpr).SetType(typeSystem.Bool);
                node.CopyFrom(combined);
                return;
            }

            if (type.IsSystemCollectionsIEnumerableT() ||
                type.IsSystemCollectionsICollectionT() ||
                type.IsSystemCollectionsIListT())
            {
                // TODO: implement InstanceOf with type check for array types.
                // (is that even possible here?)
            }

            if (type.IsSystemIFormattable())
            {
                // Call "(is x) || IsFormattable(x)"
                var formattable   = compiler.GetDot42InternalType(InternalConstants.CompilerHelperName).Resolve();
                var isFormattable = formattable.Methods.First(x => x.Name == "IsVirtualFormattable");

                // "is"
                var isExpr = new AstExpression(node).SetArguments(storeTempVar).SetCode(AstCode.SimpleInstanceOf);

                // Call IsFormattable
                var isFormattableExpr = new AstExpression(node.SourceLocation, AstCode.Call, isFormattable, loadTempVar).SetType(typeSystem.Bool);

                // Combined
                var combined = new AstExpression(node.SourceLocation, AstCode.Or, null, isExpr, isFormattableExpr).SetType(typeSystem.Bool);
                node.CopyFrom(combined);
                return;
            }

            // Normal instanceof
            node.Code = AstCode.SimpleInstanceOf;
        }
Esempio n. 35
0
        private List<AstNode> ConvertToAst(IEnumerable<ByteCode> body)
        {
            var ast = new List<AstNode>();

            // Convert stack-based IL code to ILAst tree
            foreach (var byteCode in body)
            {
                var ilRange = new InstructionRange(byteCode.Offset, byteCode.EndOffset);

                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                var expr = new AstExpression(byteCode.SequencePoint, byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);
                if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0)
                {
                    var prefixes = new AstExpressionPrefix[byteCode.Prefixes.Length];
                    for (var i = 0; i < prefixes.Length; i++)
                    {
                        var operand = byteCode.Prefixes[i].Operand;
                        if (operand is FieldReference)
                        {
                            operand = XBuilder.AsFieldReference(module, (FieldReference) operand);
                        }
                        else if (operand is MethodReference)
                        {
                            operand = XBuilder.AsMethodReference(module, (MethodReference)operand);
                        }
                        else if (operand is TypeReference)
                        {
                            operand = XBuilder.AsTypeReference(module, (TypeReference) operand);
                        }
                        prefixes[i] = new AstExpressionPrefix((AstCode)byteCode.Prefixes[i].OpCode.Code, operand);
                    }
                    expr.Prefixes = prefixes;
                }

                // Label for this instruction
                if (byteCode.Label != null)
                {
                    ast.Add(byteCode.Label);
                }

                // Reference arguments using temporary variables
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    var tmpVar = new AstGeneratedVariable("expr_" + byteCode.Offset.ToString("X2"), byteCode.StoreTo.Select(x => x.OriginalName).FirstOrDefault());
                    ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, tmpVar, expr));
                    foreach (var storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new AstExpression(byteCode.SequencePoint, AstCode.Stloc, storeTo, new AstExpression(byteCode.SequencePoint, AstCode.Ldloc, tmpVar)));
                    }
                }
            }

            return ast;
        }
Esempio n. 36
0
        /// <summary>
        /// Create the body of the class ctor.
        /// </summary>
        private AstBlock CreateClassCtorBody(bool isWide, XFieldDefinition enumInfoField, XFieldDefinition defaultField, XMethodReference enumInfoCtor, XTypeReference valueType, XTypeSystem typeSystem)
        {
            var internalEnumType = Compiler.GetDot42InternalType("Enum");
            var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo");
            var valueToFieldMap = new Dictionary<object, XFieldDefinition>();
            var ldc = isWide ? AstCode.Ldc_I8 : AstCode.Ldc_I4;

            var nameVar     = new AstGeneratedVariable("enumName", null) {Type = typeSystem.String};
            var enumInfoVar = new AstGeneratedVariable("enumInfo", null) {Type = internalEnumInfoType};
            var valVar      = new AstGeneratedVariable("val", null) { Type = enumInfoField.FieldType };

            var ast = AstBlock.CreateOptimizedForTarget(
                // Instantiate enum info field
                new AstExpression(AstNode.NoSource, AstCode.Stsfld, enumInfoField,
                    new AstExpression(AstNode.NoSource, AstCode.Stloc, enumInfoVar,
                        new AstExpression(AstNode.NoSource, AstCode.Newobj, enumInfoCtor))));

            // Instantiate values for each field
            var ordinal = 0;
            foreach (var field in XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)))
            {
                // Find dex field
                object value;
                if (!field.TryGetEnumValue(out value))
                    throw new CompilerException(string.Format("Cannot get enum value from field {0}", field.FullName));
                value = isWide ? (object)XConvert.ToLong(value) : (object)XConvert.ToInt(value);
                XFieldDefinition existingField;
                AstExpression valueExpr;
                if (valueToFieldMap.TryGetValue(value, out existingField))
                {
                    // Re-use instance of existing field
                    valueExpr = new AstExpression(AstNode.NoSource, AstCode.Ldsfld, existingField);
                }
                else
                {
                    // Record
                    valueToFieldMap[value] = field;

                    // Call ctor
                    valueExpr = new AstExpression(AstNode.NoSource, AstCode.Newobj, ctor,
                        new AstExpression(AstNode.NoSource, AstCode.Stloc, nameVar,
                            new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name)),
                        new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, ordinal),
                        new AstExpression(AstNode.NoSource, AstCode.Stloc, valVar,
                            new AstExpression(AstNode.NoSource, ldc, value)));
                }

                // Initialize static field
                var storeExpression = new AstExpression(AstNode.NoSource, AstCode.Stsfld, field, valueExpr);

                // Add to info
                var addMethod = new XMethodReference.Simple("Add", true, typeSystem.Void, internalEnumInfoType,
                    XParameter.Create("value", valueType),
                    XParameter.Create("name", typeSystem.String),
                    XParameter.Create("instance", internalEnumType));
                ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Call, addMethod,
                    new AstExpression(AstNode.NoSource, AstCode.Ldloc, enumInfoVar),
                    new AstExpression(AstNode.NoSource, AstCode.Ldloc, valVar),
                    new AstExpression(AstNode.NoSource, AstCode.Ldloc, nameVar),
                    storeExpression));

                // Increment ordinal
                ordinal++;
            }

            // Initialize default field
            var getValueMethod = new XMethodReference.Simple("GetValue", true, internalEnumType, internalEnumInfoType,
                                                             XParameter.Create("value", valueType));
            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stsfld, defaultField,
                new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, XType,
                    new AstExpression(AstNode.NoSource, AstCode.Call, getValueMethod,
                        new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField),
                        new AstExpression(AstNode.NoSource, ldc, 0)))));

            // Return
            ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null));
            return ast;
        }