/// <summary> /// Pops a value off the stack and stores it into the given local. /// /// To create a local, use DeclareLocal(). /// </summary> public Emit <DelegateType> StoreLocal(Local local) { if (local == null) { throw new ArgumentNullException("local"); } if (((IOwned)local).Owner != this) { if (((IOwned)local).Owner is DisassembledOperations <DelegateType> ) { return(StoreLocal(local.Name)); } FailOwnership(local); } UnusedLocals.Remove(local); switch (local.Index) { case 0: UpdateState(OpCodes.Stloc_0, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); case 1: UpdateState(OpCodes.Stloc_1, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); case 2: UpdateState(OpCodes.Stloc_2, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); case 3: UpdateState(OpCodes.Stloc_3, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); } if (local.Index >= byte.MinValue && local.Index <= byte.MaxValue) { byte asByte; unchecked { asByte = (byte)local.Index; } UpdateState(OpCodes.Stloc_S, asByte, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); } UpdateState(OpCodes.Stloc, local, Wrap(StackTransition.Pop(local.StackType), "StoreLocal")); return(this); }
/// <summary> /// Pushes a pointer to the given local onto the stack. /// /// To create a local, use DeclareLocal. /// </summary> public Emit <DelegateType> LoadLocalAddress(Local local) { if (local == null) { throw new ArgumentNullException("local"); } if (((IOwned)local).Owner != this) { if (((IOwned)local).Owner is DisassembledOperations <DelegateType> ) { return(LoadLocalAddress(local.Name)); } FailOwnership(local); } UnusedLocals.Remove(local); var type = local.StackType.Type; var ptrType = type.MakePointerType(); if (local.Index >= byte.MinValue && local.Index <= byte.MaxValue) { byte asByte; unchecked { asByte = (byte)local.Index; } UpdateState(OpCodes.Ldloca_S, asByte, Wrap(StackTransition.Push(ptrType), "LoadLocalAddress")); return(this); } short asShort; unchecked { asShort = (short)local.Index; } UpdateState(OpCodes.Ldloca, asShort, Wrap(StackTransition.Push(ptrType), "LoadLocalAddress")); return(this); }
/// <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> /// 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); }