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(); }
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); }
protected internal void SetExitTypeDescriptor(TypeDescriptor descriptor) { // If this property or field access would return a primitive - and yet // it is also marked null safe - then the exit type descriptor must be // promoted to the box type to allow a null value to be passed on if (_nullSafe && CodeFlow.IsValueType(descriptor)) { _originalPrimitiveExitTypeDescriptor = descriptor; _exitTypeDescriptor = CodeFlow.ToBoxedDescriptor(descriptor); } else { _exitTypeDescriptor = descriptor; } }
private void UpdateExitTypeDescriptor(object result) { var executorToCheck = _cachedExecutor; if (executorToCheck != null && executorToCheck.Get() is ReflectiveMethodExecutor executor) { var method = executor.Method; var descriptor = ComputeExitDescriptor(result, method.ReturnType); if (_nullSafe && CodeFlow.IsValueType(descriptor)) { _originalPrimitiveExitTypeDescriptor = descriptor; _exitTypeDescriptor = CodeFlow.ToBoxedDescriptor(descriptor); } else { _exitTypeDescriptor = descriptor; } } }
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); }
protected void GenerateComparisonCode(ILGenerator gen, CodeFlow cf, OpCode brToElseInstruction) { var left = LeftOperand; var right = RightOperand; var leftDesc = left.ExitDescriptor; var rightDesc = right.ExitDescriptor; var elseTarget = gen.DefineLabel(); var endOfIfTarget = gen.DefineLabel(); var unboxLeft = !CodeFlow.IsValueType(leftDesc); var unboxRight = !CodeFlow.IsValueType(rightDesc); cf.EnterCompilationScope(); left.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (CodeFlow.IsValueType(leftDesc)) { gen.Emit(OpCodes.Box, leftDesc.Value); unboxLeft = true; } cf.EnterCompilationScope(); right.GenerateCode(gen, cf); cf.ExitCompilationScope(); if (CodeFlow.IsValueType(rightDesc)) { gen.Emit(OpCodes.Box, rightDesc.Value); unboxRight = true; } var leftLocal = gen.DeclareLocal(typeof(object)); var rightLocal = gen.DeclareLocal(typeof(object)); gen.Emit(OpCodes.Stloc, rightLocal); gen.Emit(OpCodes.Stloc, leftLocal); gen.Emit(OpCodes.Ldloc, leftLocal); gen.Emit(OpCodes.Ldloc, rightLocal); // This code block checks whether the left or right operand is null and handles // those cases before letting the original code (that only handled actual numbers) run var rightIsNonNullTarget = gen.DefineLabel(); // stack: left/right gen.Emit(OpCodes.Brtrue, rightIsNonNullTarget); // stack: left // here: RIGHT==null LEFT==unknown var leftNotNullRightIsNullTarget = gen.DefineLabel(); gen.Emit(OpCodes.Brtrue, leftNotNullRightIsNullTarget); // stack: empty // here: RIGHT==null LEFT==null // load 0 or 1 depending on comparison instruction if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Ble) { gen.Emit(OpCodes.Ldc_I4_0); } else if (brToElseInstruction == OpCodes.Bgt || brToElseInstruction == OpCodes.Blt) { gen.Emit(OpCodes.Ldc_I4_1); } else { throw new InvalidOperationException("Unsupported: " + brToElseInstruction); } gen.Emit(OpCodes.Br, endOfIfTarget); gen.MarkLabel(leftNotNullRightIsNullTarget); // stack: empty // RIGHT==null LEFT!=null // load 0 or 1 depending on comparison instruction if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Bgt) { gen.Emit(OpCodes.Ldc_I4_0); } else if (brToElseInstruction == OpCodes.Ble || brToElseInstruction == OpCodes.Blt) { gen.Emit(OpCodes.Ldc_I4_1); } else { throw new InvalidOperationException("Unsupported: " + brToElseInstruction); } gen.Emit(OpCodes.Br, endOfIfTarget); gen.MarkLabel(rightIsNonNullTarget); // stack: left // here: RIGHT!=null LEFT==unknown var neitherRightNorLeftAreNullTarget = gen.DefineLabel(); gen.Emit(OpCodes.Brtrue, neitherRightNorLeftAreNullTarget); // stack: empty // here: RIGHT!=null LEFT==null if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Bgt) { gen.Emit(OpCodes.Ldc_I4_1); } else if (brToElseInstruction == OpCodes.Ble || brToElseInstruction == OpCodes.Blt) { gen.Emit(OpCodes.Ldc_I4_0); } else { throw new InvalidOperationException("Unsupported: " + brToElseInstruction); } gen.Emit(OpCodes.Br, endOfIfTarget); gen.MarkLabel(neitherRightNorLeftAreNullTarget); // stack: empty // neither were null so unbox and proceed with numeric comparison gen.Emit(OpCodes.Ldloc, leftLocal); if (unboxLeft) { gen.Emit(OpCodes.Unbox_Any, leftDesc.Value); } // stack: left gen.Emit(OpCodes.Ldloc, rightLocal); if (unboxRight) { gen.Emit(OpCodes.Unbox_Any, rightDesc.Value); } // stack: left, right // Br instruction gen.Emit(brToElseInstruction, elseTarget); // Stack: Empty gen.Emit(OpCodes.Ldc_I4_1); gen.Emit(OpCodes.Br, endOfIfTarget); gen.MarkLabel(elseTarget); // Stack: Empty gen.Emit(OpCodes.Ldc_I4_0); gen.MarkLabel(endOfIfTarget); // Stack: result on stack, convert to bool var result = gen.DeclareLocal(typeof(bool)); 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); }