internal LocalStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { // 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(variable.IsByRef ? variable.Type.MakeByRefType() : variable.Type, variable); }
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.EmitInt(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); } }
public void Test1() { Expression <Func <TestClassA, int?> > exp = o => o.ArrayB[0].X; Func <TestClassA, int?> compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); Assert.That(compiledExp(null), Is.EqualTo(null)); Assert.That(compiledExp(new TestClassA()), Is.EqualTo(null)); Assert.That(compiledExp(new TestClassA { ArrayB = new TestClassB[] { null } }), Is.EqualTo(null)); Assert.That(compiledExp(new TestClassA { ArrayB = new[] { new TestClassB() } }), Is.EqualTo(null)); int?actual = compiledExp(new TestClassA { ArrayB = new[] { new TestClassB { X = 1 } } }); Assert.That(actual, Is.EqualTo(1)); }
public void TestLogical4() { Expression <Func <TestClassA, bool?> > exp = o => !(o.B.X > 0); Func <TestClassA, bool?> compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); Assert.IsNull(compiledExp(null)); Assert.IsNull(compiledExp(new TestClassA())); Assert.IsNull(compiledExp(new TestClassA { B = new TestClassB() })); Assert.AreEqual(true, compiledExp(new TestClassA { B = new TestClassB { X = -1 } })); Assert.AreEqual(false, compiledExp(new TestClassA { B = new TestClassB { X = 1 } })); }
public void Each_03() { var lambda = LambdaCompiler.Compile <Nothing>(new Nothing(), @" var(@foo, list(""57"", ""67"", ""77"", ""88.88"", ""97"")) var(@bar, list()) each(@ix, foo, { if(any(@eq(""57"", ix), @eq(""77"", ix), @eq(""88.88"", ix)), { add(bar, number(ix)) }) }) bar"); var result = lambda(); Assert.IsTrue(result is List <object>); var list = result as List <object>; Assert.AreEqual(3, list.Count); Assert.AreEqual(57, list[0]); Assert.AreEqual(77, list[1]); Assert.AreEqual(88.88, list[2]); }
public Test2() { list = new List <TestData>(); for (int i = 0; i < 1000000; ++i) { list.Add(new TestData { X = i }); } linq = x => x.Aggregate(0, (s, o) => s + o.X); loopsSharp = x => { int res = 0; for (int i = 0; i < x.Count; i++) { res = res + x[i].X; } return(res); }; Expression <Func <List <TestData>, int> > exp = x => x.Aggregate(0, (s, o) => s + o.X); loopsExpression = LambdaCompiler.Compile(exp.EliminateLinq(), CompilerOptions.None); }
public void TestEnumConstant() { var typeBuilder = moduleBuilder.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Class); var methodName = nameof(TestEnumConstant); var methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Static); // Cannot use Expression<Func<>> interface here because enum type gets optimized to int // Expression<Func<TestEnum, string>> expression = x => x == TestEnum.Two ? "2" : x.ToString(); var parameter = Expression.Parameter(typeof(TestEnum)); var constant = Expression.Constant(TestEnum.Two, typeof(TestEnum)); var condition = Expression.Condition(Expression.Equal(parameter, constant), Expression.Constant("2", typeof(string)), Expression.Call(parameter, typeof(object).GetMethod("ToString", Type.EmptyTypes))); var lambda = Expression.Lambda(condition, parameter); LambdaCompiler.CompileToMethod(lambda, methodBuilder, CompilerOptions.All); var type = typeBuilder.CreateType(); var method = type.GetMethod(methodName, new[] { typeof(TestEnum) }); Assert.That(method.Invoke(null, new object[] { TestEnum.Two }), Is.EqualTo("2")); Assert.That(method.Invoke(null, new object[] { TestEnum.One }), Is.EqualTo("One")); }
public void TestNullablePrimitiveConstant() { var typeBuilder = moduleBuilder.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Class); var methodName = nameof(TestNullablePrimitiveConstant); var methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Static); // Cannot declare constant of nullable type via Expression<Func<>> interface // Expression<Func<int?, string>> expression = x => x == (int? 2) ? "two" : x.ToString(); var parameter = Expression.Parameter(typeof(int?)); var constant = Expression.Constant((int?)2, typeof(int?)); var condition = Expression.Condition(Expression.Equal(parameter, constant), Expression.Constant("two", typeof(string)), Expression.Call(parameter, typeof(int?).GetMethod("ToString", Type.EmptyTypes))); var lambda = Expression.Lambda(condition, parameter); LambdaCompiler.CompileToMethod(lambda, methodBuilder, CompilerOptions.All); var type = typeBuilder.CreateType(); var method = type.GetMethod(methodName, new[] { typeof(int?) }); Assert.That(method.Invoke(null, new object[] { new int?(2) }), Is.EqualTo("two")); Assert.That(method.Invoke(null, new object[] { new int?(3) }), Is.EqualTo("3")); }
private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) { lc.EmitClosureArgument(); lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Locals")); AddLocal(lc, locals.SelfVariable); EmitSet(locals.SelfVariable); }
// 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) { var v = locals.SelfVariable; var local = new LocalStorage(lc, v); local.EmitStore(ResolveVariable(v)); _locals.Add(v, local); } }
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); }
// 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 { #if CLR2 // array[i] = new StrongBox<T>(default(T)); lc.IL.EmitDefault(v.Type); 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)); #endif } // 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); }
internal LocalBoxStorage(LambdaCompiler compiler, ParameterExpression variable) : base(compiler, variable) { _boxType = typeof(StrongBox<>).MakeGenericType(variable.Type); _boxValueField = _boxType.GetField("Value"); _boxLocal = compiler.GetNamedLocal(_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; }
// 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 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); // 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 writebacks if needed EmitWriteBack(wb); }
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; }
internal Storage(LambdaCompiler compiler, ParameterExpression variable) { Compiler = compiler; Variable = variable; }
/// <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)); }
internal ArgumentStorage(LambdaCompiler compiler, ParameterExpression p) : base(compiler, p) { _argument = compiler.GetLambdaArgument(compiler.Parameters.IndexOf(p)); }
private void SetParent(LambdaCompiler lc, CompilerScope parent) { Debug.Assert(_parent == null && parent != this); _parent = parent; if (NeedsClosure && _parent != null) { _closureHoistedLocals = _parent.NearestHoistedLocals; } var hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly(); if (hoistedVars.Count > 0) { _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars); AddLocal(lc, _hoistedLocals.SelfVariable); } }