# sorear/niecza

### Subversion checkout URL

You can clone with
or
.
Fetching contributors…
Cannot retrieve contributors at this time
919 lines (822 sloc) 41.8 KB
 using System; using System.Reflection; using System.Reflection.Emit; using Niecza; using System.Collections.Generic; using System.Threading; namespace Niecza { class CLROpts { public static readonly bool Debug = Environment.GetEnvironmentVariable("NIECZA_CLR_TRACE") != null; public static readonly bool MMDDebug = Environment.GetEnvironmentVariable("NIECZA_MMD_TRACE") != null; } // These classes implement the basic C# multiple dispatch algorithm: // Candidates form a poset. Select the greatest element of the subset // filtered by admissability of the actual arguments. // // The actual algorithm starts by topologically sorting the candidates // $C_i$, that is, assigning $i$ values such that // $C_i > C_j \rightarrow i > j$. Assume there are no nontrivial equal // elements in the ordering. // // Now suppose $C_n$ is the last // admissible candidate (if there is none, then there is trivially no // greatest element). // // If there is a greatest element, then it is $C_n$. Proof. Suppose // $C_m$ is the greatest element. $C_m > C_i$ for any admissable $i$, // therefore $m >t i$ and $m$ is the last admissable index, therefore // equal to $n$. // // So all that remains is to check if $C_m$ is actually the greatest // element, that is to check that $C_p$ is inadmissable for all $p$ where // $C_p \nless C_m$. The case $p > m$ is already known; we keep a list // of possible conflictors, values of $p < m$ for all $m$. abstract class MultiCandidate { public abstract bool Admissable(Variable[] pos, VarHash named); public abstract int Compare(int arity, MultiCandidate other); public abstract bool AdmissableArity(int arity); public abstract int MinDispatchArity(); internal int[] conflictors; } class CandidateSet { MultiCandidate[][] cands; MultiCandidate[] orig; string name; public CandidateSet(string name, MultiCandidate[] orig) { this.orig = orig; this.name = name; int max_arity = 0; foreach (MultiCandidate mc in orig) { int mda = mc.MinDispatchArity(); if (mda > max_arity) max_arity = mda; } cands = new MultiCandidate[max_arity+1][]; } MultiCandidate[] GetCandidateList(int arity) { if (arity > cands.Length - 1) arity = cands.Length - 1; if (cands[arity] == null) { MultiCandidate[] n = SortCandidates(arity); // this is needed to ensure memory write ordering IIUC Interlocked.CompareExchange(ref cands[arity], n, null); return n; } else { return cands[arity]; } } // throws on dispatch failure public MultiCandidate DoDispatch(Variable[] pos, VarHash named) { MultiCandidate[] avail = GetCandidateList(pos.Length); int last_ix; for (last_ix = avail.Length - 1; last_ix >= 0; last_ix--) if (avail[last_ix].Admissable(pos, named)) break; if (last_ix < 0) { throw new NieczaException("No candidates for dispatch to " + name + "; candidates are:" + Console.Out.NewLine + " " + Kernel.JoinS(Console.Out.NewLine + " ", avail)); } foreach (int ci in avail[last_ix].conflictors) { if (avail[ci].Admissable(pos, named)) { List matched = new List(); foreach (MultiCandidate mc in avail) if (mc.Admissable(pos, named)) matched.Add(mc); throw new NieczaException("Ambiguous dispatch for " + name + "; matched candidates are:" + Console.Out.NewLine + " " + Kernel.JoinS(Console.Out.NewLine + " ", matched)); } } if (CLROpts.MMDDebug) Console.WriteLine("Using {0}", avail[last_ix]); return avail[last_ix]; } MultiCandidate[] SortCandidates(int arity) { List afilt = new List(); foreach (MultiCandidate mc in orig) if (mc.AdmissableArity(arity)) afilt.Add(mc); int n = afilt.Count; bool[] gt = new bool[n*n]; int[] blocks = new int[n]; // # of unused elements less than i int[] reorder = new int[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { int comp = afilt[i].Compare(arity, afilt[j]); if (CLROpts.MMDDebug && comp != 0) Console.WriteLine("{0} {1} {2}", afilt[i], (comp > 0 ? '>' : '<'), afilt[j]); if (comp > 0) { // $C_i > C_j$ gt[i*n+j] = true; blocks[i]++; } else if (comp < 0) { gt[j*n+i] = true; blocks[j]++; } } } int assigned = 0; while (assigned != n) { int i; for (i = 0; i < n; i++) if (blocks[i] == 0) break; reorder[assigned++] = i; for (int j = 0; j < n; j++) if (gt[j*n + i]) blocks[j]--; blocks[i] = int.MaxValue; } MultiCandidate[] ret = new MultiCandidate[n]; for (int i = 0; i < n; i++) { ret[i] = afilt[reorder[i]]; List conflicts = new List(); for (int j = 0; j < i; j++) { if (!gt[reorder[i]*n + reorder[j]]) conflicts.Add(j); } ret[i].conflictors = conflicts.ToArray(); } if (CLROpts.MMDDebug) { Console.WriteLine("--- MMD CANDIDATE SORT ORDER ---"); for (int i = 0; i < n; i++) { Console.WriteLine("{0}: {1}", i, ret[i]); Console.WriteLine(" c: {0}", Kernel.JoinS(" ", ret[i].conflictors)); } } return ret; } } sealed class PropertyProxy : Variable { PropertyInfo prop; object obj; object[] argv; public PropertyProxy(PropertyInfo prop, object obj, object[] argv) { this.rw = true; // make sure Fetch is called repeatedly this.islist = false; this.prop = prop; this.obj = obj; this.argv = argv; } public override P6any Fetch() { if (!prop.CanRead) throw new NieczaException("Property " + prop.Name + " is write-only"); MethodInfo mi = prop.GetGetMethod(); object ret = mi.Invoke(obj, argv); return CLRWrapperProvider.BoxResult(mi.ReturnType, ret).Fetch(); } public override void Store(P6any v) { if (!prop.CanWrite) throw new NieczaException("Property " + prop.Name + " is read-only"); MethodInfo mi = prop.GetSetMethod(); object[] argv_ = argv; Array.Resize(ref argv_, argv.Length + 1); if (!CLRWrapperProvider.CoerceArgument(out argv_[argv.Length], prop.PropertyType, Kernel.NewROScalar(v))) throw new NieczaException("Unable to coerce value of type " + v.mo.name + " for " + prop.Name); // could also be a range problem mi.Invoke(obj, argv_); } public override Variable GetVar() { return Kernel.BoxAnyMO(this, Kernel.ScalarMO); } public override void Freeze(Niecza.Serialization.FreezeBuffer fb) { throw new NotImplementedException(); } } sealed class FieldProxy : Variable { FieldInfo field; object obj; public FieldProxy(FieldInfo field, object obj) { this.rw = true; // make sure Fetch is called repeatedly this.islist = false; this.field = field; this.obj = obj; } public override P6any Fetch() { object ret = field.GetValue(obj); return CLRWrapperProvider.BoxResult(field.FieldType, ret).Fetch(); } public override void Store(P6any v) { if (field.IsInitOnly || field.IsLiteral) throw new NieczaException("Field " + field.Name + " is read-only"); object clr; if (!CLRWrapperProvider.CoerceArgument(out clr, field.FieldType, Kernel.NewROScalar(v))) throw new NieczaException("Unable to coerce value of type " + v.mo.name + " for " + field.Name); // could also be a range problem field.SetValue(obj, clr); } public override Variable GetVar() { return Kernel.BoxAnyMO(this, Kernel.ScalarMO); } public override void Freeze(Niecza.Serialization.FreezeBuffer fb) { throw new NotImplementedException(); } } class OverloadCandidate : MultiCandidate { MemberInfo what_call; Type[] args; bool[] refs; Type param_array; private OverloadCandidate(MemberInfo what_call, Type[] args, bool[] refs, Type param_array) { this.what_call = what_call; this.args = args; this.refs = refs; this.param_array = param_array; } public static void MakeCandidates(MemberInfo what, ParameterInfo[] pi, List into) { Type[] args1 = new Type[pi.Length]; bool[] refs = new bool[pi.Length]; for (int i = 0; i < pi.Length; i++) { args1[i] = pi[i].ParameterType; if (args1[i].IsByRef) { args1[i] = args1[i].GetElementType(); refs[i] = true; } } into.Add(new OverloadCandidate(what, args1, refs, null)); if (pi.Length != 0 && pi[pi.Length-1].GetCustomAttributes( typeof(ParamArrayAttribute), false).Length != 0) { Type[] args2 = new Type[args1.Length - 1]; Array.Copy(args1, 0, args2, 0, args2.Length); into.Add(new OverloadCandidate(what, args2, refs, args1[args1.Length - 1].GetElementType())); } } public override string ToString() { string s1 = Kernel.JoinS(", ", args); if (param_array != null) { return s1 + (s1 == "" ? "params " : ", params ") + param_array + "[]"; } else { return s1; } } void WritebackRefs(Variable[] pos, object[] argv) { for (int i = 0; i < args.Length; i++) if (refs[i]) pos[i].Store(CLRWrapperProvider.BoxResult(args[i], argv[i]).Fetch()); } public Variable Invoke(object obj, Variable[] pos, VarHash named) { object[] argv = new object[args.Length + (param_array != null ? 1 : 0)]; for (int i = 0; i < args.Length; i++) CLRWrapperProvider.CoerceArgument(out argv[i], args[i], pos[i]); if (param_array != null) { int npa = pos.Length - args.Length; Array pa = Array.CreateInstance(param_array, npa); for (int j = 0; j < npa; j++) { object arg; CLRWrapperProvider.CoerceArgument(out arg, param_array, pos[j + args.Length]); pa.SetValue(arg, j); } argv[args.Length] = pa; } if (what_call is MethodInfo) { MethodInfo mi = (MethodInfo) what_call; object ret = mi.Invoke((mi.IsStatic ? null : obj), argv); WritebackRefs(pos, argv); return CLRWrapperProvider.BoxResult(mi.ReturnType, ret); } else if (what_call is ConstructorInfo) { ConstructorInfo ci = (ConstructorInfo) what_call; object ret = ci.Invoke(null, argv); WritebackRefs(pos, argv); return CLRWrapperProvider.BoxResult(ci.DeclaringType, ret); } else if (what_call is FieldInfo) { return new FieldProxy((FieldInfo) what_call, obj); } else if (what_call is PropertyInfo) { return new PropertyProxy((PropertyInfo) what_call, obj, argv); } else { throw new NieczaException("Unhandled member type " + what_call.GetType()); } } public override bool Admissable(Variable[] pos, VarHash named) { if (named != null && named.IsNonEmpty) return false; if (!AdmissableArity(pos.Length)) return false; object dummy; for (int i = 0; i < args.Length; i++) if (!CLRWrapperProvider.CoerceArgument(out dummy, args[i], pos[i]) || (refs[i] && !pos[i].rw)) return false; // XXX: maybe param arrays should be treated as slurpies? for (int i = args.Length; i < pos.Length; i++) if (!CLRWrapperProvider.CoerceArgument(out dummy, param_array, pos[i])) return false; return true; } public override int Compare(int arity, MultiCandidate other_) { bool any_better = false, any_worse = false, any_diff = false; OverloadCandidate other = (OverloadCandidate) other_; for (int ix = 0; ix < arity; ix++) { Type t1 = ix >= args.Length ? param_array : args[ix]; Type t2 = ix >= other.args.Length ? other.param_array : other.args[ix]; int res = CompareType(t1, t2); if (t1 != t2) any_diff = true; if (res > 0) any_better = true; if (res < 0) any_worse = true; } if (any_better && !any_worse) return 1; if (any_worse && !any_better) return -1; if (!any_diff) { if (param_array == null && other.param_array != null) return 1; if (param_array != null && other.param_array == null) return -11; } return 0; } const int NUM_NUMTYPES = 12; [Immutable] static Type[] num_types = new Type[] { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float), typeof(double), typeof(decimal), }; // +1 if Y is a signed type shorter-or-equal to unsigned X, or // Y is implicitly convertable to X [Immutable] static sbyte[,] num_preced = new sbyte[,] { //sb ub ss us si ui sl ul ch sf df dc { 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 }, //sbyte { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 }, //byte { 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1 }, //short { 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1 }, //ushort { 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 }, //int { 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1 }, //uint { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 }, //long { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 }, //ulong { 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1 }, //char { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //float { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //double { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //decimal }; int CompareType(Type t1, Type t2) { int i1, i2; if (t1 == t2) return 0; for (i1 = 0; i1 < NUM_NUMTYPES && t1 != num_types[i1]; i1++) ; for (i2 = 0; i2 < NUM_NUMTYPES && t2 != num_types[i2]; i2++) ; if (i1 != NUM_NUMTYPES && i2 != NUM_NUMTYPES) return num_preced[i1,i2] - num_preced[i2,i1]; if (t1.IsAssignableFrom(t2)) return -1; if (t2.IsAssignableFrom(t1)) return 1; return 0; } public override bool AdmissableArity(int arity) { return param_array == null ? arity == args.Length : arity >= args.Length; } public override int MinDispatchArity() { return args.Length + 1; } } public class CLRWrapperProvider { [TrueGlobal] static object wrapper_cache_lock = new object(); [CompartmentGlobal] static Dictionary wrapper_cache; [CompartmentGlobal] static Dictionary named_wrapper_cache; public static STable GetWrapper(Type t) { lock (wrapper_cache_lock) { if (wrapper_cache == null) wrapper_cache = new Dictionary(); STable r; if (wrapper_cache.TryGetValue(t, out r)) return r; wrapper_cache[t] = r = NewWrapper(t); return r; } } public static STable GetNamedWrapper(string nm) { lock (wrapper_cache_lock) { if (wrapper_cache == null) wrapper_cache = new Dictionary(); if (named_wrapper_cache == null) named_wrapper_cache = new Dictionary(); STable r; if (named_wrapper_cache.TryGetValue(nm, out r)) return r; Type ty = Type.GetType(nm.Substring(1)); if (CLROpts.Debug) Console.WriteLine("Loading type {0} ... {1}", nm.Substring(1), ty == null ? "failed" : "succeeded"); if (ty != null) { wrapper_cache[ty] = r = NewWrapper(ty); named_wrapper_cache[nm] = r; } else { named_wrapper_cache[nm] = r = StashCursor.MakePackage( "CLR" + nm.Replace(".","::"), StashCursor.MakeCLR_WHO(nm)).Fetch().mo; } return r; } } static void MultiAdd(Dictionary> d, string n, MemberInfo mi, ParameterInfo[] pi) { List l; if (!d.TryGetValue(n, out l)) d[n] = l = new List(); OverloadCandidate.MakeCandidates(mi, pi, l); } static Frame Binder(Frame th) { Variable[] rpos = new Variable[th.pos.Length - 1]; Array.Copy(th.pos, 1, rpos, 0, rpos.Length); OverloadCandidate oc = (OverloadCandidate) (th.info.param[0] as CandidateSet).DoDispatch(rpos, th.named); th.caller.resultSlot = oc.Invoke(Kernel.UnboxAny
(P6any f) { return (TR)Callback(f, typeof(TR), new object[] { }, new Type[] { }); } public static void dv0(P6any f) { Callback(f, typeof(void), new object[] { }, new Type[] { }); } public static TR dnv1(P6any f, T0 a0) { return (TR)Callback(f, typeof(TR), new object[] { a0, }, new Type[] { typeof(T0), }); } public static void dv1(P6any f, T0 a0) { Callback(f, typeof(void), new object[] { a0, }, new Type[] { typeof(T0), }); } public static TR dnv2(P6any f, T0 a0, T1 a1) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1, }, new Type[] { typeof(T0),typeof(T1), }); } public static void dv2(P6any f, T0 a0, T1 a1) { Callback(f, typeof(void), new object[] { a0,a1, }, new Type[] { typeof(T0),typeof(T1), }); } public static TR dnv3(P6any f, T0 a0, T1 a1, T2 a2) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2, }, new Type[] { typeof(T0),typeof(T1),typeof(T2), }); } public static void dv3(P6any f, T0 a0, T1 a1, T2 a2) { Callback(f, typeof(void), new object[] { a0,a1,a2, }, new Type[] { typeof(T0),typeof(T1),typeof(T2), }); } public static TR dnv4(P6any f, T0 a0, T1 a1, T2 a2, T3 a3) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3), }); } public static void dv4(P6any f, T0 a0, T1 a1, T2 a2, T3 a3) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3), }); } public static TR dnv5(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3,a4, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4), }); } public static void dv5(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3,a4, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4), }); } public static TR dnv6(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3,a4,a5, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5), }); } public static void dv6(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3,a4,a5, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5), }); } public static TR dnv7(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3,a4,a5,a6, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6), }); } public static void dv7(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3,a4,a5,a6, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6), }); } public static TR dnv8(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3,a4,a5,a6,a7, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6),typeof(T7), }); } public static void dv8(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3,a4,a5,a6,a7, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6),typeof(T7), }); } public static TR dnv9(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { return (TR)Callback(f, typeof(TR), new object[] { a0,a1,a2,a3,a4,a5,a6,a7,a8, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6),typeof(T7),typeof(T8), }); } public static void dv9(P6any f, T0 a0, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { Callback(f, typeof(void), new object[] { a0,a1,a2,a3,a4,a5,a6,a7,a8, }, new Type[] { typeof(T0),typeof(T1),typeof(T2),typeof(T3),typeof(T4),typeof(T5),typeof(T6),typeof(T7),typeof(T8), }); } [Immutable] static MethodInfo[] delegate_methods; static CLRWrapperProvider() { delegate_methods = new MethodInfo[20]; foreach (MethodInfo mi in typeof(CLRWrapperProvider).GetMethods()) { string nm = mi.Name; if (nm[0] == 'd' && nm[1] == 'v') delegate_methods[2 * (nm[2] - '0')] = mi; if (nm[0] == 'd' && nm[1] == 'n' && nm[2] == 'v') delegate_methods[2 * (nm[3] - '0') + 1] = mi; } } static object Callback(P6any fun, Type ret, object[] args, Type[] aty) { Variable[] pos = new Variable[args.Length]; for (int i = 0; i < args.Length; i++) pos[i] = BoxResult(aty[i], args[i]); Variable retv = Kernel.RunInferior(fun.Invoke( Kernel.GetInferiorRoot(), pos, null)); if (ret == typeof(void)) return null; object reto; if (!CoerceArgument(out reto, ret, retv)) throw new Exception("Return value coercion failed, " + retv.Fetch().mo.name + " to " + ret.FullName); return reto; } static Frame default_handler(Frame th) { // XXX there HAS to be a better way to do this. STable mo = ((Variable)th.lex0).Fetch().mo; object obj = Array.CreateInstance(mo.box_type, 1).GetValue(0); th.caller.resultSlot = obj == null ? mo.typeVar : Kernel.BoxAnyMO
Something went wrong with that request. Please try again.