internal LocalStorage(LambdaCompiler compiler, ParameterExpression v) : base(compiler, v) { // ByRef variables are supported. This is used internally by // the compiler when emitting an inlined lambda invoke, to // handle ByRef parameters. BlockExpression prevents this // from being exposed to user created trees. _local = compiler.GetNamedLocal(v.IsByRef ? v.Type.MakeByRefType() : v.Type, v.Name); }
private void AllocateLocals(LambdaCompiler lc) { foreach (ParameterExpression expression in this.GetVariables()) { if (((VariableStorageKind) this.Definitions[expression]) == VariableStorageKind.Local) { Storage storage; if (this.IsMethod && lc.Parameters.Contains(expression)) { storage = new ArgumentStorage(lc, expression); } else { storage = new LocalStorage(lc, expression); } this._locals.Add(expression, storage); } } }
private void EmitDelegateConstruction(LambdaCompiler inner) { var delegateType = inner._lambda.Type; if (inner._method is DynamicMethod dynamicMethod) { // dynamicMethod.CreateDelegate(delegateType, closure) _boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod)); IL.EmitType(delegateType); EmitClosureCreation(inner); IL.Emit(OpCodes.Callvirt, typeof(DynamicMethod).GetMethod(nameof(DynamicMethod.CreateDelegate), new[] { typeof(Type), typeof(object) })); IL.Emit(OpCodes.Castclass, delegateType); } else { // new DelegateType(closure) EmitClosureCreation(inner); IL.Emit(OpCodes.Ldftn, inner._method); IL.Emit(OpCodes.Newobj, (ConstructorInfo)delegateType.GetMember(".ctor")[0]); } }
private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) { int index; if (!_indexes.TryGetValue(value, out index)) { _indexes.Add(value, index = _values.Count); _values.Add(value); } lc.IL.EmitPrimitive(index); lc.IL.Emit(OpCodes.Ldelem_Ref); if (type.IsValueType) { lc.IL.Emit(OpCodes.Unbox_Any, type); } else if (type != typeof(object)) { lc.IL.Emit(OpCodes.Castclass, type); } }
// Allocates slots for IL locals or IL arguments private void AllocateLocals(LambdaCompiler lc) { foreach (ParameterExpression v in GetVariables()) { if (Definitions[v] == VariableStorageKind.Local) { Storage s; //If v is in lc.Parameters, it is a parameter. //Otherwise, it is a local variable. if (lc.Parameters.Contains(v)) { s = new ArgumentStorage(lc, v); } else { s = new LocalStorage(lc, v); } _locals.Add(v, s); } } }
/// <summary> /// Compiler entry point /// </summary> /// <param name="lambda">LambdaExpression to compile.</param> /// <param name="method">Product of compilation</param> /// <param name="delegateType">Type of the delegate to create</param> /// <param name="emitDebugSymbols">True to emit debug symbols, false otherwise.</param> /// <param name="forceDynamic">Force dynamic method regardless of save assemblies.</param> /// <returns>The compiled delegate.</returns> internal static Delegate CompileLambda(LambdaExpression lambda, Type delegateType, bool emitDebugSymbols, bool forceDynamic, out MethodInfo method) { // 1. Create signature List <Type> types; List <string> names; string name; Type returnType; ComputeSignature(lambda, out types, out names, out name, out returnType); // 2. Bind lambda AnalyzedTree tree = AnalyzeLambda(ref lambda); // 3. Create lambda compiler LambdaCompiler c = CreateDynamicCompiler(tree, lambda, name, returnType, types, null, emitDebugSymbols, forceDynamic); // 4. Emit c.EmitLambdaBody(null); // 5. Return the delegate. return(c.CreateDelegate(delegateType, out method)); }
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection <ParameterExpression> vars) { if (NearestHoistedLocals != null) { // Find what array each variable is on & its index var indexes = new List <long>(vars.Count); foreach (var variable in vars) { // For each variable, find what array it's defined on ulong parents = 0; HoistedLocals locals = NearestHoistedLocals; while (!locals.Indexes.ContainsKey(variable)) { parents++; locals = locals.Parent; Debug.Assert(locals != null); } // combine the number of parents we walked, with the // real index of variable to get the index to emit. ulong index = (parents << 32) | (uint)locals.Indexes[variable]; indexes.Add((long)index); } if (indexes.Count > 0) { EmitGet(NearestHoistedLocals.SelfVariable); lc.EmitConstantArray(indexes.ToArray()); lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new[] { typeof(object[]), typeof(long[]) })); return; } } // No visible variables lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes)); return; }
/// <summary> /// Emits code which creates new instance of the delegateType delegate. /// /// Since the delegate is getting closed over the "Closure" argument, this /// cannot be used with virtual/instance methods (inner must be static method) /// </summary> private void EmitDelegateConstruction(LambdaCompiler inner) { Type delegateType = inner._lambda.Type; DynamicMethod dynamicMethod = inner._method as DynamicMethod; if (dynamicMethod != null) { // Emit MethodInfo.CreateDelegate instead because DynamicMethod is not in Windows 8 Profile _boundConstants.EmitConstant(this, dynamicMethod, typeof(MethodInfo)); _ilg.EmitType(delegateType); EmitClosureCreation(inner); _ilg.Emit(OpCodes.Callvirt, typeof(MethodInfo).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) })); _ilg.Emit(OpCodes.Castclass, delegateType); } else { // new DelegateType(closure) EmitClosureCreation(inner); _ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method); _ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0])); } }
private void EmitNewHoistedLocals(LambdaCompiler lc) { if (this._hoistedLocals != null) { lc.IL.EmitInt(this._hoistedLocals.Variables.Count); lc.IL.Emit(OpCodes.Newarr, typeof(object)); int num = 0; foreach (ParameterExpression expression in this._hoistedLocals.Variables) { lc.IL.Emit(OpCodes.Dup); lc.IL.EmitInt(num++); Type type = typeof(StrongBox <>).MakeGenericType(new Type[] { expression.Type }); if (this.IsMethod && lc.Parameters.Contains(expression)) { int index = lc.Parameters.IndexOf(expression); lc.EmitLambdaArgument(index); lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(new Type[] { expression.Type })); } else if (expression == this._hoistedLocals.ParentVariable) { this.ResolveVariable(expression, this._closureHoistedLocals).EmitLoad(); lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(new Type[] { expression.Type })); } else { lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); } if (this.ShouldCache(expression)) { lc.IL.Emit(OpCodes.Dup); this.CacheBoxToLocal(lc, expression); } lc.IL.Emit(OpCodes.Stelem_Ref); } this.EmitSet(this._hoistedLocals.SelfVariable); } }
/// <summary> /// Emits a delegate to the method generated for the LambdaExpression. /// May end up creating a wrapper to match the requested delegate type. /// </summary> /// <param name="lambda">Lambda for which to generate a delegate</param> /// private void EmitDelegateConstruction(LambdaExpression lambda) { // 1. Create the new compiler LambdaCompiler impl; if (_method is DynamicMethod) { impl = new LambdaCompiler(_tree, lambda); } else { // When the lambda does not have a name or the name is empty, generate a unique name for it. var name = string.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name; var mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static); impl = new LambdaCompiler(_tree, lambda, mb); } // 2. emit the lambda // Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization. impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail); // 3. emit the delegate creation in the outer lambda EmitDelegateConstruction(impl); }
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection <ParameterExpression> vars) { if (NearestHoistedLocals != null && vars.Count > 0) { // Find what array each variable is on & its index var indexes = new ArrayBuilder <long>(vars.Count); foreach (ParameterExpression variable in vars) { // For each variable, find what array it's defined on ulong parents = 0; HoistedLocals?locals = NearestHoistedLocals; while (!locals.Indexes.ContainsKey(variable)) { parents++; locals = locals.Parent; Debug.Assert(locals != null); } // combine the number of parents we walked, with the // real index of variable to get the index to emit. ulong index = (parents << 32) | (uint)locals.Indexes[variable]; indexes.UncheckedAdd((long)index); } EmitGet(NearestHoistedLocals.SelfVariable); lc.EmitConstantArray(indexes.ToArray()); lc.IL.Emit(OpCodes.Call, RuntimeOps_CreateRuntimeVariables_ObjectArray_Int64Array); } else { // No visible variables lc.IL.Emit(OpCodes.Call, RuntimeOps_CreateRuntimeVariables); } }
private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) { lc.EmitClosureArgument(); lc.IL.Emit(OpCodes.Ldfld, Closure_Locals); AddLocal(lc, locals.SelfVariable); EmitSet(locals.SelfVariable); }
private static void EmitConstantsArray(LambdaCompiler lc) { lc.EmitClosureArgument(); lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants")); }
internal ArgumentStorage(LambdaCompiler compiler, ParameterExpression p) : base(compiler, p) { this._argument = compiler.GetLambdaArgument(compiler.Parameters.IndexOf(p)); }
internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) { this.SetParent(lc, parent); this.AllocateLocals(lc); if (this.IsMethod && (this._closureHoistedLocals != null)) { this.EmitClosureAccess(lc, this._closureHoistedLocals); } this.EmitNewHoistedLocals(lc); if (this.IsMethod) { this.EmitCachedVariables(); } return this; }
private void EmitNewHoistedLocals(LambdaCompiler lc) { if (this._hoistedLocals != null) { lc.IL.EmitInt(this._hoistedLocals.Variables.Count); lc.IL.Emit(OpCodes.Newarr, typeof(object)); int num = 0; foreach (ParameterExpression expression in this._hoistedLocals.Variables) { lc.IL.Emit(OpCodes.Dup); lc.IL.EmitInt(num++); Type type = typeof(StrongBox<>).MakeGenericType(new Type[] { expression.Type }); if (this.IsMethod && lc.Parameters.Contains(expression)) { int index = lc.Parameters.IndexOf(expression); lc.EmitLambdaArgument(index); lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(new Type[] { expression.Type })); } else if (expression == this._hoistedLocals.ParentVariable) { this.ResolveVariable(expression, this._closureHoistedLocals).EmitLoad(); lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(new Type[] { expression.Type })); } else { lc.IL.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); } if (this.ShouldCache(expression)) { lc.IL.Emit(OpCodes.Dup); this.CacheBoxToLocal(lc, expression); } lc.IL.Emit(OpCodes.Stelem_Ref); } this.EmitSet(this._hoistedLocals.SelfVariable); } }
internal LocalBoxStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { this._boxType = typeof(StrongBox <>).MakeGenericType(new Type[] { variable.Type }); this._boxValueField = this._boxType.GetField("Value"); this._boxLocal = compiler.GetNamedLocal(this._boxType, variable); }
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection<ParameterExpression> vars) { if (NearestHoistedLocals != null) { // Find what array each variable is on & its index var indexes = new List<long>(vars.Count); foreach (var variable in vars) { // For each variable, find what array it's defined on ulong parents = 0; HoistedLocals locals = NearestHoistedLocals; while (!locals.Indexes.ContainsKey(variable)) { parents++; locals = locals.Parent; Debug.Assert(locals != null); } // combine the number of parents we walked, with the // real index of variable to get the index to emit. ulong index = (parents << 32) | (uint)locals.Indexes[variable]; indexes.Add((long)index); } if (indexes.Count > 0) { EmitGet(NearestHoistedLocals.SelfVariable); lc.EmitConstantArray(indexes.ToArray()); lc.IL.EmitCall(typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new[] { typeof(object[]), typeof(long[]) })); return; } } // No visible variables lc.IL.EmitCall(typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes)); return; }
internal LocalStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { _local = compiler.GetNamedLocal(variable.Type, variable.Name); }
internal LocalStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { this._local = compiler.GetNamedLocal(variable.IsByRef ? variable.Type.MakeByRefType() : variable.Type, variable); }
// Allocates slots for IL locals or IL arguments private void AllocateLocals(LambdaCompiler lc) { foreach (ParameterExpression v in GetVariables()) { if (Definitions[v] == VariableStorageKind.Local) { // // If v is in lc.Parameters, it is a parameter. // Otherwise, it is a local variable. // // Also, for inlined lambdas we'll create a local, which // is possibly a byref local if the parameter is byref. // Storage s; if (IsMethod && lc.Parameters.Contains(v)) { s = new ArgumentStorage(lc, v); } else { s = new LocalStorage(lc, v); } _locals.Add(v, s); } } }
private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) { lc.EmitClosureArgument(); lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Locals")); this.AddLocal(lc, locals.SelfVariable); this.EmitSet(locals.SelfVariable); }
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection<ParameterExpression> vars) { if (this.NearestHoistedLocals != null) { List<long> list = new List<long>(vars.Count); foreach (ParameterExpression expression in vars) { ulong num = 0L; HoistedLocals nearestHoistedLocals = this.NearestHoistedLocals; while (!nearestHoistedLocals.Indexes.ContainsKey(expression)) { num += (ulong) 1L; nearestHoistedLocals = nearestHoistedLocals.Parent; } ulong num2 = (num << 0x20) | ((ulong) nearestHoistedLocals.Indexes[expression]); list.Add((long) num2); } if (list.Count > 0) { this.EmitGet(this.NearestHoistedLocals.SelfVariable); lc.EmitConstantArray<long>(list.ToArray()); lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new Type[] { typeof(object[]), typeof(long[]) })); return; } } lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes)); }
private void CacheBoxToLocal(LambdaCompiler lc, ParameterExpression v) { LocalBoxStorage storage = new LocalBoxStorage(lc, v); storage.EmitStoreBox(); this._locals.Add(v, storage); }
private void SetParent(LambdaCompiler lc, CompilerScope parent) { this._parent = parent; if (this.NeedsClosure && (this._parent != null)) { this._closureHoistedLocals = this._parent.NearestHoistedLocals; } ReadOnlyCollection<ParameterExpression> vars = (from p in this.GetVariables() where ((VariableStorageKind) this.Definitions[p]) == VariableStorageKind.Hoisted select p).ToReadOnly<ParameterExpression>(); if (vars.Count > 0) { this._hoistedLocals = new HoistedLocals(this._closureHoistedLocals, vars); this.AddLocal(lc, this._hoistedLocals.SelfVariable); } }
internal Storage(LambdaCompiler compiler, ParameterExpression variable) { Compiler = compiler; Variable = variable; }
internal void AddLocal(LambdaCompiler gen, ParameterExpression variable) { this._locals.Add(variable, new LocalStorage(gen, variable)); }
private void EmitInlinedInvoke(InvocationExpression invoke) { var lambda = invoke.LambdaOperand; // This is tricky: we need to emit the arguments outside of the // scope, but set them inside the scope. Fortunately, using the IL // stack it is entirely doable. // 1. Emit invoke arguments List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke); // 2. Create the nested LambdaCompiler var inner = new LambdaCompiler(this, lambda); // 3. Emit the body inner.EmitLambdaBody(_scope, true); // 4. Emit writebacks if needed EmitWriteBack(wb); }
internal LocalBoxStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { this._boxType = typeof(StrongBox<>).MakeGenericType(new Type[] { variable.Type }); this._boxValueField = this._boxType.GetField("Value"); this._boxLocal = compiler.GetNamedLocal(this._boxType, variable); }
/// <summary> /// Called when entering a lambda/block. Performs all variable allocation /// needed, including creating hoisted locals and IL locals for accessing /// parent locals /// </summary> internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) { SetParent(lc, parent); AllocateLocals(lc); if (IsMethod && _closureHoistedLocals != null) { EmitClosureAccess(lc, _closureHoistedLocals); } EmitNewHoistedLocals(lc); if (IsMethod) { EmitCachedVariables(); } return this; }
internal Storage(LambdaCompiler compiler, ParameterExpression variable) { this.Compiler = compiler; this.Variable = variable; }
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection<ParameterExpression> vars) { if (NearestHoistedLocals != null && vars.Count > 0) { // Find what array each variable is on & its index var indexes = new ArrayBuilder<long>(vars.Count); foreach (ParameterExpression variable in vars) { // For each variable, find what array it's defined on ulong parents = 0; HoistedLocals locals = NearestHoistedLocals; while (!locals.Indexes.ContainsKey(variable)) { parents++; locals = locals.Parent; Debug.Assert(locals != null); } // combine the number of parents we walked, with the // real index of variable to get the index to emit. ulong index = (parents << 32) | (uint)locals.Indexes[variable]; indexes.UncheckedAdd((long)index); } EmitGet(NearestHoistedLocals.SelfVariable); lc.EmitConstantArray(indexes.ToArray()); lc.IL.Emit(OpCodes.Call, RuntimeOps_CreateRuntimeVariables_ObjectArray_Int64Array); } else { // No visible variables lc.IL.Emit(OpCodes.Call, RuntimeOps_CreateRuntimeVariables); } }
private void EmitClosureAccess(LambdaCompiler lc, HoistedLocals locals) { if (locals != null) { this.EmitClosureToVariable(lc, locals); while ((locals = locals.Parent) != null) { ParameterExpression selfVariable = locals.SelfVariable; LocalStorage storage = new LocalStorage(lc, selfVariable); storage.EmitStore(this.ResolveVariable(selfVariable)); this._locals.Add(selfVariable, storage); } } }
private void SetParent(LambdaCompiler lc, CompilerScope parent) { Debug.Assert(_parent == null && parent != this); _parent = parent; if (NeedsClosure && _parent != null) { _closureHoistedLocals = _parent.NearestHoistedLocals; } ReadOnlyCollection<ParameterExpression> hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly(); if (hoistedVars.Count > 0) { _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars); AddLocal(lc, _hoistedLocals.SelfVariable); } }
internal ArgumentStorage(LambdaCompiler compiler, ParameterExpression p) : base(compiler, p) { _argument = compiler.GetLambdaArgument(compiler.Parameters.IndexOf(p)); }
// Emits creation of the hoisted local storage private void EmitNewHoistedLocals(LambdaCompiler lc) { if (_hoistedLocals == null) { return; } // create the array lc.IL.EmitInt(_hoistedLocals.Variables.Count); lc.IL.Emit(OpCodes.Newarr, typeof(object)); // initialize all elements int i = 0; foreach (ParameterExpression v in _hoistedLocals.Variables) { // array[i] = new StrongBox<T>(...); lc.IL.Emit(OpCodes.Dup); lc.IL.EmitInt(i++); Type boxType = typeof(StrongBox<>).MakeGenericType(v.Type); if (IsMethod && lc.Parameters.Contains(v)) { // array[i] = new StrongBox<T>(argument); int index = lc.Parameters.IndexOf(v); lc.EmitLambdaArgument(index); lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type })); } else if (v == _hoistedLocals.ParentVariable) { // array[i] = new StrongBox<T>(closure.Locals); ResolveVariable(v, _closureHoistedLocals).EmitLoad(); lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type })); } else { // array[i] = new StrongBox<T>(); lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(Type.EmptyTypes)); } // if we want to cache this into a local, do it now if (ShouldCache(v)) { lc.IL.Emit(OpCodes.Dup); CacheBoxToLocal(lc, v); } lc.IL.Emit(OpCodes.Stelem_Ref); } // store it EmitSet(_hoistedLocals.SelfVariable); }
/// <summary> /// Adds a new virtual variable corresponding to an IL local /// </summary> internal void AddLocal(LambdaCompiler gen, ParameterExpression variable) { _locals.Add(variable, new LocalStorage(gen, variable)); }
private void CacheBoxToLocal(LambdaCompiler lc, ParameterExpression v) { Debug.Assert(ShouldCache(v) && !_locals.ContainsKey(v)); var local = new LocalBoxStorage(lc, v); local.EmitStoreBox(); _locals.Add(v, local); }
private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) { var lambda = invoke.LambdaOperand; // This is tricky: we need to emit the arguments outside of the // scope, but set them inside the scope. Fortunately, using the IL // stack it is entirely doable. // 1. Emit invoke arguments List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke); // 2. Create the nested LambdaCompiler var inner = new LambdaCompiler(this, lambda, invoke); // 3. Emit the body // if the inlined lambda is the last expression of the whole lambda, // tail call can be applied. if (wb.Count != 0) { flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail); } inner.EmitLambdaBody(_scope, true, flags); // 4. Emit write-backs if needed EmitWriteBack(wb); }
// Creates IL locals for accessing closures private void EmitClosureAccess(LambdaCompiler lc, HoistedLocals locals) { if (locals == null) { return; } EmitClosureToVariable(lc, locals); while ((locals = locals.Parent) != null) { ParameterExpression v = locals.SelfVariable; var local = new LocalStorage(lc, v); local.EmitStore(ResolveVariable(v)); _locals.Add(v, local); } }