Exemple #1
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);
        }
        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);
            }
        }
Exemple #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);
            }
        }
Exemple #4
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();
        }
        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);
            }
        }
Exemple #6
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);
            }
        }
            private void GenerateCode(FieldInfo field, ILGenerator gen, CodeFlow cf)
            {
                var stackDescriptor = cf.LastDescriptor();

                if (stackDescriptor == null)
                {
                    CodeFlow.LoadTarget(gen);
                    stackDescriptor = Spring.TypeDescriptor.OBJECT;
                }

                if (!field.IsStatic)
                {
                    // Instance
                    if (field.DeclaringType.IsValueType)
                    {
                        if (stackDescriptor != null && stackDescriptor.IsBoxed)
                        {
                            gen.Emit(OpCodes.Unbox_Any, field.DeclaringType);
                        }
                    }
                    else
                    {
                        if (stackDescriptor == null || field.DeclaringType != stackDescriptor.Value)
                        {
                            gen.Emit(OpCodes.Castclass, field.DeclaringType);
                        }
                    }

                    gen.Emit(OpCodes.Ldfld, field);
                }
                else
                {
                    // Static
                    if (stackDescriptor != null)
                    {
                        // A static field/method call will not consume what is on the stack,
                        // it needs to be popped off.
                        gen.Emit(OpCodes.Pop);
                    }

                    if (field.IsLiteral)
                    {
                        EmitLiteralFieldCode(gen, field);
                    }
                    else
                    {
                        gen.Emit(OpCodes.Ldsfld, field);
                    }
                }
            }
            private void GenerateCode(MethodInfo method, ILGenerator gen, CodeFlow cf)
            {
                var stackDescriptor = cf.LastDescriptor();

                if (stackDescriptor == null)
                {
                    CodeFlow.LoadTarget(gen);
                    stackDescriptor = Spring.TypeDescriptor.OBJECT;
                }

                if (!method.IsStatic)
                {
                    // Instance
                    if (method.DeclaringType.IsValueType)
                    {
                        if (stackDescriptor != null && stackDescriptor.IsBoxed)
                        {
                            gen.Emit(OpCodes.Unbox_Any, method.DeclaringType);
                        }

                        var vtLocal = gen.DeclareLocal(method.DeclaringType);
                        gen.Emit(OpCodes.Stloc, vtLocal);
                        gen.Emit(OpCodes.Ldloca, vtLocal);
                        gen.Emit(OpCodes.Call, method);
                    }
                    else
                    {
                        if (stackDescriptor == null || method.DeclaringType != stackDescriptor.Value)
                        {
                            gen.Emit(OpCodes.Castclass, method.DeclaringType);
                        }

                        gen.Emit(OpCodes.Callvirt, method);
                    }
                }
                else
                {
                    // Static
                    if (stackDescriptor != null)
                    {
                        // A static field/method call will not consume what is on the stack,
                        // it needs to be popped off.
                        gen.Emit(OpCodes.Pop);
                    }

                    gen.Emit(OpCodes.Call, method);
                }
            }
        public void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf)
        {
            var descriptor = cf.LastDescriptor();

            if (descriptor == null || descriptor.Value != typeof(IDictionary))
            {
                if (descriptor == null)
                {
                    CodeFlow.LoadTarget(gen);
                }

                gen.Emit(OpCodes.Castclass, typeof(IDictionary));
            }

            gen.Emit(OpCodes.Ldstr, propertyName);
            gen.Emit(OpCodes.Callvirt, _getItem);
        }
Exemple #10
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);
        }
Exemple #11
0
        private void Walk(ILGenerator gen, CodeFlow cf, SpelNode operand)
        {
            if (operand is OpPlus plus)
            {
                Walk(gen, cf, plus.LeftOperand);
                Walk(gen, cf, plus.RightOperand);
            }
            else if (operand != null)
            {
                cf.EnterCompilationScope();
                operand.GenerateCode(gen, cf);
                if (cf.LastDescriptor() != TypeDescriptor.STRING)
                {
                    gen.Emit(OpCodes.Castclass, typeof(string));
                }

                cf.ExitCompilationScope();
                gen.Emit(OpCodes.Callvirt, _appendString);
            }
        }
Exemple #12
0
        public override void GenerateCode(ILGenerator gen, CodeFlow cf)
        {
            var descriptor = cf.LastDescriptor();

            if (descriptor == null)
            {
                CodeFlow.LoadTarget(gen);
            }

            if (_indexedType == IndexedType.ARRAY)
            {
                var arrayType = _exitTypeDescriptor.Value.MakeArrayType();
                gen.Emit(OpCodes.Castclass, arrayType);
                var child = _children[0];
                cf.EnterCompilationScope();
                child.GenerateCode(gen, cf);
                cf.ExitCompilationScope();
                gen.Emit(GetLdElemInsn(_exitTypeDescriptor.Value));
            }
            else if (_indexedType == IndexedType.LIST)
            {
                gen.Emit(OpCodes.Castclass, typeof(IList));
                cf.EnterCompilationScope();
                _children[0].GenerateCode(gen, cf);
                cf.ExitCompilationScope();
                gen.Emit(OpCodes.Callvirt, _listGetItemMethod);
            }
            else if (_indexedType == IndexedType.MAP)
            {
                gen.Emit(OpCodes.Castclass, typeof(IDictionary));

                // Special case when the key is an unquoted string literal that will be parsed as
                // a property/field reference
                if (_children[0] is PropertyOrFieldReference reference)
                {
                    var mapKeyName = reference.Name;
                    gen.Emit(OpCodes.Ldstr, mapKeyName);
                }
                else
                {
                    cf.EnterCompilationScope();
                    _children[0].GenerateCode(gen, cf);
                    cf.ExitCompilationScope();
                }

                gen.Emit(OpCodes.Callvirt, _dictionaryGetItemMethod);
            }
            else if (_indexedType == IndexedType.OBJECT)
            {
                if (_cachedReadAccessor is not ReflectivePropertyAccessor.OptimalPropertyAccessor accessor)
                {
                    throw new InvalidOperationException("No cached read accessor");
                }

                bool isStatic;
                var  method = accessor.Member as MethodInfo;
                var  field  = accessor.Member as FieldInfo;

                if (method != null)
                {
                    isStatic = method.IsStatic;
                }
                else
                {
                    isStatic = field.IsStatic;
                }

                var targetType = accessor.Member.DeclaringType;
                if (!isStatic && (descriptor == null || targetType != descriptor.Value))
                {
                    gen.Emit(OpCodes.Castclass, targetType);
                }

                if (method != null)
                {
                    if (isStatic)
                    {
                        gen.Emit(OpCodes.Call, method);
                    }
                    else
                    {
                        gen.Emit(OpCodes.Callvirt, method);
                    }
                }
                else
                {
                    if (isStatic)
                    {
                        gen.Emit(OpCodes.Ldsfld, field);
                    }
                    else
                    {
                        gen.Emit(OpCodes.Ldfld, field);
                    }
                }
            }

            cf.PushDescriptor(_exitTypeDescriptor);
        }
Exemple #13
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);
        }