Exemple #1
0
        /// <summary>
        /// Writes the CIL stream out to the MethodBuilder used to create this Emit.
        ///
        /// Validation that cannot be run until a method is finished is run, and various instructions
        /// are re-written to choose "optimal" forms (Br may become Br_S, for example).
        ///
        /// Once this method is called the Emit may no longer be modified.
        ///
        /// Returns a MethodBuilder, which can be used to define overrides or for further inspection.
        ///
        /// `instructions` will be set to a representation of the instructions making up the returned method.
        /// Note that this string is typically *not* enough to regenerate the method, it is available for
        /// debugging purposes only.  Consumers may find it useful to log the instruction stream in case
        /// the returned method fails validation (indicative of a bug in Sigil) or
        /// behaves unexpectedly (indicative of a logic bug in the consumer code).
        /// </summary>
        public MethodBuilder CreateMethod(out string instructions, OptimizationOptions optimizationOptions = OptimizationOptions.All)
        {
            if (MtdBuilder == null)
            {
                throw new InvalidOperationException("Emit was not created to build a method, thus CreateMethod cannot be called");
            }

            if (MethodBuilt)
            {
                instructions = null;
                return(MtdBuilder);
            }

            Seal(optimizationOptions);

            MethodBuilt = true;

            var il = MtdBuilder.GetILGenerator();

            instructions = IL.UnBuffer(il);

            AutoNamer.Release(this);

            return(MtdBuilder);
        }
Exemple #2
0
        /// <summary>
        /// Creates a new Emit, optionally using the provided name and owner for the inner DynamicMethod.
        ///
        /// If name is not defined, a sane default is generated.
        ///
        /// If owner is not defined, a module with the same trust as the executing assembly is used instead.
        ///
        /// If doVerify is false (default is true) Sigil will *not* throw an exception on invalid IL.  This is faster, but the benefits
        /// of Sigil are reduced to "a nicer ILGenerator interface".
        ///
        /// If strictBranchValidation is true (default is false) Sigil will enforce "Backward branch constraints" which are *technically* required
        /// for valid CIL, but in practice often ignored.  The most common case to set this option is if you are generating types to write to disk.
        /// </summary>
        public static Emit <DelegateType> NewDynamicMethod(Type owner, string name = null, bool doVerify = true, bool strictBranchVerification = false)
        {
            if (owner == null)
            {
                return(NewDynamicMethod(name: name, module: null));
            }

            name = name ?? AutoNamer.Next("_DynamicMethod");

            ValidateNewParameters <DelegateType>();

            var delType = typeof(DelegateType);

            var invoke         = delType.GetMethod("Invoke");
            var returnType     = invoke.ReturnType;
            var parameterTypes = ((LinqArray <ParameterInfo>)invoke.GetParameters()).Select(s => s.ParameterType).ToArray();

            var dynMethod = new DynamicMethod(name, returnType, parameterTypes, owner, skipVisibility: true);

            var ret = new Emit <DelegateType>(dynMethod.CallingConvention, returnType, parameterTypes, AllowsUnverifiableCode(owner.Module), doVerify, strictBranchVerification);

            ret.DynMethod = dynMethod;

            return(ret);
        }
Exemple #3
0
        /// <summary>
        /// Writes the CIL stream out to the ConstructorBuilder used to create this Emit.
        ///
        /// Validation that cannot be run until a method is finished is run, and various instructions
        /// are re-written to choose "optimal" forms (Br may become Br_S, for example).
        ///
        /// Once this method is called the Emit may no longer be modified.
        ///
        /// Returns a ConstructorBuilder, which can be used to define overrides or for further inspection.
        ///
        /// `instructions` will be set to a representation of the instructions making up the returned constructor.
        /// Note that this string is typically *not* enough to regenerate the constructor, it is available for
        /// debugging purposes only.  Consumers may find it useful to log the instruction stream in case
        /// the returned constructor fails validation (indicative of a bug in Sigil) or
        /// behaves unexpectedly (indicative of a logic bug in the consumer code).
        /// </summary>
        public ConstructorBuilder CreateConstructor(out string instructions, OptimizationOptions optimizationOptions = OptimizationOptions.All)
        {
            if (ConstrBuilder == null)
            {
                throw new InvalidOperationException("Emit was not created to build a constructor, thus CreateConstructor cannot be called");
            }

            if (ConstructorBuilt)
            {
                instructions = null;
                return(ConstrBuilder);
            }

            ConstructorBuilt = true;

            Seal(optimizationOptions);

            var il = ConstrBuilder.GetILGenerator();

            instructions = IL.UnBuffer(il);

            AutoNamer.Release(this);

            return(ConstrBuilder);
        }
Exemple #4
0
        /// <summary>
        /// Ends the given finally block.
        /// </summary>
        public Emit <DelegateType> EndFinallyBlock(FinallyBlock forFinally)
        {
            if (forFinally == null)
            {
                throw new ArgumentNullException("forFinally");
            }

            if (((IOwned)forFinally).Owner != this)
            {
                FailOwnership(forFinally);
            }

            var finallyBlock = FinallyBlocks[forFinally];

            if (finallyBlock.Item2 != -1)
            {
                throw new InvalidOperationException("EndFinallyBlock expects an unclosed finally block, but " + forFinally + " is already closed");
            }

            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            UpdateState(Wrap(new[] { new StackTransition(0) }, "EndFinallyBlock"));

            IL.EndFinallyBlock();

            FinallyBlocks[forFinally] = SigilTuple.Create(finallyBlock.Item1, IL.Index);

            return(this);
        }
Exemple #5
0
        /// <summary>
        /// Creates a new Emit, optionally using the provided name and owner for the inner DynamicMethod.
        ///
        /// If name is not defined, a sane default is generated.
        ///
        /// If owner is not defined, a module with the same trust as the executing assembly is used instead.
        ///
        /// If doVerify is false (default is true) Sigil will *not* throw an exception on invalid IL.  This is faster, but the benefits
        /// of Sigil are reduced to "a nicer ILGenerator interface".
        ///
        /// If strictBranchValidation is true (default is false) Sigil will enforce "Backward branch constraints" which are *technically* required
        /// for valid CIL, but in practice often ignored.  The most common case to set this option is if you are generating types to write to disk.
        /// </summary>
        public static Emit <DelegateType> NewDynamicMethod(Type owner, string name = null, bool doVerify = true, bool strictBranchVerification = false)
        {
            if (owner == null)
            {
                return(NewDynamicMethod(name: name, module: null));
            }

            name = name ?? AutoNamer.Next("_DynamicMethod");

            ValidateNewParameters <DelegateType>();

            var delType = typeof(DelegateType);

            var invoke         = delType.GetMethod("Invoke");
            var returnType     = invoke.ReturnType;
            var parameterTypes = ((LinqArray <ParameterInfo>)invoke.GetParameters()).Select(s => s.ParameterType).ToArray();

            var dynMethod = new DynamicMethod(name, returnType, parameterTypes, owner, skipVisibility: true);

            // TODO: see https://github.com/dotnet/corefx/issues/4543 item 2
#if NETSTANDARD
            const bool allowUnverifiable = false;
#else
            bool allowUnverifiable = AllowsUnverifiableCode(TypeHelpers.GetModule(owner));
#endif
            var ret = new Emit <DelegateType>(dynMethod.CallingConvention, returnType, parameterTypes, allowUnverifiable, doVerify, strictBranchVerification)
            {
                DynMethod = dynMethod
            };

            return(ret);
        }
Exemple #6
0
        /// <summary>
        /// Ends the given catch block.
        /// </summary>
        public Emit <DelegateType> EndCatchBlock(CatchBlock forCatch)
        {
            if (forCatch == null)
            {
                throw new ArgumentNullException("forCatch");
            }

            if (((IOwned)forCatch).Owner != this)
            {
                FailOwnership(forCatch);
            }

            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            UpdateState(Wrap(new[] { new StackTransition(0) }, "EndCatchBlock"));

            var location = CatchBlocks[forCatch];

            if (location.Item2 != -1)
            {
                throw new InvalidOperationException("CatchBlock has already been ended");
            }

            IL.EndCatchBlock();

            CatchBlocks[forCatch] = SigilTuple.Create(location.Item1, IL.Index);

            return(this);
        }
Exemple #7
0
        /// <summary>
        /// Begins a catch block for the given exception type in the given exception block.
        ///
        /// The given exception block must still be open.
        /// </summary>
        public CatchBlock BeginCatchBlock(ExceptionBlock forTry, Type exceptionType)
        {
            if (exceptionType == null)
            {
                throw new ArgumentNullException("exceptionType");
            }

            if (forTry == null)
            {
                throw new ArgumentNullException("forTry");
            }

            if (((IOwned)forTry).Owner != this)
            {
                FailOwnership(forTry);
            }

            if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek())
            {
                throw new InvalidOperationException("Cannot start CatchBlock on " + forTry + " while inner ExceptionBlock is still open");
            }

            if (!typeof(Exception).IsAssignableFrom(exceptionType))
            {
                throw new ArgumentException("BeginCatchBlock expects a type descending from Exception, found " + exceptionType, "exceptionType");
            }

            var currentlyOpen = CatchBlocks.Where(c => c.Key.ExceptionBlock == forTry && c.Value.Item2 == -1).Select(s => s.Key).SingleOrDefault();

            if (currentlyOpen != null)
            {
                throw new InvalidOperationException("Cannot start a new catch block, " + currentlyOpen + " has not been ended");
            }

            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            UpdateState(Wrap(new[] { new StackTransition(0) }, "BeginCatchBlock"));

            var tryBlock = TryBlocks[forTry];

            if (tryBlock.Item2 != -1)
            {
                throw new SigilVerificationException("BeginCatchBlock expects an unclosed exception block, but " + forTry + " is already closed", IL.Instructions(AllLocals));
            }

            IL.BeginCatchBlock(exceptionType);

            UpdateState(Wrap(StackTransition.Push(exceptionType), "BeginCatchBlock"));

            var ret = new CatchBlock(exceptionType, forTry);

            CatchBlocks[ret] = SigilTuple.Create(IL.Index, -1);

            return(ret);
        }
Exemple #8
0
        internal Delegate InnerCreateDelegate(Type delegateType, out string instructions, OptimizationOptions optimizationOptions)
        {
            Seal(optimizationOptions);

            var il = DynMethod.GetILGenerator();

            instructions = IL.UnBuffer(il);

            AutoNamer.Release(this);

            return(DynMethod.CreateDelegate(delegateType));
        }
Exemple #9
0
        /// <summary>
        /// Creates a new EmitNonGeneric, optionally using the provided name and module for the inner DynamicMethod.
        ///
        /// If name is not defined, a sane default is generated.
        ///
        /// If module is not defined, a module with the same trust as the executing assembly is used instead.
        ///
        /// If doVerify is false (default is true) Sigil will *not* throw an exception on invalid IL.  This is faster, but the benefits
        /// of Sigil are reduced to "a nicer ILGenerator interface".
        ///
        /// If strictBranchValidation is true (default is false) Sigil will enforce "Backward branch constraints" which are *technically* required
        /// for valid CIL, but in practice often ignored.  The most common case to set this option is if you are generating types to write to disk.
        /// </summary>
        public static Emit NewDynamicMethod(Type returnType, Type[] parameterTypes, string name = null, ModuleBuilder module = null, bool doVerify = true, bool strictBranchVerification = false)
        {
            ValidateReturnAndParameterTypes(returnType, parameterTypes);

            module = module ?? Emit <NonGenericPlaceholderDelegate> .Module;

            var innerEmit = Emit <NonGenericPlaceholderDelegate> .MakeNonGenericEmit(CallingConventions.Standard, returnType, parameterTypes, Emit <NonGenericPlaceholderDelegate> .AllowsUnverifiableCode(module), doVerify, strictBranchVerification);

            var ret = new Emit(innerEmit, NonGenericEmitType.DynamicMethod);

            ret.Module         = module;
            ret.Name           = name ?? AutoNamer.Next("_DynamicMethod");
            ret.ReturnType     = returnType;
            ret.ParameterTypes = parameterTypes;

            return(ret);
        }
Exemple #10
0
        /// <summary>
        /// Declare a new local of the given type in the current method.
        ///
        /// Name is optional, and only provided for debugging purposes.  It has no
        /// effect on emitted IL.
        ///
        /// Be aware that each local takes some space on the stack, inefficient use of locals
        /// could lead to StackOverflowExceptions at runtime.
        /// </summary>
        public Local DeclareLocal(Type type, string name = null)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            name = name ?? AutoNamer.Next(this, "_local", Locals.Names, Labels.Names);

            if (CurrentLocals.ContainsKey(name))
            {
                throw new InvalidOperationException("Local with name '" + name + "' already exists");
            }

            var existingLocal = FreedLocals.FirstOrDefault(l => l.LocalType == type);

            DeclareLocallDelegate local;
            ushort localIndex;

            if (existingLocal == null)
            {
                local = IL.DeclareLocal(type);

                localIndex = NextLocalIndex;
                NextLocalIndex++;
            }
            else
            {
                local      = existingLocal.LocalDel;
                localIndex = existingLocal.Index;

                FreedLocals.Remove(existingLocal);
            }

            var ret = new Local(this, localIndex, type, local, name, LocalReleased, IL.Index);

            UnusedLocals.Add(ret);

            AllLocals.Add(ret);

            CurrentLocals[ret.Name] = ret;

            return(ret);
        }
Exemple #11
0
        /// <summary>
        /// Begins a finally block on the given exception block.
        ///
        /// Only one finally block can be defined per exception block, and the block cannot appear within a catch block.
        ///
        /// The given exception block must still be open.
        /// </summary>
        public FinallyBlock BeginFinallyBlock(ExceptionBlock forTry)
        {
            if (forTry == null)
            {
                throw new ArgumentNullException("forTry");
            }

            if (((IOwned)forTry).Owner != this)
            {
                FailOwnership(forTry);
            }

            var tryBlock = TryBlocks[forTry];

            if (tryBlock.Item2 != -1)
            {
                throw new InvalidOperationException("BeginFinallyBlock expects an unclosed exception block, but " + forTry + " is already closed");
            }

            if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek())
            {
                throw new InvalidOperationException("Cannot begin FinallyBlock on " + forTry + " while inner ExceptionBlock " + CurrentExceptionBlock.Peek() + " is still open");
            }

            if (FinallyBlocks.Any(kv => kv.Key.ExceptionBlock == forTry))
            {
                throw new InvalidOperationException("There can only be one finally block per ExceptionBlock, and one is already defined for " + forTry);
            }

            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            UpdateState(Wrap(new[] { new StackTransition(0) }, "BeginFinallyBlock"));

            var ret = new FinallyBlock(forTry);

            IL.BeginFinallyBlock();

            FinallyBlocks[ret] = SigilTuple.Create(IL.Index, -1);

            return(ret);
        }
Exemple #12
0
        /// <summary>
        /// Defines a new label.
        ///
        /// This label can be used for branching, leave, and switch instructions.
        ///
        /// A label must be marked exactly once after being defined, using the MarkLabel() method.
        /// </summary>
        public Label DefineLabel(string name = null)
        {
            name = name ?? AutoNamer.Next(this, "_label", Locals.Names, Labels.Names);

            if (CurrentLabels.ContainsKey(name))
            {
                throw new InvalidOperationException("Label with name '" + name + "' already exists");
            }

            var label = IL.DefineLabel();

            var ret = new Label(this, label, name);

            UnusedLabels.Add(ret);
            UnmarkedLabels.Add(ret);

            CurrentLabels[name] = ret;

            return(ret);
        }
Exemple #13
0
        internal static Emit <DelegateType> DisassemblerDynamicMethod(Type[] parameters = null, string name = null, ModuleBuilder module = null, bool doVerify = true, bool strictBranchVerification = false)
        {
            module = module ?? Module;

            name = name ?? AutoNamer.Next("_DynamicMethod");

            ValidateNewParameters <DelegateType>();

            var delType = typeof(DelegateType);

            var invoke         = delType.GetMethod("Invoke");
            var returnType     = invoke.ReturnType;
            var parameterTypes = parameters ?? LinqAlternative.Select(invoke.GetParameters(), s => s.ParameterType).ToArray();

            var dynMethod = new DynamicMethod(name, returnType, parameterTypes, module, skipVisibility: true);

            var ret = new Emit <DelegateType>(dynMethod.CallingConvention, returnType, parameterTypes, AllowsUnverifiableCode(module), doVerify, strictBranchVerification);

            ret.DynMethod = dynMethod;

            return(ret);
        }
Exemple #14
0
        /// <summary>
        /// Start a new exception block.  This is roughly analogous to a `try` block in C#, but an exception block contains it's catch and finally blocks.
        /// </summary>
        public ExceptionBlock BeginExceptionBlock()
        {
            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            UpdateState(Wrap(new[] { new StackTransition(0) }, "BeginExceptionBlock"));

            var labelDel = IL.BeginExceptionBlock();
            var label    = new Label(this, labelDel, AutoNamer.Next(this, "__exceptionBlockEnd"));

            CurrentLabels[label.Name] = label;

            var ret = new ExceptionBlock(label);

            TryBlocks[ret] = SigilTuple.Create(IL.Index, -1);

            CurrentExceptionBlock.Push(ret);

            return(ret);
        }
Exemple #15
0
        /// <summary>
        /// Declare a new local of the given type in the current method.
        ///
        /// Name is optional, and only provided for debugging purposes.  It has no
        /// effect on emitted IL.
        ///
        /// Be aware that each local takes some space on the stack, inefficient use of locals
        /// could lead to StackOverflowExceptions at runtime.
        ///
        /// Jil will reuse local index on the stack if the corresponding Local instance has been disposed.
        /// By default Jil will set reused locals to their default value, you can change this behavior
        /// by passing initializeReused = false.
        /// </summary>
        public Local DeclareLocal(Type type, string name = null, bool initializeReused = true)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            name = name ?? AutoNamer.Next(this, "_local", Locals.Names, Labels.Names);

            if (CurrentLocals.ContainsKey(name))
            {
                throw new InvalidOperationException("Local with name '" + name + "' already exists");
            }

            var existingLocal = FreedLocals.FirstOrDefault(l => l.LocalType == type);

            DeclareLocallDelegate local;
            ushort localIndex;

            if (existingLocal == null)
            {
                local = IL.DeclareLocal(type);

                localIndex = NextLocalIndex;
                NextLocalIndex++;
            }
            else
            {
                local      = existingLocal.LocalDel;
                localIndex = existingLocal.Index;

                FreedLocals.Remove(existingLocal);
            }

            var ret = new Local(this, localIndex, type, local, name, LocalReleased, IL.Index);

            UnusedLocals.Add(ret);

            AllLocals.Add(ret);

            CurrentLocals[ret.Name] = ret;

            // we need to initialize this local to it's default value, because otherwise
            //   it might be read from to get some non-sense
            if (existingLocal != null && initializeReused)
            {
                if (!TypeHelpers.IsValueType(type))
                {
                    // reference types all get nulled
                    LoadNull();
                    StoreLocal(ret);
                }
                else
                {
                    // handle known primitives better
                    //   so as not to confuse the JIT
                    var defaultLoaded = false;

                    if (type == typeof(bool))
                    {
                        LoadConstant(default(bool));
                        defaultLoaded = true;
                    }

                    if (type == typeof(byte))
                    {
                        LoadConstant(default(byte));
                        defaultLoaded = true;
                    }

                    if (type == typeof(sbyte))
                    {
                        LoadConstant(default(sbyte));
                        defaultLoaded = true;
                    }

                    if (type == typeof(short))
                    {
                        LoadConstant(default(short));
                        defaultLoaded = true;
                    }

                    if (type == typeof(ushort))
                    {
                        LoadConstant(default(ushort));
                        defaultLoaded = true;
                    }

                    if (type == typeof(int))
                    {
                        LoadConstant(default(int));
                        defaultLoaded = true;
                    }

                    if (type == typeof(uint))
                    {
                        LoadConstant(default(uint));
                        defaultLoaded = true;
                    }

                    if (type == typeof(long))
                    {
                        LoadConstant(default(long));
                        defaultLoaded = true;
                    }

                    if (type == typeof(ulong))
                    {
                        LoadConstant(default(ulong));
                        defaultLoaded = true;
                    }

                    if (type == typeof(float))
                    {
                        LoadConstant(default(float));
                        defaultLoaded = true;
                    }

                    if (type == typeof(double))
                    {
                        LoadConstant(default(double));
                        defaultLoaded = true;
                    }

                    if (defaultLoaded)
                    {
                        StoreLocal(ret);
                    }
                    else
                    {
                        // if it's use defined though, we've got little choice
                        LoadLocalAddress(ret);
                        InitializeObject(type);
                    }
                }
            }

            return(ret);
        }
Exemple #16
0
        /// <summary>
        /// Ends the given exception block.
        ///
        /// All catch and finally blocks associated with the given exception block must be ended before this method is called.
        /// </summary>
        public Emit <DelegateType> EndExceptionBlock(ExceptionBlock forTry)
        {
            if (forTry == null)
            {
                throw new ArgumentNullException("forTry");
            }

            if (((IOwned)forTry).Owner != this)
            {
                FailOwnership(forTry);
            }

            var location = TryBlocks[forTry];

            // Can't close the same exception block twice
            if (location.Item2 != -1)
            {
                throw new InvalidOperationException("ExceptionBlock has already been ended");
            }

            if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek())
            {
                throw new InvalidOperationException("Cannot end outer ExceptionBlock " + forTry + " while inner EmitExceptionBlock " + CurrentExceptionBlock.Peek() + " is open");
            }

            // Can't close an exception block while there are outstanding catch blocks
            foreach (var kv in CatchBlocks.AsEnumerable())
            {
                if (kv.Key.ExceptionBlock != forTry)
                {
                    continue;
                }

                if (kv.Value.Item2 == -1)
                {
                    throw new InvalidOperationException("Cannot end ExceptionBlock, CatchBlock " + kv.Key + " has not been ended");
                }
            }

            foreach (var kv in FinallyBlocks.AsEnumerable())
            {
                if (kv.Key.ExceptionBlock != forTry)
                {
                    continue;
                }

                if (kv.Value.Item2 == -1)
                {
                    throw new InvalidOperationException("Cannot end ExceptionBlock, FinallyBlock " + kv.Key + " has not been ended");
                }
            }

            if (!CatchBlocks.Any(k => k.Key.ExceptionBlock == forTry) && !FinallyBlocks.Any(k => k.Key.ExceptionBlock == forTry))
            {
                throw new InvalidOperationException("Cannot end ExceptionBlock without defining at least one of a catch or finally block");
            }

            IL.EndExceptionBlock();

            TryBlocks[forTry] = SigilTuple.Create(location.Item1, IL.Index);

            Marks[forTry.Label] = IL.Index;

            CurrentExceptionBlock.Pop();

            if (MustMark)
            {
                MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel")));
            }

            return(this);
        }