Ejemplo n.º 1
0
        /// <summary>
        /// Emit IL for the given expression
        /// </summary>
        /// <typeparam name="TInLeft"></typeparam>
        /// <typeparam name="TInRight"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <typeparam name="TEmit"></typeparam>
        /// <param name="expr"></param>
        /// <param name="emitter"></param>
        /// <param name="errorLabel"></param>
        /// <param name="leftConst">Indicates if the left parameter is a constant</param>
        /// <param name="rightConst">Indicates if the right parameter is a constant</param>
        /// <returns>A tuple, indicating the type left on the stack by this expression and if the expression **is** potentially fallible (i.e. errorLabel may be jumped to)</returns>
        public static ConvertResult ConvertBinary <TInLeft, TInRight, TOut, TEmit>(
            this Expression <Func <TInLeft, TInRight, TOut> > expr,
            OptimisingEmitter <TEmit> emitter,
            ExceptionBlock errorLabel,
            Value?leftConst,
            Value?rightConst
            )
        {
            if (expr.ReturnType == typeof(StaticError))
            {
                return(new ConvertResult(typeof(StaticError), null, null));
            }

            // Try to convert expression without putting things into locals. Only works with certain expressions.
            var fast = TryConvertBinaryFastPath(expr, emitter, errorLabel, leftConst, rightConst);

            if (fast != null)
            {
                return(fast.Value);
            }

            // Put the parameters into local, ready to be used later
            using var localRight = emitter.DeclareLocal(typeof(TInRight), "ConvertBinary_Right", false);
            emitter.StoreLocal(localRight);
            using var localLeft = emitter.DeclareLocal(typeof(TInLeft), "ConvertBinary_Left", false);
            emitter.StoreLocal(localLeft);
            var parameters = new Dictionary <string, Parameter> {
                { expr.Parameters[0].Name !, new Parameter(localLeft, leftConst) },
Ejemplo n.º 2
0
 /// <summary>
 /// Call an instance method on a given type, taking zero arguments
 /// </summary>
 /// <typeparam name="TEmit"></typeparam>
 /// <typeparam name="TCallee"></typeparam>
 /// <param name="emitter"></param>
 /// <param name="methodName"></param>
 public static void CallRuntimeThis0 <TEmit, TCallee>(this OptimisingEmitter <TEmit> emitter, string methodName)
 {
     using (var local = emitter.DeclareLocal(typeof(TCallee), "CallRuntimeThis0_Callee", false))
     {
         emitter.StoreLocal(local);
         emitter.LoadLocalAddress(local, false);
         emitter.CallRuntimeN <TEmit, TCallee>(methodName);
     }
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Get the value of a field (even a private one) on a type
        /// </summary>
        /// <typeparam name="TEmit"></typeparam>
        /// <typeparam name="TType"></typeparam>
        /// <param name="emitter"></param>
        /// <param name="fieldName"></param>
        public static void GetRuntimeFieldValue <TEmit, TType>(this OptimisingEmitter <TEmit> emitter, string fieldName)
        {
            var field = typeof(TType).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static) !;

            using (var local = emitter.DeclareLocal(typeof(TType), "GetRuntimeFieldValue_Callee", false))
            {
                emitter.StoreLocal(local);
                emitter.LoadLocalAddress(local, true);
                emitter.LoadField(field);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Get the value of a property on a type
        /// </summary>
        /// <typeparam name="TEmit"></typeparam>
        /// <typeparam name="TType"></typeparam>
        /// <param name="emitter"></param>
        /// <param name="propertyName"></param>
        public static void GetRuntimePropertyValue <TEmit, TType>(this OptimisingEmitter <TEmit> emitter, string propertyName)
        {
            // ReSharper disable once PossibleNullReferenceException
            var method = typeof(TType).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static) !.GetMethod !;

            using (var local = emitter.DeclareLocal(typeof(TType), "GetRuntimePropertyValue_Callee", false))
            {
                emitter.StoreLocal(local);
                emitter.LoadLocalAddress(local, true);
                emitter.Call(method);
            }
        }
Ejemplo n.º 5
0
        private void IdentityConversionTest <T>()
        {
            var type = typeof(T).ToStackType();

            var emitter = Emit <Func <T, T> > .NewDynamicMethod(strictBranchVerification : true);

            var optim = new OptimisingEmitter <Func <T, T> >(emitter);

            optim.EmitCoerce(type, type);

            Assert.AreEqual(0, optim.InstructionCount);
        }
Ejemplo n.º 6
0
        private Func <TA, TB> CreateFunc <TA, TB>(Action <OptimisingEmitter <Func <TA, TB> > > act)
        {
            var emitter = Emit <Func <TA, TB> > .NewDynamicMethod(strictBranchVerification : true);

            using (var optim = new OptimisingEmitter <Func <TA, TB> >(emitter))
            {
                optim.LoadArgument(0);
                act(optim);
                optim.Return();
            }

            return(emitter.CreateDelegate());
        }
        public ArraySegmentMemoryAccessor(
            OptimisingEmitter <TEmit> emitter,
            ushort externalArraySegmentArg,
            ushort internalArraySegmentArg,
            IReadonlyInternalsMap internals,
            IReadonlyExternalsMap externals,
            IStaticTypeTracker types,
            Local?changeSet)
        {
            _emitter = emitter;
            _externalArraySegmentArg = externalArraySegmentArg;
            _internalArraySegmentArg = internalArraySegmentArg;
            _internals = internals;
            _externals = externals;
            _types     = types;
            _changeSet = changeSet;

            _cache      = new Dictionary <VariableName, TypedLocal>();
            _cacheDirty = new Dictionary <VariableName, Local>();
            _mutated    = new HashSet <VariableName>();
        }
Ejemplo n.º 8
0
            /// <summary>
            /// Emit code to dynamically check for errors. If an error would occur clear out the stack and jump away to the given label
            /// </summary>
            /// <typeparam name="TEmit"></typeparam>
            /// <param name="emitter"></param>
            /// <param name="errorLabel"></param>
            /// <param name="parameters"></param>
            public void EmitDynamicWillThrow <TEmit>(OptimisingEmitter <TEmit> emitter, Compiler.Emitter.Instructions.ExceptionBlock errorLabel, IReadOnlyList <Local> parameters)
            {
                // Load parameters back onto stack
                for (var i = parameters.Count - 1; i >= 0; i--)
                {
                    emitter.LoadLocal(parameters[i]);
                }

                // Invoke the `will throw` method to discover if this invocation would trigger a runtime error
                emitter.Call(WillThrow);

                // Create a label to jump past the error handling for the normal case
                var noThrowLabel = emitter.DefineLabel();

                // Jump past error handling if this is ok
                emitter.BranchIfFalse(noThrowLabel);

                // If execution reaches here it means an error would occur in this operation
                emitter.Leave(errorLabel);

                emitter.MarkLabel(noThrowLabel);
            }
Ejemplo n.º 9
0
        /// <summary>
        /// Call a method on the `Runtime` class, taking a given set of arguments
        /// </summary>
        /// <typeparam name="TEmit"></typeparam>
        /// <param name="emitter"></param>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        public static void CallRuntimeN <TEmit>(this OptimisingEmitter <TEmit> emitter, string methodName, params Type[] args)
        {
            var method = typeof(Runtime).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static, null, args, null) !;

            emitter.Call(method);
        }
Ejemplo n.º 10
0
 /// <summary>
 /// Coerce an object of the given type into another type
 /// </summary>
 /// <typeparam name="TEmit"></typeparam>
 /// <param name="emitter"></param>
 /// <param name="input"></param>
 /// <param name="output"></param>
 public static void EmitCoerce <TEmit>(this OptimisingEmitter <TEmit> emitter, StackType input, StackType output)
 {
     switch (input, output)
     {
Ejemplo n.º 11
0
        /// <summary>
        /// Compile a line of Yolol into the given IL emitter
        /// </summary>
        /// <param name="line"></param>
        /// <param name="emit"></param>
        /// <param name="lineNumber"></param>
        /// <param name="maxLines"></param>
        /// <param name="maxStringLength"></param>
        /// <param name="internalVariableMap"></param>
        /// <param name="externalVariableMap"></param>
        /// <param name="staticTypes"></param>
        /// <param name="changeDetection"></param>
        public static void Compile(
            this Line line,
            Emit <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> > emit,
            int lineNumber,
            int maxLines,
            int maxStringLength,
            IReadonlyInternalsMap internalVariableMap,
            IReadonlyExternalsMap externalVariableMap,
            IReadOnlyDictionary <VariableName, Type>?staticTypes = null,
            bool changeDetection = false
            )
        {
            using (var emitter = new OptimisingEmitter <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >(emit))
            {
                void EmitFallthroughCalc()
                {
                    if (lineNumber == maxLines)
                    {
                        emitter.LoadConstant(1);
                    }
                    else
                    {
                        emitter.LoadConstant(lineNumber + 1);
                    }
                }

                // Special case for totally empty lines
                if (line.Statements.Statements.Count == 0)
                {
                    EmitFallthroughCalc();
                    emitter.LoadConstant(0ul);
                    emitter.NewObject <ChangeSet, ulong>();
                    emitter.NewObject(typeof(LineResult).GetConstructor(new[] { typeof(int), typeof(ChangeSet) }) !);
                    emitter.Return();
                    return;
                }

                // Begin an exception block to catch Yolol runtime errors
                var exBlock = emitter.BeginExceptionBlock();

                // Create a local to store the return address from inside the try/catch block
                var retAddr = emitter.DeclareLocal <int>("ret_addr", initializeReused: false);

                // Create a local for the change bit set
                var changeSet = changeDetection ? emitter.DeclareLocal <ulong>("change_set") : null;

                // Store the default return address to go to
                EmitFallthroughCalc();
                emitter.StoreLocal(retAddr);

                // Create a label which any `goto` statements can use. They drop their destination PC on the stack and then jump to this label
                var gotoLabel = emitter.DefineLabel2("encountered_goto");

                var types = new StaticTypeTracker(staticTypes);

                // Create a memory accessor which manages reading and writing the memory arrays
                using (var accessor = new ArraySegmentMemoryAccessor <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >(
                           emitter,
                           1,
                           0,
                           internalVariableMap,
                           externalVariableMap,
                           types,
                           changeSet
                           ))
                {
                    accessor.EmitLoad(line);

                    // Convert the entire line into IL
                    var converter = new ConvertLineVisitor <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >(emitter, maxLines, accessor, exBlock, gotoLabel, types, maxStringLength);
                    converter.Visit(line);

                    // When a line finishes (with no gotos in the line) call flow eventually reaches here. Go to the next line.
                    emitter.Leave(exBlock);

                    // Create a block to handle gotos. The destination will already be on the stack, so just return
                    if (gotoLabel.IsUsed)
                    {
                        emitter.MarkLabel(gotoLabel);
                        emitter.StoreLocal(retAddr);
                        emitter.Leave(exBlock);
                    }

                    // Catch all execution exceptions and return the appropriate next line number to fall through to
                    var catchBlock = emitter.BeginCatchBlock <ExecutionException>(exBlock);
#if DEBUG
                    using (var ex = emitter.DeclareLocal(typeof(ExecutionException), initializeReused: false))
                    {
                        emitter.StoreLocal(ex);
                        emitter.WriteLine("execution exception: {0}", ex);
                    }
#else
                    emitter.Pop();
#endif

                    // Close the exception block which was wrapping the entire method
                    emitter.EndCatchBlock(catchBlock);
                    emitter.EndExceptionBlock(exBlock);
                }

                // Load the return address from inside the catch block
                emitter.LoadLocal(retAddr);

                // Create the change set, either from the computer value or the default value (which indicates that everything has changed)
                if (changeSet != null)
                {
                    emitter.LoadLocal(changeSet);
                }
                else
                {
                    emitter.LoadConstant(ulong.MaxValue);
                }
                emitter.NewObject <ChangeSet, ulong>();

                emitter.NewObject(typeof(LineResult).GetConstructor(new[] { typeof(int), typeof(ChangeSet) }) !);
                emitter.Return();

                emitter.Optimise();

#if DEBUG
                Console.WriteLine($"// {line}");
                Console.WriteLine(emitter.ToString());
                Console.WriteLine($"------------------------------");
#endif
            }
        }