extern alias LA; extern alias LB; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; namespace Fx { public interface IShape { int Area(int scale); string Describe(string prefix); } public delegate int Combine(int a, int b); public abstract class ShapeBase { public abstract int Sides(); public virtual int Weight(int density) { return density * 2; } } public class Square : ShapeBase, IShape { private int _side; // ctor: excluded public Square(int side) { _side = side; } static Square() { Origin = 7; } public static int Origin; // implicit interface impl: virtual newslot, excluded public int Area(int scale) { return _side * _side * scale; } public string Describe(string prefix) { return prefix + ":square:" + _side; } // abstract impl: excluded public override int Sides() { return 4; } // virtual override: excluded public override int Weight(int density) { return density * 3; } // property accessors: excluded public int Side { get { return _side; } set { _side = value; } } // event accessors: excluded public event Action Resized; public void RaiseResized(int v) { Resized?.Invoke(v); } // ordinary instance method: PADDED public int Scaled(int factor, int offset) { return _side * factor + offset; } // zero arg instance method: PADDED public int Bare() { return _side; } } public class Explicit : IShape { // explicit interface impl: excluded int IShape.Area(int scale) { return scale; } string IShape.Describe(string prefix) { return prefix + ":explicit"; } } public static class Ops { // taken with ldftn via method group conversion: excluded public static int Add(int a, int b) { return a + b; } // PADDED, ordinary statics public static int Mul(int a, int b) { return a * b; } public static int Neg(int a) { return -a; } public static int Zero() { return 0; } // PADDED, called as a nested argument and as a branch target public static int Clamp(int v, int lo, int hi) { return v < lo ? lo : (v > hi ? hi : v); } // PADDED, recursion public static int Fact(int n) { return n <= 1 ? 1 : n * Fact(n - 1); } // PADDED, byref parameters public static void Split(int v, out int lo, ref int hi, in int bump) { lo = v & 0xFF; hi = (v >> 8) + bump; } // PADDED, generic public static string Pair(T a, T b) { return a + "|" + b; } // PADDED, many argument types public static string Mixed(byte b, short s, long l, float f, double d, char c, bool t, string str) { return b + "/" + s + "/" + l + "/" + f.ToString("F1") + "/" + d.ToString("F1") + "/" + c + "/" + t + "/" + str; } // PADDED, called inside a try/catch and throws public static int Boom(int v) { if (v > 0) throw new InvalidOperationException("boom" + v); return v; } // PADDED, loop with a backward branch over a call public static int Sum(int n) { int acc = 0; for (int i = 0; i < n; i++) { acc = Add3(acc, i); } return acc; } // PADDED, called from the loop above public static int Add3(int a, int b) { return a + b; } // whole body wrapped in try/catch: the consume prologue must not branch into the // protected region public static int Guarded(int v) { try { return 100 / v; } catch (DivideByZeroException) { return -1; } finally { Touched++; } } public static int Touched; // try block starting at the very first instruction, with a nested call public static string Wrapped(int a, int b) { try { return "w" + Mul(a, b); } catch (Exception e) { return e.Message; } } // Two DIFFERENT types that share a namespace-qualified name. Spilling both at the same // ordinal must not reuse one local: TypeSig.FullName omits the assembly, so keying on it // would type the second spill as the first type. public static int TakeA(LA::Shared.Thing t, int n) { return t.V + n; } public static int TakeB(LB::Shared.Thing t, int n) { return t.V * n; } public static int SameName() { return TakeA(new LA::Shared.Thing(1), 2) + TakeB(new LB::Shared.Thing(3), 4); } // Arguments must still be EVALUATED left to right even though they are PUSHED in a // permuted order. Tick records evaluation order; Order records value routing. public static string OrderLog = ""; public static int Tick(int n) { OrderLog += n; return n; } public static int Order(int a, int b, int c) { return a * 100 + b * 10 + c; } // ldtoken / reflection target: excluded public static int Reflected(int a) { return a + 1000; } public static string UseReflection() { var m = typeof(Ops).GetMethod("Reflected"); return m == null ? "null" : m.Name + ":" + m.GetParameters().Length; } // [DllImport]: excluded (no body, never a candidate) [DllImport("nonexistent", EntryPoint = "never_called")] public static extern int Native(int a); public static int UseDelegate(int a, int b) { Combine c = Add; // ldftn Ops::Add Func lam = x => x * 5; // ldftn on the lambda return c(a, b) + lam(a); } } [Serializable] public class Payload { public int Value; public static int AfterCount; // located by attribute, so the rename policy permits renaming it - but BinaryFormatter and // Newtonsoft both validate the signature, so its argument list must not change. [OnDeserialized] internal void AfterLoad(StreamingContext ctx) { AfterCount++; } [OnSerializing] internal void BeforeSave(StreamingContext ctx) { AfterCount += 2; } } public class Counter { private int _n; public int Step(int by) { _n += by; return _n; } public int Value() { return _n; } } public static class Entry { public static string RunAll() { var sb = new StringBuilder(); var sq = new Square(5); sb.Append(sq.Area(2)).Append(';'); sb.Append(sq.Describe("p")).Append(';'); sb.Append(sq.Sides()).Append(';'); sb.Append(sq.Weight(4)).Append(';'); sq.Side = 6; sb.Append(sq.Side).Append(';'); sb.Append(sq.Scaled(3, 1)).Append(';'); sb.Append(sq.Bare()).Append(';'); sb.Append(Square.Origin).Append(';'); int captured = -1; sq.Resized += v => captured = v; sq.RaiseResized(42); sb.Append(captured).Append(';'); IShape ex = new Explicit(); sb.Append(ex.Area(9)).Append(';'); sb.Append(ex.Describe("q")).Append(';'); sb.Append(Ops.Mul(6, 7)).Append(';'); sb.Append(Ops.Neg(11)).Append(';'); sb.Append(Ops.Zero()).Append(';'); // nested call as an argument, exercises overlapping spills sb.Append(Ops.Clamp(Ops.Mul(3, 40), 10, 100)).Append(';'); sb.Append(Ops.Clamp(5, Ops.Neg(-20), Ops.Mul(5, 5))).Append(';'); sb.Append(Ops.Fact(6)).Append(';'); sb.Append(Ops.Sum(10)).Append(';'); int lo, hi = 3, bump = 4; Ops.Split(0x1234, out lo, ref hi, in bump); sb.Append(lo).Append(',').Append(hi).Append(';'); sb.Append(Ops.Pair(1, 2)).Append(';'); sb.Append(Ops.Pair("a", "b")).Append(';'); sb.Append(Ops.Mixed(1, -2, 3L, 4.5f, 6.5, 'z', true, "s")).Append(';'); try { Ops.Boom(3); } catch (InvalidOperationException e) { sb.Append(e.Message).Append(';'); } sb.Append(Ops.Boom(0)).Append(';'); Ops.OrderLog = ""; int ordered = Ops.Order(Ops.Tick(1), Ops.Tick(2), Ops.Tick(3)); sb.Append(ordered).Append(',').Append(Ops.OrderLog).Append(';'); sb.Append(Ops.SameName()).Append(';'); sb.Append(Ops.Guarded(4)).Append(';'); sb.Append(Ops.Guarded(0)).Append(';'); sb.Append(Ops.Touched).Append(';'); sb.Append(Ops.Wrapped(3, 4)).Append(';'); sb.Append(Ops.UseDelegate(2, 3)).Append(';'); sb.Append(Ops.UseReflection()).Append(';'); var c = new Counter(); for (int i = 0; i < 4; i++) { c.Step(i); } sb.Append(c.Value()).Append(';'); return sb.ToString(); } } }