254 lines
8.9 KiB
C#
254 lines
8.9 KiB
C#
|
|
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<int> 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>(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<int, int> 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<int>(1, 2)).Append(';');
|
||
|
|
sb.Append(Ops.Pair<string>("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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|