public override void GenerateCode(ILGenerator gen, CodeFlow cf) { if (_cachedReadAccessor is not ICompilablePropertyAccessor accessorToUse) { throw new InvalidOperationException("Property accessor is not compilable: " + _cachedReadAccessor); } Label?skipIfNullLabel = null; if (_nullSafe) { skipIfNullLabel = gen.DefineLabel(); gen.Emit(OpCodes.Dup); gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Cgt_Un); gen.Emit(OpCodes.Brfalse, skipIfNullLabel.Value); } accessorToUse.GenerateCode(_name, gen, cf); cf.PushDescriptor(_exitTypeDescriptor); if (_originalPrimitiveExitTypeDescriptor != null) { // The output of the accessor is a primitive but from the block above it might be null, // so to have a common stack element type at skipIfNull target it is necessary // to box the primitive CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); } if (skipIfNullLabel.HasValue) { gen.MarkLabel(skipIfNullLabel.Value); } }
private void GenerateStaticMethodCode(ILGenerator gen, CodeFlow cf, MethodInfo method) { var stackDescriptor = cf.LastDescriptor(); Label?skipIfNullTarget = null; if (_nullSafe) { skipIfNullTarget = GenerateNullCheckCode(gen); } if (stackDescriptor != null) { // Something on the stack when nothing is needed gen.Emit(OpCodes.Pop); } GenerateCodeForArguments(gen, cf, method, _children); gen.Emit(OpCodes.Call, method); if (_originalPrimitiveExitTypeDescriptor != null) { // The output of the accessor will be a primitive but from the block above it might be null, // so to have a 'common stack' element at skipIfNull target we need to box the primitive CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); } if (skipIfNullTarget.HasValue) { gen.MarkLabel(skipIfNullTarget.Value); } }
private CompiledExpression CreateExpressionClass(ISpelNode expressionToCompile) { var compiledExpression = new SpelCompiledExpression(_loggerFactory); var methodName = "SpelExpression" + _suffixId.GetAndIncrement(); var method = new DynamicMethod(methodName, typeof(object), new Type[] { typeof(SpelCompiledExpression), typeof(object), typeof(IEvaluationContext) }, typeof(SpelCompiledExpression)); var ilGenerator = method.GetILGenerator(4096); var cf = new CodeFlow(compiledExpression); try { expressionToCompile.GenerateCode(ilGenerator, cf); var lastDescriptor = cf.LastDescriptor(); CodeFlow.InsertBoxIfNecessary(ilGenerator, lastDescriptor); if (lastDescriptor == TypeDescriptor.V) { ilGenerator.Emit(OpCodes.Ldnull); } ilGenerator.Emit(OpCodes.Ret); compiledExpression.MethodDelegate = method.CreateDelegate(typeof(SpelExpressionDelegate)); var initMethod = cf.Finish(_suffixId.Value); if (initMethod != null) { compiledExpression.InitDelegate = initMethod.CreateDelegate(typeof(SpelExpressionInitDelegate)); } return(compiledExpression); } catch (Exception ex) { _logger?.LogDebug(expressionToCompile.GetType().Name + ".GenerateCode opted out of compilation: " + ex.Message); return(null); } }
public override void GenerateCode(ILGenerator gen, CodeFlow cf) { CodeFlow.LoadEvaluationContext(gen); var leftDesc = LeftOperand.ExitDescriptor; var rightDesc = RightOperand.ExitDescriptor; var leftPrim = CodeFlow.IsValueType(leftDesc); var rightPrim = CodeFlow.IsValueType(rightDesc); cf.EnterCompilationScope(); LeftOperand.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (leftPrim) { CodeFlow.InsertBoxIfNecessary(gen, leftDesc); } cf.EnterCompilationScope(); RightOperand.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (rightPrim) { CodeFlow.InsertBoxIfNecessary(gen, rightDesc); } gen.Emit(OpCodes.Call, _equalityCheck); cf.PushDescriptor(TypeDescriptor.Z); }
protected static void GenerateCodeForArgument(ILGenerator gen, CodeFlow cf, SpelNode argument, TypeDescriptor paramDesc) { cf.EnterCompilationScope(); argument.GenerateCode(gen, cf); var lastDesc = cf.LastDescriptor(); if (lastDesc == null) { throw new InvalidOperationException("No last descriptor"); } var valueTypeOnStack = CodeFlow.IsValueType(lastDesc); // Check if need to box it for the method reference? if (valueTypeOnStack && paramDesc.IsReferenceType) { CodeFlow.InsertBoxIfNecessary(gen, lastDesc); } else if (paramDesc.IsValueType && !paramDesc.IsBoxed && !valueTypeOnStack) { gen.Emit(OpCodes.Unbox_Any, paramDesc.Value); } else { // This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in) CodeFlow.InsertCastClass(gen, paramDesc); } cf.ExitCompilationScope(); }
private void GenerateInstanceMethodCode(ILGenerator gen, CodeFlow cf, MethodInfo targetMethod, Type targetType) { var stackDescriptor = cf.LastDescriptor(); if (stackDescriptor == null) { // Nothing on the stack but something is needed CodeFlow.LoadTarget(gen); stackDescriptor = TypeDescriptor.OBJECT; } Label?skipIfNullTarget = null; if (_nullSafe) { skipIfNullTarget = GenerateNullCheckCode(gen); } if (targetType.IsValueType) { if (stackDescriptor.IsBoxed || stackDescriptor.IsReferenceType) { gen.Emit(OpCodes.Unbox_Any, targetType); } var local = gen.DeclareLocal(targetType); gen.Emit(OpCodes.Stloc, local); gen.Emit(OpCodes.Ldloca, local); } else { if (stackDescriptor.Value != targetType) { CodeFlow.InsertCastClass(gen, new TypeDescriptor(targetType)); } } GenerateCodeForArguments(gen, cf, targetMethod, _children); if (targetType.IsValueType) { gen.Emit(OpCodes.Call, targetMethod); } else { gen.Emit(OpCodes.Callvirt, targetMethod); } if (_originalPrimitiveExitTypeDescriptor != null) { // The output of the accessor will be a primitive but from the block above it might be null, // so to have a 'common stack' element at skipIfNull target we need to box the primitive CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); } if (skipIfNullTarget.HasValue) { gen.MarkLabel(skipIfNullTarget.Value); } }
public void GenerateInitCode(string constantFieldName, ILGenerator gen, CodeFlow codeflow, bool nested = false) { LocalBuilder listLocal = null; if (!nested) { // Get list on stack GenerateLoadListCode(gen, constantFieldName); // Save to local for easy access listLocal = gen.DeclareLocal(typeof(IList)); gen.Emit(OpCodes.Stloc, listLocal); } else { // Create nested list to work with gen.Emit(OpCodes.Newobj, _listConstr); gen.Emit(OpCodes.Castclass, typeof(IList)); } var childCount = ChildCount; for (var c = 0; c < childCount; c++) { if (!nested) { gen.Emit(OpCodes.Ldloc, listLocal); } else { gen.Emit(OpCodes.Dup); } // The children might be further lists if they are not constants. In this // situation do not call back into generateCode() because it will register another clinit adder. // Instead, directly build the list here: if (_children[c] is InlineList list) { list.GenerateInitCode(constantFieldName, gen, codeflow, true); } else { _children[c].GenerateCode(gen, codeflow); var lastDesc = codeflow.LastDescriptor(); if (CodeFlow.IsValueType(lastDesc)) { CodeFlow.InsertBoxIfNecessary(gen, lastDesc); } } gen.Emit(OpCodes.Callvirt, _addMethod); // Ignore int return gen.Emit(OpCodes.Pop); } }
public override void GenerateCode(ILGenerator gen, CodeFlow cf) { // May reach here without it computed if all elements are literals ComputeExitTypeDescriptor(); cf.EnterCompilationScope(); _children[0].GenerateCode(gen, cf); var lastDesc = cf.LastDescriptor() ?? throw new InvalidOperationException("No last descriptor"); if (!CodeFlow.IsValueType(lastDesc)) { gen.Emit(OpCodes.Unbox_Any, typeof(bool)); } cf.ExitCompilationScope(); var elseTarget = gen.DefineLabel(); var endOfIfTarget = gen.DefineLabel(); gen.Emit(OpCodes.Brfalse, elseTarget); cf.EnterCompilationScope(); _children[1].GenerateCode(gen, cf); if (!CodeFlow.IsValueType(_exitTypeDescriptor)) { lastDesc = cf.LastDescriptor(); if (lastDesc == null) { throw new InvalidOperationException("No last descriptor"); } CodeFlow.InsertBoxIfNecessary(gen, lastDesc); } cf.ExitCompilationScope(); gen.Emit(OpCodes.Br, endOfIfTarget); gen.MarkLabel(elseTarget); cf.EnterCompilationScope(); _children[2].GenerateCode(gen, cf); if (!CodeFlow.IsValueType(_exitTypeDescriptor)) { lastDesc = cf.LastDescriptor(); if (lastDesc == null) { throw new InvalidOperationException("No last descriptor"); } CodeFlow.InsertBoxIfNecessary(gen, lastDesc); } cf.ExitCompilationScope(); gen.MarkLabel(endOfIfTarget); cf.PushDescriptor(_exitTypeDescriptor); }
public override void GenerateCode(ILGenerator gen, CodeFlow cf) { LeftOperand.GenerateCode(gen, cf); CodeFlow.InsertBoxIfNecessary(gen, cf.LastDescriptor()); if (_type == null) { throw new InvalidOperationException("No type available"); } var convert = gen.DeclareLocal(typeof(bool)); gen.Emit(OpCodes.Isinst, _type); gen.Emit(OpCodes.Ldnull); gen.Emit(OpCodes.Cgt_Un); gen.Emit(OpCodes.Stloc, convert); gen.Emit(OpCodes.Ldloc, convert); cf.PushDescriptor(_exitTypeDescriptor); }
public override void GenerateCode(ILGenerator gen, CodeFlow cf) { CodeFlow.LoadEvaluationContext(gen); var leftDesc = LeftOperand.ExitDescriptor; var rightDesc = RightOperand.ExitDescriptor; var leftPrim = CodeFlow.IsValueType(leftDesc); var rightPrim = CodeFlow.IsValueType(rightDesc); cf.EnterCompilationScope(); LeftOperand.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (leftPrim) { CodeFlow.InsertBoxIfNecessary(gen, leftDesc); } cf.EnterCompilationScope(); RightOperand.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (rightPrim) { CodeFlow.InsertBoxIfNecessary(gen, rightDesc); } // returns bool gen.Emit(OpCodes.Call, _equalityCheck); // Invert the boolean var result = gen.DeclareLocal(typeof(bool)); gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Ceq); gen.Emit(OpCodes.Stloc, result); gen.Emit(OpCodes.Ldloc, result); cf.PushDescriptor(TypeDescriptor.Z); }
public override void GenerateCode(ILGenerator gen, CodeFlow cf) { // exit type descriptor can be null if both components are literal expressions ComputeExitTypeDescriptor(); cf.EnterCompilationScope(); _children[0].GenerateCode(gen, cf); var lastDesc = cf.LastDescriptor(); if (lastDesc == null) { throw new InvalidOperationException("No last descriptor"); } // if primitive result, boxed will be on stack CodeFlow.InsertBoxIfNecessary(gen, lastDesc); cf.ExitCompilationScope(); var ifResult = gen.DeclareLocal(typeof(bool)); var finalResult = gen.DeclareLocal(_exitTypeDescriptor.Value); var loadFinalResult = gen.DefineLabel(); // Save off child1 result var child1Result = gen.DeclareLocal(typeof(object)); gen.Emit(OpCodes.Stloc, child1Result); var child1IsNull = gen.DefineLabel(); gen.Emit(OpCodes.Ldloc, child1Result); // br if child1 null gen.Emit(OpCodes.Brfalse, child1IsNull); // Check for empty string gen.Emit(OpCodes.Ldstr, string.Empty); gen.Emit(OpCodes.Ldloc, child1Result); gen.Emit(OpCodes.Callvirt, _equalsMethod); gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Ceq); // save empty string result gen.Emit(OpCodes.Stloc, ifResult); var loadCheckIfResults = gen.DefineLabel(); gen.Emit(OpCodes.Br, loadCheckIfResults); // Child1 null, load false for if result gen.MarkLabel(child1IsNull); gen.Emit(OpCodes.Ldc_I4_0); gen.Emit(OpCodes.Stloc, ifResult); // Fall thru to check if results; // Mark Check if Results gen.MarkLabel(loadCheckIfResults); // Load if results; gen.Emit(OpCodes.Ldloc, ifResult); var callChild2 = gen.DefineLabel(); // If faild, call child2 for results gen.Emit(OpCodes.Brfalse, callChild2); // Final result is child 1, save final gen.Emit(OpCodes.Ldloc, child1Result); gen.Emit(OpCodes.Stloc, finalResult); gen.Emit(OpCodes.Br, loadFinalResult); gen.MarkLabel(callChild2); cf.EnterCompilationScope(); _children[1].GenerateCode(gen, cf); if (!CodeFlow.IsValueType(_exitTypeDescriptor)) { lastDesc = cf.LastDescriptor(); if (lastDesc == null) { throw new InvalidOperationException("No last descriptor"); } if (lastDesc == TypeDescriptor.V) { gen.Emit(OpCodes.Ldnull); } else { CodeFlow.InsertBoxIfNecessary(gen, lastDesc); } } cf.ExitCompilationScope(); gen.Emit(OpCodes.Stloc, finalResult); // Load final result on stack gen.MarkLabel(loadFinalResult); gen.Emit(OpCodes.Ldloc, finalResult); cf.PushDescriptor(_exitTypeDescriptor); }