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);
            }
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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);
            }
        }
示例#4
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);
        }
示例#5
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();
        }
示例#6
0
        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);
            }
        }
示例#7
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);
            }
        }
示例#8
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);
        }
示例#9
0
        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);
        }
示例#10
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);
        }
示例#11
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);
        }