예제 #1
0
파일: CilReader.cs 프로젝트: Sable/McCli
        /// <summary>
        /// Reads a single CIL instruction from a given data stream.
        /// </summary>
        /// <param name="stream">The source data stream.</param>
        /// <param name="instruction">The instruction that was read, undefined if the method returns zero.</param>
        /// <returns>A value indicating if an instruction was read, <c>false</c> if the end of the stream was reached.</returns>
        public static bool ReadInstruction(Stream stream, out RawInstruction instruction)
        {
            Contract.Requires(stream != null && stream.CanRead);

            int firstOpcodeByte = stream.ReadByte();

            if (unchecked ((byte)firstOpcodeByte) != firstOpcodeByte)
            {
                instruction = default(RawInstruction);
                return(false);
            }

            OpcodeValue opcodeValue;

            if (OpcodeValueEnum.IsFirstOfTwoBytes((byte)firstOpcodeByte))
            {
                opcodeValue = (OpcodeValue)((firstOpcodeByte << 8) | stream.ReadUInt8());
            }
            else
            {
                opcodeValue = (OpcodeValue)firstOpcodeByte;
            }

            var opcode = Opcode.FromValue(opcodeValue);

            if (opcode == Opcode.Switch)
            {
                // Read jump table
                int[] jumpTable = new int[stream.ReadUInt32()];
                for (int i = 0; i < jumpTable.Length; ++i)
                {
                    jumpTable[i] = stream.ReadInt32();
                }
                instruction = RawInstruction.CreateSwitch(jumpTable);
            }
            else
            {
                NumericalOperand operand;
                switch ((int)opcode.OperandKind.GetSizeInBytes())
                {
                case 0: operand = default(NumericalOperand); break;

                case 1: operand = stream.ReadInt8(); break;

                case 2: operand = stream.ReadInt16(); break;

                case 4: operand = stream.ReadInt32(); break;

                case 8: operand = stream.ReadInt64(); break;

                default: throw new NotSupportedException("Unexpected opcode operand size");
                }
                instruction = new RawInstruction(opcode, operand);
            }

            return(true);
        }
예제 #2
0
        private void EmitCall(ImmutableArray <Variable> targets, FunctionMethod function, ImmutableArray <Variable> arguments)
        {
            var signature = function.Signature;

            Contract.Assert(arguments.Length == signature.Inputs.Count);
            Contract.Assert(targets.Length <= signature.Outputs.Count);

            // Prepare the eventual return value store
            if (signature.HasReturnValue && targets.Length == 1)
            {
                BeginEmitStore(targets[0]);
            }

            // Push the input arguments
            for (int i = 0; i < arguments.Length; ++i)
            {
                var argument = arguments[i];
                EmitLoad(argument);
                EmitConvert(argument.StaticRepr, signature.Inputs[i]);
            }

            if (signature.OutParameterCount > 0)
            {
                // Push the pointers to the output arguments
                for (int i = 0; i < signature.Outputs.Count; ++i)
                {
                    if (i < targets.Length)
                    {
                        var target   = targets[i];
                        var location = GetLocation(target);
                        if (declaration.Outputs.Contains(target) && declaration.Outputs.Length >= 2)
                        {
                            cil.Load(location);                             // Target is a ByRef parameter, so already a (managed) pointer
                        }
                        else
                        {
                            cil.LoadAddress(location);
                        }
                    }
                    else
                    {
                        throw new NotImplementedException("Ignored outputs of a multi-output function.");
                    }
                }
            }

            // Call the function
            var method = function.Method;

            if (method is System.Reflection.Emit.MethodBuilder)
            {
                // We have to supply the parameter types ourselves since MethodBodyWriter
                // can't call MethodBuilder.GetParameters() without receiving an exception.
                cil.Call(Opcode.GetDefaultCall(method), method,
                         function.Signature.GetParameterCliTypes(), function.Signature.ReturnCliType);
            }
            else
            {
                // We're calling a builtin
                var builtinCilOpcodeAttribute = method.GetCustomAttribute <BuiltinCilOpcodeAttribute>();
                if (builtinCilOpcodeAttribute == null)
                {
                    cil.Invoke(method);
                }
                else
                {
                    // Calling the builtin is equivalent to emitting a sequence of CIL opcodes, so do that instead
                    foreach (var opcodeValue in builtinCilOpcodeAttribute.Opcodes)
                    {
                        var opcode = Opcode.FromValue((OpcodeValue)opcodeValue);
                        Contract.Assert(opcode != null);
                        cil.Instruction(opcode);
                    }
                }
            }

            // Handle the return value, if any
            if (signature.HasReturnValue)
            {
                if (targets.Length == 1)
                {
                    EmitConvert(signature.Outputs[0], targets[0].StaticRepr);
                    EndEmitStore(targets[0]);
                }
                else
                {
                    // Return value ignored.
                    Contract.Assert(targets.Length == 0);
                    cil.Pop();
                }
            }
        }