Пример #1
0
        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);
        }
Пример #2
0
        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();
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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;
     }
 }
Пример #6
0
        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;
                }
            }
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }