/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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); }