# sorear/niecza

### Subversion checkout URL

You can clone with
or
.
Fetching contributors…

Cannot retrieve contributors at this time

983 lines (875 sloc) 45.5 kB
 using System; using System.Reflection; using System.Reflection.Emit; using Niecza; using Niecza.Serialization; 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(Frame th, Variable[] pos, VarHash named); public abstract int Compare(int arity, MultiCandidate other); public abstract bool AdmissableArity(int arity); public abstract int MinDispatchArity(); public abstract Frame Invoke(Frame th, Variable[] pos, VarHash named); internal int[] conflictors; } class CandidateSet : IFreeze { object[] cands; // XXX no VolatileRead ? 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 object[max_arity+1]; } MultiCandidate[] GetCandidateList(int arity) { if (arity > cands.Length - 1) arity = cands.Length - 1; // No, really, we need a volatile read here to avoid seeing // stale data on systems like Alpha where "data-dependency fences" // are used. See the Linux RCU docs. var list = (MultiCandidate[])Thread.VolatileRead(ref cands[arity]); if (list == 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 list; } } static void CheckJunctionArg(Variable v, ref int jun_pivot, ref string jun_pivot_n, ref int jun_rank, int num, string nam) { P6any obj = v.Fetch(); if (!obj.mo.HasType(Kernel.JunctionMO)) return; int jrank = Kernel.UnboxAny((P6any) ((P6opaque)obj).slots[0]) / 2; if (jrank < jun_rank) { jun_rank = jrank; jun_pivot = num; jun_pivot_n = nam; } } static Frame CheckJunctions(Frame th, Variable[] pos, VarHash named, P6any junc_call) { int jun_pivot = -1; int jun_rank = int.MaxValue; string jun_pivot_n = null; for (int i = 0; i < pos.Length; i++) CheckJunctionArg(pos[i], ref jun_pivot, ref jun_pivot_n, ref jun_rank, i, null); if (named != null) { foreach (KeyValuePair kv in named) CheckJunctionArg(kv.Value, ref jun_pivot, ref jun_pivot_n, ref jun_rank, -2, kv.Key); } if (jun_pivot == -1) return null; Variable jct = (jun_pivot == -2 ? named[jun_pivot_n] : pos[jun_pivot]); Frame nth = th.MakeChild(null, Kernel.AutoThreadSubSI, Kernel.AnyP); P6opaque jo = (P6opaque) jct.Fetch(); nth.named = named; nth.pos = pos; nth.lex1 = Kernel.GetInfo(junc_call); nth.lex2 = jun_pivot_n; nth.lex3 = jo.slots[0]; nth.lex4 = Kernel.UnboxAny((P6any)jo.slots[1]); nth.lex5 = Kernel.GetOuter(junc_call); nth.lex6 = junc_call; nth.lex7 = null; nth.lex8 = new Variable[((Variable[])nth.lex4).Length]; nth.lex9 = jct; nth.lexi0 = jun_pivot; nth.lexi1 = 0; return nth; } // throws on dispatch failure public Frame DoDispatch(Frame th, Variable[] pos, VarHash named, P6any junc_call) { MultiCandidate[] avail = GetCandidateList(pos.Length); int last_ix; for (last_ix = avail.Length - 1; last_ix >= 0; last_ix--) if (avail[last_ix].Admissable(th, pos, named)) break; if (last_ix < 0) { Frame nth = CheckJunctions(th, pos, named, junc_call); if (nth != null) return nth; return Kernel.Die(th, "Cannot call " + name + "; none of these signatures match:" + Console.Out.NewLine + " " + Kernel.JoinS(Console.Out.NewLine + " ", orig)); } foreach (int ci in avail[last_ix].conflictors) { if (avail[ci].Admissable(th, pos, named)) { List matched = new List(); foreach (MultiCandidate mc in avail) if (mc.Admissable(th, pos, named)) matched.Add(mc); return Kernel.Die(th, "Ambiguous call to " + name + "; these signatures all match:" + Console.Out.NewLine + " " + Kernel.JoinS(Console.Out.NewLine + " ", matched)); } } if (CLROpts.MMDDebug) Console.WriteLine("Using {0}", avail[last_ix]); return avail[last_ix].Invoke(th, pos, named); } 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; } void IFreeze.Freeze(FreezeBuffer fb) { // anyone who holds a ref to one of these needs to recreate it. fb.Ephemeralize(); } } 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+1].Store(CLRWrapperProvider.BoxResult(args[i], argv[i]).Fetch()); } // pos[0] = self is not used for dispatch public override Frame Invoke(Frame th, 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+1]); if (param_array != null) { int npa = pos.Length - 1 - 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 + 1]); pa.SetValue(arg, j); } argv[args.Length] = pa; } object obj = 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) { if (th.ip == 0) { th.ip = 1; return Frame.Binder(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.