Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: c3c572b1ca
Fetching contributors…

Cannot retrieve contributors at this time

6651 lines (5979 sloc) 269.351 kB
using System;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.IO;
using Niecza.CLRBackend;
using Niecza.Serialization;
namespace Niecza {
// We like to reuse continuation objects for speed - every function only
// creates one kind of continuation, but tweaks a field for exact return
// point. As such, call frames and continuations are in 1:1 correspondence
// and are unified. Functions take a current continuation and return a new
// continuation; we tail recurse with trampolines.
// Used by DynFrame to plug in code
public delegate Frame DynBlockDelegate(Frame frame);
public sealed class DispatchEnt : IFreeze {
public DispatchEnt next;
public SubInfo info;
public Frame outer;
public P6any ip6;
public DispatchEnt() {}
public DispatchEnt(DispatchEnt next, P6any ip6) {
this.ip6 = ip6;
this.next = next;
P6opaque d = (P6opaque)ip6;
this.outer = (Frame) d.slots[0];
this.info = (SubInfo) d.slots[1];
}
public DispatchEnt(DispatchEnt next) { this.next = next; }
void IFreeze.Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.DispatchEnt);
fb.ObjRef(next);
fb.ObjRef(info);
fb.ObjRef(outer);
fb.ObjRef(ip6);
}
internal static DispatchEnt Thaw(ThawBuffer tb) {
DispatchEnt de = new DispatchEnt();
tb.Register(de);
de.next = (DispatchEnt) tb.ObjRef();
de.info = (SubInfo) tb.ObjRef();
de.outer = (Frame) tb.ObjRef();
de.ip6 = (P6any) tb.ObjRef();
return de;
}
}
// A Variable is the meaning of function arguments, of any subexpression
// except the targets of := and ::=.
public abstract class Variable : IFreeze {
public const int RO = 0;
public const int RW = 1;
public const int LIST = 2;
public virtual int Mode { get { return (this is ListVariable) ? LIST : (this is P6any) ? RO : RW; } }
public bool Rw { get { return !(this is P6any); } }
public bool List { get { return this is ListVariable; } }
public virtual void Vivify() { }
public virtual Variable Assign(Variable inp) {
Store(inp.Fetch());
return this;
}
public virtual Variable AssignO(P6any inp, bool listishly) {
Store(inp);
return this;
}
public abstract P6any Fetch();
public abstract void Store(P6any v);
public virtual Variable GetVar() {
return Kernel.BoxAnyMO<Variable>(this, Compartment.Top.ScalarMO);
}
public abstract void Freeze(FreezeBuffer fb);
// note: callers need to make sure type is set up properly if null
public Variable() { }
[Immutable]
public static readonly Variable[] None = new Variable[0];
}
public abstract class ViviHook : IFreeze {
public abstract void Freeze(FreezeBuffer fb);
public abstract void Do(Variable toviv);
}
public class SubViviHook : ViviHook {
P6any sub;
public SubViviHook(P6any sub) { this.sub = sub; }
public override void Do(Variable toviv) {
Kernel.RunInferior(sub.Invoke(Kernel.GetInferiorRoot(),
new Variable[] { toviv }, null));
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.SubViviHook);
fb.ObjRef(sub);
}
internal static object Thaw(ThawBuffer tb) {
var n = new SubViviHook(null);
tb.Register(n);
n.sub = (P6any) tb.ObjRef();
return n;
}
}
public class HashViviHook : ViviHook {
P6any hash;
string key;
public HashViviHook(P6any hash, string key) { this.hash = hash; this.key = key; }
public override void Do(Variable toviv) {
VarHash rh = Kernel.UnboxAny<VarHash>(hash);
rh[key] = toviv;
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.HashViviHook);
fb.ObjRef(hash);
fb.String(key);
}
internal static IFreeze Thaw(ThawBuffer tb) {
var n = new HashViviHook(null, null);
tb.Register(n);
n.hash = (P6any) tb.ObjRef();
n.key = tb.String();
return n;
}
}
public class NewHashViviHook : ViviHook {
Variable hashv;
string key;
public NewHashViviHook(Variable hashv, string key) { this.hashv = hashv; this.key = key; }
public override void Do(Variable toviv) {
VarHash rh = new VarHash();
rh[key] = toviv;
hashv.Store(Kernel.BoxRaw(rh, Compartment.Top.HashMO));
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.NewHashViviHook);
fb.ObjRef(hashv);
fb.String(key);
}
internal static IFreeze Thaw(ThawBuffer tb) {
var n = new NewHashViviHook(null, null);
tb.Register(n);
n.hashv = (Variable) tb.ObjRef();
n.key = tb.String();
return n;
}
}
public class ArrayViviHook : ViviHook {
P6any ary;
int key;
public ArrayViviHook(P6any ary, int key) { this.ary = ary; this.key = key; }
public override void Do(Variable toviv) {
VarDeque vd = (VarDeque) ary.GetSlot(Compartment.Top.ListMO, "$!items");
while (vd.Count() <= key)
vd.Push(Kernel.NewTypedScalar(null));
vd[key] = toviv;
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.ArrayViviHook);
fb.ObjRef(ary);
fb.Int(key);
}
internal static IFreeze Thaw(ThawBuffer tb) {
var n = new ArrayViviHook(null, 0);
tb.Register(n);
n.ary = (P6any) tb.ObjRef();
n.key = tb.Int();
return n;
}
}
public class NewArrayViviHook : ViviHook {
Variable ary;
int key;
public NewArrayViviHook(Variable ary, int key) { this.ary = ary; this.key = key; }
public override void Do(Variable toviv) {
VarDeque vd = new VarDeque();
while (vd.Count() <= key)
vd.Push(Kernel.NewTypedScalar(null));
vd[key] = toviv;
P6opaque d = new P6opaque(Compartment.Top.ArrayMO);
d.slots[0] = vd;
d.slots[1] = new VarDeque();
ary.Store(d);
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.NewArrayViviHook);
fb.ObjRef(ary);
fb.Int(key);
}
internal static IFreeze Thaw(ThawBuffer tb) {
var n = new NewArrayViviHook(null, 0);
tb.Register(n);
n.ary = (Variable) tb.ObjRef();
n.key = tb.Int();
return n;
}
}
public sealed class ListVariable: Variable {
P6any val;
public ListVariable(P6any val) { this.val = val; }
public override P6any Fetch() { return val; }
public override Variable Assign(Variable inp) {
val.mo.mro_LISTSTORE.Get(this, inp);
return this;
}
public override Variable AssignO(P6any inp, bool listishly) {
val.mo.mro_LISTSTORE.Get(this, listishly ?
(Variable)new ListVariable(inp) : inp);
return this;
}
public override void Store(P6any v) {
throw new NieczaException("Writing to readonly scalar");
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.ListVariable);
fb.ObjRef(val);
}
internal static ListVariable Thaw(ThawBuffer tb) {
ListVariable n = new ListVariable(null);
tb.Register(n);
n.val = (P6any) tb.ObjRef();
return n;
}
}
public sealed class RWVariable: Variable {
STable type; // null for Any/Mu variables, and roish
P6any val;
ViviHook whence;
public RWVariable(STable type, ViviHook whence, P6any val) {
this.val = val; this.whence = whence;
this.type = type;
}
public override P6any Fetch() { return val; }
public override Variable Assign(Variable inp) {
Store(inp.Fetch());
return this;
}
public override Variable AssignO(P6any inp, bool listishly) {
Store(inp);
return this;
}
public override void Store(P6any v) {
if (v == Compartment.Top.NilP) {
v = type == null ? Compartment.Top.AnyP : type.initObj;
}
if (type != null && !v.Does(type)) {
throw new NieczaException("Nominal type check failed for scalar store; got " + v.mo.name + ", needed " + type.name + " or subtype");
}
if (whence != null) {
ViviHook vh = whence;
whence = null;
vh.Do(this);
}
val = v;
}
public override void Vivify() {
ViviHook w = whence;
whence = null;
if (w != null) w.Do(this);
}
public override void Freeze(FreezeBuffer fb) {
int code = ((int)SerializationCode.RWVariable) +
(whence != null ? 1 : 0);
fb.Byte((byte)code);
if (whence != null) fb.ObjRef(whence);
fb.ObjRef(type);
fb.ObjRef(val);
}
internal static RWVariable Thaw(ThawBuffer tb, int subcode) {
RWVariable n = new RWVariable(null, null, null);
tb.Register(n);
if (subcode != 0) n.whence = (ViviHook) tb.ObjRef();
n.type = (STable) tb.ObjRef();
n.val = (P6any) tb.ObjRef();
return n;
}
}
public sealed class TiedVariable: Variable {
P6any fetch;
P6any store;
ViviHook whence;
private TiedVariable() { }
public TiedVariable(P6any whsub, P6any fetch, P6any store) {
this.fetch = fetch;
this.store = store;
this.whence = whsub.IsDefined() ? new SubViviHook(whsub) : null;
}
public override P6any Fetch() {
Variable vr = Kernel.RunInferior(fetch.Invoke(
Kernel.GetInferiorRoot(), new [] {Compartment.Top.AnyP}, null));
return vr.Fetch();
}
public override void Store(P6any v) {
if (whence != null) {
ViviHook vh = whence;
whence = null;
vh.Do(this);
}
Kernel.RunInferior(store.Invoke(Kernel.GetInferiorRoot(),
new [] { Compartment.Top.AnyP, v }, null));
}
public override void Vivify() {
ViviHook w = whence;
whence = null;
if (w != null) w.Do(this);
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.TiedVariable);
fb.ObjRef(fetch);
fb.ObjRef(store);
fb.ObjRef(whence);
}
internal static TiedVariable Thaw(ThawBuffer tb) {
TiedVariable n = new TiedVariable();
tb.Register(n);
n.fetch = (P6any) tb.ObjRef();
n.store = (P6any) tb.ObjRef();
n.whence = (ViviHook) tb.ObjRef();
return n;
}
}
// Used to make Variable sharing explicit in some cases
public class StashEnt : IFreeze {
public Variable v;
public string file;
public int line;
public bool constant;
public void Bind(Variable v) {
if (constant)
throw new NieczaException("Binding of constant common variable");
this.v = v;
}
void IFreeze.Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.StashEnt);
fb.Byte((byte)(constant ? 1 : 0));
fb.ObjRef(v);
fb.String(file);
fb.Int(line);
}
internal static StashEnt Thaw(ThawBuffer tb) {
StashEnt r = new StashEnt();
tb.Register(r);
r.constant = tb.Byte() != 0;
r.v = (Variable)tb.ObjRef();
r.file = tb.String();
r.line = tb.Int();
return r;
}
}
// We need to isolate the compilations of different modules from
// each other, which is accomplished by a stack of isolation containers.
// Each global variable in niecza must be assured to be incapable of
// compromising the isolation. To simplify this, four restricted
// contracts are offered:
//
// [Immutable] fields are never changed, so they cannot act as a side
// channel. This is assumed for initonly fields of primitive type or
// some well-known immutable types. For other cases, such as arrays,
// that are not manifestly immutable, this attribute can be used to mark
// them.
//
// [TrueGlobal] fields can be changed during execution, but are not
// changed under the direct control of Perl 6 code. They may leak some
// information such as random number seeds. They may NOT point at Perl 6
// level objects.
[AttributeUsage(AttributeTargets.Field)]
class CORESavedAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
class ImmutableAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
class TrueGlobalAttribute : Attribute { }
// Boxes all per-dll state required by the code generator
sealed class EmitUnit {
// This is only used during GenerateCode calls, which cannot
// call back into Perl 6 code, so it doesn't need to be
// containerized, or even saved
[ThreadStatic]
internal static EmitUnit Current;
public AssemblyBuilder asm_builder;
public ModuleBuilder mod_builder;
public TypeBuilder type_builder;
public int nextid;
public Dictionary<object, FieldInfo> constants;
public NamProcessor np;
Dictionary<string, CpsOp> val_constants;
List<NamProcessor> fill = new List<NamProcessor>();
string dll_name;
int funique;
public EmitUnit(string uname, string asm_name, string dll_name, bool is_mainish) {
if (Config.CGForceSave && dll_name == null)
dll_name = asm_name + ".exe";
this.dll_name = dll_name;
asm_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName(asm_name),
(dll_name == null ? AssemblyBuilderAccess.Run :
AssemblyBuilderAccess.RunAndSave),
Backend.obj_dir);
mod_builder = dll_name == null ?
asm_builder.DefineDynamicModule(asm_name) :
asm_builder.DefineDynamicModule(asm_name, dll_name);
type_builder = mod_builder.DefineType(asm_name,
TypeAttributes.Public | TypeAttributes.Sealed |
TypeAttributes.Class | TypeAttributes.BeforeFieldInit);
if (is_mainish) {
var mainb = type_builder.DefineMethod("Main",
MethodAttributes.Static | MethodAttributes.Public,
typeof(void), new Type[] { typeof(string[]) });
var il = mainb.GetILGenerator();
il.Emit(OpCodes.Ldstr, uname);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(Kernel).GetMethod("MainHandler"));
il.Emit(OpCodes.Ret);
asm_builder.SetEntryPoint(mainb);
}
constants = new Dictionary<object,FieldInfo>(new IdentityComparer());
val_constants = new Dictionary<string,CpsOp>();
}
public void CgSub(SubInfo sub, bool erase) {
if (sub.code != RuntimeUnit.JitCompileSub && sub.nam_str == null)
return; // not defined by Perl 6?
EmitUnit oc = Current;
if (Config.CGVerbose > 0)
Console.WriteLine("generating code for: {0} ({1:X})",
sub.name, sub.GetHashCode());
Current = this;
try {
np = new NamProcessor(new CpsBuilder(this,
"C" + fill.Count + sub.name, true), sub);
np.MakeBody(Reader.Read(sub.nam_str, sub.nam_refs));
fill.Add(np);
} finally {
np = null;
Current = oc;
}
if (erase) {
sub.nam_str = null;
sub.nam_refs = null;
}
}
public Type Finish(out object constTable) {
var type = type_builder.CreateType();
constTable = Activator.CreateInstance(type);
if (dll_name != null)
asm_builder.Save(dll_name);
foreach (NamProcessor th in fill)
th.FillSubInfo(type, constTable);
var fields = new Dictionary<string,FieldInfo>();
foreach (FieldInfo fi in type.GetFields())
fields[fi.Name] = fi;
var rconstants = new Dictionary<object,FieldInfo>();
foreach (var kv in constants)
rconstants[kv.Key] = fields[kv.Value.Name];
constants = rconstants;
return type;
}
internal CpsOp TypeConstant(STable s) {
return RefConstant(s == null ? "" : s.name, "MO", s, Tokens.STable);
}
internal CpsOp TypeConstantP(STable s) {
return RefConstant(s.name, "P", s.typeObj, Tokens.P6any);
}
internal CpsOp SubConstant(SubInfo s) {
return RefConstant(s == null ? "" : s.name, "S", s, Tokens.SubInfo);
}
internal CpsOp FrameConstant(Frame s) {
return RefConstant(s.info.name, "F", s, Tokens.Frame);
}
internal CpsOp RefConstant(string n1, string n2, object val, Type nty) {
if (val == null)
return CpsOp.Null(nty);
FieldInfo fi;
if (!constants.TryGetValue(val, out fi))
constants[val] = fi = NewField(n1, n2, nty ?? val.GetType());
return CpsOp.IsConst(CpsOp.GetConst(fi));
}
internal CpsOp ValConstant(string key, object val) {
CpsOp r;
if (!val_constants.TryGetValue(key, out r))
val_constants[key] = r = RefConstant(key, "", val, val is Variable ? typeof(Variable) : null);
return r;
}
internal CpsOp VarConstStr(string s) {
return ValConstant('S' + s, Builtins.MakeStr(s));
}
internal CpsOp VarConstNum(double n) {
char[] c = new char[5];
c[0] = 'N';
long b = BitConverter.DoubleToInt64Bits(n);
c[1] = (char)(b);
c[2] = (char)(b >> 16);
c[3] = (char)(b >> 32);
c[4] = (char)(b >> 48);
return ValConstant(new string(c), Builtins.MakeFloat(n));
}
public static Variable ExactNum(int numbase, string digits) {
BigInteger num = BigInteger.Zero;
BigInteger den = BigInteger.Zero;
bool frac = false;
bool neg = false;
foreach (char d in digits) {
if (d == '-') neg = true;
if (d == '_') continue;
if (d == '/') {
if (frac)
throw new Exception("two slashes in " + digits);
frac = true;
continue;
}
int digval;
if (d >= '0' && d <= '9') { digval = d - '0'; }
else if (d >= 'a' && d <= 'z') { digval = d + 10 - 'a'; }
else if (d >= 'A' && d <= 'Z') { digval = d + 10 - 'A'; }
else { throw new Exception("invalid digit in " + digits); }
if (digval >= numbase) { throw new Exception("out of range digit in " + digits); }
if (frac) {
den *= numbase;
den += digval;
} else {
num *= numbase;
num += digval;
}
}
if (neg) num = -num;
if (num == BigInteger.Zero && den.Sign > 0)
den = BigInteger.One;
if (den > BigInteger.One) {
BigInteger g = BigInteger.GreatestCommonDivisor(num, den);
if (g != BigInteger.One) {
num /= g;
den /= g;
}
}
ulong sden;
if (!den.AsUInt64(out sden)) {
return Builtins.MakeFloat((double)num / (double)den);
}
if (sden == 0)
return Builtins.MakeInt(num);
return Builtins.MakeFixRat(num, sden);
}
internal CpsOp VarConstExact(int bas, string digs) {
return ValConstant("X" + bas + ',' + digs, ExactNum(bas, digs));
}
internal CpsOp CCConst(int[] cc) {
StringBuilder code = new StringBuilder("CC");
foreach (int x in cc) {
code.Append((char)x);
code.Append((char)(x>>16));
}
return ValConstant(code.ToString(), new CC(cc));
}
internal CpsOp StringListConst(string[] sl) {
StringBuilder code = new StringBuilder("LS");
foreach (string s in sl) {
code.Append((char)(s.Length >> 16));
code.Append((char)(s.Length));
code.Append(s);
}
return ValConstant(code.ToString(), sl);
}
internal CpsOp CCListConst(int[][] ccl) {
StringBuilder code = new StringBuilder("LC");
foreach (int[] cc in ccl) {
code.Append((char)(cc.Length >> 16));
code.Append((char)(cc.Length));
foreach (int x in cc) {
code.Append((char)x);
code.Append((char)(x>>16));
}
}
CC[] buf = new CC[ccl.Length];
for (int i = 0; i < buf.Length; i++) buf[i] = new CC(ccl[i]);
return ValConstant(code.ToString(), buf);
}
internal FieldBuilder NewField(string n1, string n2, Type ty) {
char[] buf = new char[20];
// add unique-id
int wp = 10;
int ac = funique++;
while (ac > 0) {
int d = ac % 36;
ac /= 36;
buf[--wp] = d >= 10 ? (char)(d + 87) : (char)(d + 48);
}
int rp = wp;
wp = 0;
while (rp != 10) { buf[wp++] = buf[rp++]; }
buf[wp++] = '_';
// and names; sanitized
foreach (char ch in n1) {
if (wp == 20) break;
if (ch >= ' ' && ch <= '~') // yes this is overkill
buf[wp++] = ch;
}
foreach (char ch in n2) {
if (wp == 20) break;
if (ch >= ' ' && ch <= '~' && wp < 20)
buf[wp++] = ch;
}
return type_builder.DefineField(new string(buf, 0, wp),
ty, FieldAttributes.Public);
}
}
class IdentityComparer : IEqualityComparer<object> {
public int GetHashCode(object o) {
return RuntimeHelpers.GetHashCode(o);
}
public new bool Equals(object a, object b) {
return a == b;
}
}
// There are two kinds of units. Primary units have their own hash of
// globals, assembly (if saved), and save file; subordinate units share
// those of the master.
public sealed class RuntimeUnit : IFreeze {
public string name, filename, source, asm_name, dll_name;
public HashSet<RuntimeUnit> depended_units;
public List<RuntimeUnit> subordinates = new List<RuntimeUnit>();
public RuntimeUnit owner;
public Dictionary<string, StashEnt> globals;
public SubInfo mainline, bottom;
public List<SubInfo> our_subs;
public bool is_mainish;
// note: type is only set to a non-null value if the type is a
// member of a saved assembly; thus type being non-null implies
// a significance to freezing and thawing the type
public Type type;
public object constTable;
public Dictionary<object, FieldInfo> constants;
public bool inited = false;
// used during construction only
public List<KeyValuePair<int,STable>> stubbed_stashes;
public int nextid;
private RuntimeUnit() { }
public RuntimeUnit(string name, string filename, string source,
bool main, bool runnow) {
this.name = name;
this.filename = filename;
this.source = source;
this.depended_units = new HashSet<RuntimeUnit>();
this.stubbed_stashes = new List<KeyValuePair<int,STable>>();
this.is_mainish = main;
if (name == "CORE")
Kernel.CreateBasicTypes();
this.asm_name = Backend.prefix + name.Replace("::", ".");
this.dll_name = asm_name + (main ? ".exe" : ".dll");
our_subs = new List<SubInfo>();
}
internal void SaveSubs(EmitUnit eu, bool erase) {
foreach (SubInfo z in our_subs)
eu.CgSub(z, erase);
foreach (RuntimeUnit zu in subordinates)
foreach (SubInfo z in zu.our_subs)
eu.CgSub(z, erase);
}
internal static Frame JitCompileSub(Frame th) {
if (th.info.code != JitCompileSub) {
th.code = th.info.code;
th.EnsureSpills(th.info.nspill);
return th;
}
if (th.info.prototype != null &&
th.info.prototype.code != JitCompileSub) {
th.info.code = th.info.prototype.code;
th.info.nspill = th.info.prototype.nspill;
th.code = th.info.code;
th.EnsureSpills(th.info.nspill);
return th;
}
SubInfo sub = th.info.prototype ?? th.info;
if (Config.CGVerbose > 0)
Console.WriteLine("Generating code for {0} now because it's about to be run", sub.name);
EmitUnit eu = new EmitUnit(null, "Anon." + Interlocked.Increment(
ref anon_id), null, false);
eu.CgSub(sub, false);
object jit_consts;
SetConstants(eu.Finish(out jit_consts), jit_consts, eu.constants);
th.code = th.info.code = sub.code;
th.info.nspill = sub.nspill;
th.EnsureSpills(th.info.nspill);
return th;
}
// note that after Save the unit is _not_ run; the compartment
// is immediately discarded!
public void Save() {
EmitUnit eu = new EmitUnit(name, asm_name, dll_name, is_mainish);
SaveSubs(eu, !Config.KeepIL);
type = eu.Finish(out constTable);
constants = eu.constants;
// all co-saved units must remember that their code is here
foreach (RuntimeUnit zu in subordinates)
zu.type = type;
Compartment.Top.reg.SaveUnit(name, this);
}
internal void SetConstants() { SetConstants(type, constTable, constants); }
static void SetConstants(Type ty, object ctab, Dictionary<object,FieldInfo> consts) {
if (ty != null) {
foreach (KeyValuePair<object, FieldInfo> kv in consts)
kv.Value.SetValue(ctab, kv.Key);
}
}
[TrueGlobal] static int anon_id;
// This is called when compiling a unit which will be run without
// saving _only_
public void PrepareEval() {
EmitUnit eu = new EmitUnit(name, "Anon." + Interlocked.Increment(
ref anon_id) + "." + asm_name, null, false);
SaveSubs(eu, false);
object jit_consts;
SetConstants(eu.Finish(out jit_consts), jit_consts, eu.constants);
}
internal void RunMainline() {
RuntimeUnit csr = this;
Compartment.Top.setting_path = new Dictionary<string,SubInfo>();
while (csr.mainline.outer != null) {
RuntimeUnit o = csr.mainline.outer.unit;
Compartment.Top.setting_path[o.name] = csr.mainline;
csr = o;
}
Kernel.RunInferior(Kernel.GetInferiorRoot().
MakeChild(null, csr.mainline, Compartment.Top.AnyP));
}
internal string LinkUnit(RuntimeUnit other) {
foreach (RuntimeUnit third in other.depended_units)
depended_units.Add(third);
foreach (KeyValuePair<string, StashEnt> gm in other.globals) {
StashEnt ose;
StashEnt nse = gm.Value;
string who = gm.Key.Substring(1, (int)gm.Key[0]);
string name = gm.Key.Substring(1 + (int)gm.Key[0]);
string err = null;
if (globals.TryGetValue(gm.Key, out ose))
err = NsMerge(who, name, ref nse, ose);
if (err != null) return err;
globals[gm.Key] = nse;
}
Compartment.Top.check.RunNew();
Compartment.Top.init.RunNew();
Compartment.Top.end.RunNew();
return null;
}
public static Variable MakeAppropriateVar(string name) {
if (name.Length >= 1 && name[0] == '@')
return Kernel.CreateArray();
if (name.Length >= 1 && name[0] == '%')
return Kernel.CreateHash();
return Kernel.NewTypedScalar(null);
}
static bool IsEmptyAggr(Variable v) {
if (!v.List) return false;
P6any p = v.Fetch();
if (p.mo == Compartment.Top.ArrayMO) {
return ((VarDeque)p.GetSlot(Compartment.Top.ListMO, "$!items")).Count() == 0 &&
((VarDeque)p.GetSlot(Compartment.Top.ListMO, "$!rest")).Count() == 0;
}
else if (p.mo == Compartment.Top.HashMO) {
return Kernel.UnboxAny<VarHash>(p).Count == 0;
}
else {
return false;
}
}
public static string NsMerge(string who, string name,
ref StashEnt nse, StashEnt ose) {
P6any nseo = nse.v.Fetch();
P6any oseo = ose.v.Fetch();
bool nseod = nseo.IsDefined();
bool oseod = oseo.IsDefined();
int omode = ose.v.Mode;
int nmode = nse.v.Mode;
// lowest priority are empty common symbols
if (nmode == Variable.RW && !nseod || !nse.constant && IsEmptyAggr(nse.v)) {
nse = ose;
return null;
}
if (omode == Variable.RW && !oseod || !ose.constant && IsEmptyAggr(ose.v)) {
return null;
}
// no conflict if items are identical
if (ose.v == nse.v || omode != Variable.RW &&
omode == nmode && oseo == nseo) {
nse = ose;
return null;
}
// no conflict if items are simple packages with the same who
// note that this will effectively change a constant, but these
// packages are observationally identical barring &infix:<does>
// (mis)use
if (ose.constant && !oseod && nse.constant && !nseod &&
(oseo.mo.mo.type == P6how.PACKAGE || nseo.mo.mo.type == P6how.PACKAGE) &&
oseo.mo.who.Isa(Compartment.Top.StashMO) &&
nseo.mo.who.Isa(Compartment.Top.StashMO) &&
Kernel.UnboxAny<string>(oseo.mo.who) ==
Kernel.UnboxAny<string>(nseo.mo.who)) {
if (nseo.mo.mo.type == P6how.PACKAGE)
nse = ose;
return null;
}
return "Two definitions found for symbol "+who+"::"+name+"\n\n" +
" first at "+ose.file+" line "+ose.line+"\n" +
" second at "+nse.file+" line "+nse.line;
}
public string NsBind(string who, string name, Variable var,
string file, int line) {
string key = (char)who.Length + who + name;
StashEnt nse = new StashEnt();
nse.constant = (var != null);
if (var == null)
var = MakeAppropriateVar(name);
nse.v = var;
nse.file = file;
nse.line = line;
StashEnt ose;
string err = null;
if (globals.TryGetValue(key, out ose))
err = NsMerge(who, name, ref nse, ose);
if (err != null) return err;
globals[key] = nse;
return null;
}
void IFreeze.Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.RuntimeUnit);
// put this FIRST so that we can bail out in Thaw if need be
fb.String(name);
RuntimeUnit[] dep = new List<RuntimeUnit>(depended_units).ToArray();
Array.Sort<RuntimeUnit>(dep, (a, b) =>
string.CompareOrdinal(a.name, b.name));
string[] srcinfo = new string[dep.Length * 2];
for (int i = 0; i < dep.Length; i++) {
srcinfo[i*2] = dep[i].name;
srcinfo[i*2+1] = Utils.HashToStr(
ObjectRegistry.NewHash().ComputeHash(
new UTF8Encoding().GetBytes(dep[i].source)));
}
fb.Strings(srcinfo);
fb.String(filename);
fb.String(source);
fb.String(asm_name);
fb.String(dll_name);
fb.ObjRef(owner);
if (owner == this) {
// master unit requires saving type and constant info
fb.Int(constants.Count);
foreach (KeyValuePair<object,FieldInfo> kv in constants) {
fb.String(kv.Value.Name);
fb.ObjRef(kv.Key);
}
fb.Int(globals.Count);
foreach (KeyValuePair<string, StashEnt> kv in globals) {
fb.String(kv.Key);
fb.ObjRef(kv.Value);
}
}
fb.Refs(dep);
fb.Refs(subordinates);
fb.ObjRef(mainline);
fb.ObjRef(bottom);
fb.Refs(our_subs);
fb.Byte((byte)(is_mainish ? 1 : 0));
if (name == "CORE") {
FieldInfo[] kf = typeof(Compartment).GetFields();
Array.Sort<FieldInfo>(kf,
(f1, f2) => string.CompareOrdinal(f1.Name, f2.Name));
foreach (FieldInfo f in kf) {
if (f.GetCustomAttributes(typeof(CORESavedAttribute), true).Length != 0) {
//Console.WriteLine("Freeze {0} {1}", f, f.GetValue(Compartment.Top));
fb.ObjRef(f.GetValue(Compartment.Top));
}
}
}
}
internal static RuntimeUnit Thaw(ThawBuffer tb) {
RuntimeUnit n = new RuntimeUnit();
tb.Register(n);
n.name = tb.String();
string[] srcinfo = tb.Strings();
if (Builtins.upcall_receiver != null) {
object[] args = new object[srcinfo.Length + 1];
Array.Copy(srcinfo, 0, args, 1, srcinfo.Length);
args[0] = "check_dated";
object result = Builtins.UpCall(args);
if (result is Exception)
throw (Exception)result;
if ((string)result != "ok")
throw new ThawException("dated sources");
}
n.filename = tb.String();
n.source = tb.String();
n.asm_name = tb.String();
n.dll_name = tb.String();
n.owner = (RuntimeUnit)tb.ObjRef();
// master-case initialization has to happen before potentially
// loading any *other* unit
if (n.owner == n) {
// is a master unit
// set this before any potential of aliasing
n.globals = new Dictionary<string,StashEnt>();
int ncon = tb.Int();
if (Backend.cross_level_load) {
// don't load a type, throw away constants
while (ncon-- > 0) {
tb.String();
tb.ObjRef();
}
} else {
Assembly assembly = Assembly.Load(n.asm_name);
n.type = tb.type = assembly.GetType(n.asm_name, true);
n.constTable = Activator.CreateInstance(n.type);
n.constants = new Dictionary<object,FieldInfo>();
var fields = new Dictionary<string,FieldInfo>();
foreach (FieldInfo fi in n.type.GetFields())
fields[fi.Name] = fi;
var meth = new Dictionary<string,MethodInfo>();
foreach (MethodInfo mi in n.type.GetMethods())
meth[mi.Name] = mi;
Compartment.Top.reg.methods[n.asm_name] = meth;
Compartment.Top.reg.instances[n.asm_name] = n.constTable;
while (ncon-- > 0) {
FieldInfo fi = fields[tb.String()];
object val = tb.ObjRef();
n.constants[val] = fi;
fi.SetValue(n.constTable, val);
}
}
int ct = tb.Int();
for (int i = 0; i < ct; i++) {
n.globals[tb.String()] = (StashEnt)tb.ObjRef();
}
}
else {
n.globals = n.owner.globals;
if (n.globals == null) throw new Exception("load goofed");
n.type = n.owner.type;
}
n.depended_units = new HashSet<RuntimeUnit>(tb.RefsA<RuntimeUnit>());
n.subordinates = tb.RefsL<RuntimeUnit>();
n.mainline = (SubInfo)tb.ObjRef();
n.bottom = (SubInfo)tb.ObjRef();
n.our_subs = tb.RefsL<SubInfo>();
n.is_mainish = tb.Byte() != 0;
if (n.name == "CORE") {
FieldInfo[] kf = typeof(Compartment).GetFields();
Array.Sort<FieldInfo>(kf,
(f1, f2) => string.CompareOrdinal(f1.Name, f2.Name));
foreach (FieldInfo f in kf) {
if (f.GetCustomAttributes(typeof(CORESavedAttribute), true).Length != 0) {
f.SetValue(Compartment.Top, tb.ObjRef());
//Console.WriteLine("Thaw {0} {1}", f, f.GetValue(Compartment.Top));
}
}
}
return n;
}
}
public sealed class LeaveHook {
public LeaveHook next;
public P6any thunk;
public int type;
public const int KEEP = 1;
public const int UNDO = 2;
public const int DIE = 4;
public const int POST = 7;
public const int SENTINEL = 15;
}
public abstract class LexInfo {
public SubInfo owner;
public string name;
public string file;
public int line;
public int pos;
public abstract void Init(Frame f);
public virtual object Get(Frame f) {
return Get(f,null);
}
public virtual void Set(Frame f, object to) { Set(f,to,null); }
public virtual object Get(Frame f, Frame orig) { return Get(f); }
public virtual void Set(Frame f, object to, Frame orig) { Set(f,to); }
internal virtual ClrOp SetCode(int up, ClrOp head) {
return SetCode(up, head, null);
}
internal virtual ClrOp SetCode(int up, ClrOp head, SubInfo orig) {
return SetCode(up, head);
}
internal virtual ClrOp GetCode(int up) { return GetCode(up, null); }
internal virtual ClrOp GetCode(int up, SubInfo orig) {
return GetCode(up);
}
// names that are forced to dynamism for quick access
public static bool IsDynamicName(string name) {
if (name == "$_" || name == "$/" || name == "$!") return true;
if (name.Length < 2) return false;
if (name[0] == '*' || name[0] == '?') return true;
if (name[1] == '*' || name[1] == '?') return true;
return false;
}
public virtual void BindFields() {}
public virtual int SigIndex() { return -1; }
internal abstract void DoFreeze(FreezeBuffer fb);
internal enum LexSerCode {
Simple, Sub, Label, Dispatch, Common, Constant, Package, Alias,
AttrAlias
}
internal ClrOp DieCode(Type rty, string msg) {
return new ClrOperator(rty, OpCodes.Throw, new [] {
new ClrConstructorCall(typeof(NieczaException).
GetConstructor(new [] { typeof(string) }),
new [] { new ClrStringLiteral(msg) }) });
}
}
public abstract class LIVarish : LexInfo {
public int index;
public LIVarish() { }
public override int SigIndex() { return index; }
public override void BindFields() {
index = owner.num_lex_slots++;
if (owner.protopad != null) {
int osz = owner.protopad.lexn == null ? 0 : owner.protopad.lexn.Length;
if (owner.num_lex_slots > 10 + osz)
Array.Resize(ref owner.protopad.lexn, owner.num_lex_slots * 2 - 10);
}
}
public override object Get(Frame f) {
return f.GetDynamic(index);
}
public override void Set(Frame f, object to) {
f.SetDynamic(index, to);
}
internal override ClrOp GetCode(int up) {
if ((owner.special & SubInfo.RUN_ONCE) != 0) {
return new ClrUnboxAny(Tokens.Variable,
new ClrMethodCall(false, Tokens.Frame.GetMethod("GetDynamic"),
EmitUnit.Current.FrameConstant(owner.protopad).head,
new ClrIntLiteral(typeof(int), index)));
}
return new ClrPadGet(up, index);
}
internal override ClrOp SetCode(int up, ClrOp to) {
if ((owner.special & SubInfo.RUN_ONCE) != 0)
return new ClrProtoSet(index,
EmitUnit.Current.FrameConstant(owner.protopad).head, to);
return new ClrPadSet(up, index, to);
}
}
// TODO: Provide some way to cache a StashEnt once the currentGlobals
// stops changing
public class LICommon : LexInfo {
public readonly string hkey;
public LICommon(string hkey) { this.hkey = hkey; }
public override void Init(Frame f) { }
internal string Stash() { return hkey.Substring(1, (int)hkey[0]); }
internal string VarName() { return hkey.Substring(1 + (int)hkey[0]); }
public override object Get(Frame f) {
return Kernel.GetGlobal(hkey);
}
public override void Set(Frame f, object to) {
Kernel.BindGlobal(hkey, (Variable)to);
}
internal override ClrOp GetCode(int up) {
return new ClrMethodCall(false, Tokens.Kernel_GetGlobal,
new ClrStringLiteral(hkey));
}
internal override ClrOp SetCode(int up, ClrOp to) {
return new ClrMethodCall(false, Tokens.Kernel_BindGlobal,
new ClrStringLiteral(hkey), to);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Common);
fb.String(hkey);
}
}
public class LIConstant : LexInfo {
public Variable value;
public LIConstant() { this.value = Compartment.Top.AnyP; }
internal LIConstant(Variable value) { this.value = value; }
public override void Init(Frame f) { }
public override void BindFields() { }
public override object Get(Frame f) { return value; }
internal override ClrOp GetCode(int up) {
return EmitUnit.Current.RefConstant(name, "", value, typeof(Variable)).head;
}
public override void Set(Frame f, object to) {
throw new NieczaException("Cannot bind to constant " + name);
}
internal override ClrOp SetCode(int up, ClrOp to) {
return DieCode(Tokens.Void, "Cannot bind to constant " + name);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Constant);
fb.ObjRef(value);
}
}
public class LISub : LIVarish {
public SubInfo def;
public LISub(SubInfo def) { this.def = def; }
internal LISub(int index, SubInfo def) { this.index = index; this.def = def; }
public override void Init(Frame f) {
Set(f, def.protosub);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Sub);
fb.Int(index);
fb.ObjRef(def);
}
}
public class LISimple : LIVarish {
public const int NOINIT = 1;
public const int ROINIT = 2;
public const int DEFOUTER = 4;
public const int LIST = 8;
public const int HASH = 16;
public int flags;
public STable type;
public LISimple(int flags, STable type) {
this.flags = flags;
this.type = type;
}
internal LISimple(int index, int flags, STable type) {
this.index = index;
this.flags = flags;
this.type = type;
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Simple);
fb.Int(index);
fb.Byte((byte)flags);
fb.ObjRef(type);
}
public override void Init(Frame f) {
if ((flags & NOINIT) != 0)
return;
if ((flags & ROINIT) != 0)
Set(f, Compartment.Top.AnyP);
else if ((flags & DEFOUTER) != 0)
Set(f, f.info.GetOuterTopic(f));
else if ((flags & LIST) != 0)
Set(f, Kernel.CreateArray());
else if ((flags & HASH) != 0)
Set(f, Kernel.CreateHash());
else
Set(f, Kernel.NewTypedScalar(type));
}
}
public class LILabel : LIVarish {
public LILabel() { }
internal LILabel(int index) { this.index = index; }
public override void Init(Frame f) {
Set(f, Kernel.NewLabelVar(f, name));
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Label);
fb.Int(index);
}
}
public class LIDispatch : LIVarish {
public LIDispatch() { }
internal LIDispatch(int index) { this.index = index; }
public override void Init(Frame f) {
MakeDispatch(f);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Dispatch);
fb.Int(index);
}
// synchronize with NamProcessor.MakeDispatch
static P6any GetProtoSub(LexInfo li, Frame f) {
return (li is LISub) ? ((LISub)li).def.protosub :
((Variable)li.Get(f,null)).Fetch();
}
internal void MakeDispatch(Frame into) {
HashSet<string> names = new HashSet<string>();
List<P6any> cands = new List<P6any>();
P6any proto = null;
string filter = name + ":";
string pn = name + ":(!proto)";
//Console.WriteLine("MakeDispatch {0}", name);
Frame f = into;
for (SubInfo csr = into.info; ; csr = csr.outer) {
bool brk = false;
foreach (KeyValuePair<string,LexInfo> kp in csr.dylex) {
if (Utils.StartsWithInvariant(filter, kp.Key) &&
kp.Key != pn &&
!names.Contains(kp.Key)) {
names.Add(kp.Key);
brk = true;
//Console.WriteLine("cand {0}", kp.Key);
cands.Add(GetProtoSub(kp.Value,f));
}
}
if (csr.outer == null) break;
// don't go above nearest proto
if (csr.dylex.ContainsKey(pn)) {
//Console.WriteLine("proto {0}", pn);
proto = GetProtoSub(csr.dylex[pn], f);
break;
}
if (brk) cands.Add(null);
f = f.outer;
}
Set(into, Kernel.MakeDispatcher(name, proto, cands.ToArray()));
}
}
public class LIAlias : LexInfo {
public string to;
public LIAlias(string to) { this.to = to; }
public override void Init(Frame f) { }
object Common(Frame f, bool set, object bind, Frame orig) {
Frame cr = f;
LexInfo li = null;
while (cr.info.dylex == null ||
!cr.info.dylex.TryGetValue(to, out li))
cr = cr.outer;
if (!set) return li.Get(cr, orig);
else { li.Set(cr, bind, orig); return null; }
}
public override object Get(Frame f, Frame orig) { return Common(f,false,null,orig); }
public override void Set(Frame f, object bind, Frame orig) { Common(f,true,bind,orig); }
internal override ClrOp GetCode(int up, SubInfo orig) {
LexInfo real;
SubInfo sc = owner;
while (!sc.dylex.TryGetValue(to, out real)) {
sc = sc.outer;
up++;
}
return real.GetCode(up, orig);
}
internal override ClrOp SetCode(int up, ClrOp bind, SubInfo orig) {
LexInfo real;
SubInfo sc = owner;
while (!sc.dylex.TryGetValue(to, out real)) {
sc = sc.outer;
up++;
}
return real.SetCode(up, bind, orig);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Alias);
fb.String(to);
}
}
public class LIAttrAlias : LexInfo {
public STable atype;
public string aname;
public LIAttrAlias(STable atype, string aname) {
this.atype = atype;
this.aname = aname;
}
public override void Init(Frame f) { }
P6any GetSelf(Frame f) {
SubInfo sc = owner;
LexInfo self;
while (sc != null && !sc.dylex.TryGetValue("self", out self)) {
sc = sc.outer;
f = f.outer;
}
if (sc == null)
throw new NieczaException("No 'self' available in this scope to resolve reference to attribute '{0}'", name);
return ((Variable)self.Get(f)).Fetch();
}
ClrOp GetSelfCode(SubInfo orig) {
SubInfo sc = orig;
LexInfo self;
int up = 0;
while (sc != null && !sc.dylex.TryGetValue("self", out self)) {
sc = sc.outer;
up++;
}
if (sc == null)
return DieCode(Tokens.P6any, string.Format("No 'self' available in this scope to resolve reference to attribute '{0}'", name));
return new ClrMethodCall(false, Tokens.Variable_Fetch,
self.GetCode(up, orig));
}
public override object Get(Frame f, Frame orig) {
if (orig == null) throw new NieczaException("Indirect access to attribute aliases NYI");
return GetSelf(orig).GetSlot(atype, aname);
}
public override void Set(Frame f, object bind, Frame orig) {
if (orig == null) throw new NieczaException("Indirect access to attribute aliases NYI");
GetSelf(orig).SetSlot(atype, aname, bind);
}
internal override ClrOp GetCode(int up, SubInfo orig) {
return new ClrUnboxAny(Tokens.Variable,
new ClrMethodCall(false, Tokens.P6any_GetSlot,
GetSelfCode(orig),EmitUnit.Current.TypeConstant(atype).head,
new ClrStringLiteral(aname)));
}
internal override ClrOp SetCode(int up, ClrOp bind, SubInfo orig) {
return new ClrMethodCall(false, Tokens.P6any_SetSlot,
GetSelfCode(orig),EmitUnit.Current.TypeConstant(atype).head,
new ClrStringLiteral(aname), bind);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.AttrAlias);
fb.ObjRef(atype);
fb.String(aname);
}
}
public class LIPackage : LexInfo {
public STable pkg;
public LIPackage(STable pkg) { this.pkg = pkg; }
public override object Get(Frame f) {
return pkg == Compartment.Top.NilP.mo ? (Variable)Compartment.Top.Nil : pkg.typeObj;
}
public override void Init(Frame f) { }
internal override ClrOp GetCode(int up) {
return pkg == Compartment.Top.NilP.mo ?
EmitUnit.Current.RefConstant("Compartment.Top.Nil", "L", Compartment.Top.Nil, typeof(Variable)).head :
EmitUnit.Current.TypeConstantP(pkg).head;
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Package);
fb.ObjRef(pkg);
}
public override void Set(Frame f, object to) {
throw new NieczaException("Cannot bind to constant " + name);
}
internal override ClrOp SetCode(int up, ClrOp to) {
return DieCode(Tokens.Void, "Cannot bind to constant " + name);
}
}
public class Parameter : P6any, IFixup {
public int flags;
public int slot;
public string name;
public string[] names;
public object def; // SubInfo or Variable
public STable type;
// XXX I don't really like dangling two extra fields on every param...
public string attribute;
public STable attribute_type;
public object[] post_constraints; // Signature | SubInfo
public override string ReprName() { return "P6parameter"; }
private Parameter() { }
public Parameter(int flags, int slot, string name,
string[] names, object def, STable type, string attr,
STable atype) {
this.mo = Compartment.Top.ParameterMO;
this.flags = flags;
this.name = name;
this.slot = slot;
this.names = names;
this.def = def;
this.type = type;
this.attribute = attr;
this.attribute_type = atype;
}
public static Parameter TPos(string name, int slot) {
return new Parameter(RWTRANS | POSITIONAL, slot, name,
null, null, Compartment.Top.AnyMO, null, null);
}
public static Parameter TNamedOpt(string name, int slot) {
return new Parameter(RWTRANS | OPTIONAL, slot, name,
new string[] { name }, null, Compartment.Top.AnyMO, null, null);
}
// Value processing
public const int HASTYPE = 1; // else Compartment.Top.AnyMO
public const int MULTI_IGNORED = 16384;
public const int ANY_DEF = 0x40000;
public const int UNDEF_ONLY = 0x80000;
public const int DEF_ONLY = 0xC0000;
public const int TYPE_ONLY = 0x100000;
public const int DEF_MASK = 0x1C0000;
public const int DEF_SHIFT = 18;
// Value binding
public const int READWRITE = 2;
public const int RWTRANS = 8;
public const int INVOCANT = 8192;
public const int IS_COPY = 32768;
public const int IS_LIST = 65536;
public const int IS_HASH = 131072;
public const int CALLABLE = 0x200000;
// Value source
public const int HASDEFAULT = 32;
public const int OPTIONAL = 64;
public const int DEFOUTER = 4096;
public const int POSITIONAL = 128;
public const int SLURPY_POS = 256;
public const int SLURPY_NAM = 512;
public const int SLURPY_CAP = 1024;
public const int SLURPY_PCL = 2048;
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.Parameter);
fb.Int(flags);
fb.Int(slot);
fb.String(name);
fb.Strings(names);
fb.ObjRef(def);
fb.ObjRef(type);
fb.Refs(post_constraints);
fb.String(attribute);
if (attribute != null)
fb.ObjRef(attribute_type);
}
internal static Parameter Thaw(ThawBuffer tb) {
Parameter n = new Parameter();
tb.Register(n);
tb.PushFixup(n);
n.flags = tb.Int();
n.slot = tb.Int();
n.name = tb.String();
n.names = tb.Strings();
n.def = tb.ObjRef();
n.type = (STable)tb.ObjRef();
n.post_constraints = tb.RefsA<object>();
n.attribute = tb.String();
if (n.attribute != null)
n.attribute_type = (STable)tb.ObjRef();
return n;
}
void IFixup.Fixup() { mo = Compartment.Top.ParameterMO; }
}
public class Signature : P6any, IFixup {
public Parameter[] parms;
public override string ReprName() { return "P6sig"; }
public Signature(params Parameter[] parms) { this.mo = Compartment.Top.SignatureMO; this.parms = parms; }
private Signature() { }
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.Signature);
fb.Refs<Parameter>(parms);
}
internal static Signature Thaw(ThawBuffer tb) {
Signature n = new Signature();
tb.Register(n);
tb.PushFixup(n);
n.parms = tb.RefsA<Parameter>();
return n;
}
void IFixup.Fixup() { mo = Compartment.Top.SignatureMO; }
}
// This stores all the invariant stuff about a Sub, i.e. everything
// except the outer pointer. Now distinct from protopads
//
// Actually not quite *in*variant; some of this stuff has to be
// changed, but it's rare by construction. We don't want to be
// like Rakudo/Parrot where simple sub cloning requires copying
// 100s of bytes.
public class SubInfo : IFreeze, IFixup {
// Essential call functions
public DynBlockDelegate code = RuntimeUnit.JitCompileSub;
public int nspill;
public Signature sig;
// Local metadata
public int[] lines;
public Dictionary<string, LexInfo> dylex;
public uint dylex_filter; // (32,1) Bloom on hash code
public string name;
public int num_lex_slots; // for code generation
// maybe should be in extend or a hint?
public LAD ltm;
public int special;
public int phaser = -1;
public string outervar;
// References to related objects
public RuntimeUnit unit;
public SubInfo outer;
public P6any protosub;
public Frame protopad;
public SubInfo prototype; // when cloning, don't jit twice
public STable cur_pkg, methodof, body_of, in_class;
public STable mo;
// caches for fast $OUTER::_, invocants, exn handling
public int outer_topic_rank;
public int outer_topic_key;
public int self_key;
public SubInfo catch_, control;
// this is used only at compile time
public Dictionary<string,UsedInScopeInfo> used_in_scope;
// if this is non-null, compilation is being delayed
public string nam_str;
public object[] nam_refs;
// Used for closing runtime-generated SubInfo over values used
// For vtable wrappers: 0 = unboxed, 1 = boxed
// For dispatch routines, 0 = parameter list
public object[] param;
public List<SubInfo> children = new List<SubInfo>();
public Dictionary<string,object[]> extend;
// not saved, rarely used
internal Dictionary<string,SubInfo> rx_compile_cache;
public int[] edata;
public string[] label_names;
// No instance fields past this point
public class UsedInScopeInfo {
public string file;
public int line;
public int levels;
public string orig_file;
public int orig_line;
}
public const int RUN_ONCE = 1;
public const int MAKE_PROTOPAD = 2;
public const int HAS_TYPE = 4;
public const int UNSAFE = 8;
public const int PARAM_ROLE = 16;
public const int MAINLINE = 32;
public const int TRANSPARENT = 64;
public const int INLINED = 128;
public const int CANNOT_INLINE = 256;
public const int RETURN_PASS = 512;
public const uint FILTER_SALT = 0x9e3779b9;
// records: $start-ip, $end-ip, $type, $goto, $lid
public const int ON_NEXT = 1;
public const int ON_LAST = 2;
public const int ON_REDO = 3;
public const int ON_RETURN = 4;
public const int ON_DIE = 5;
public const int ON_SUCCEED = 6;
public const int ON_PROCEED = 7;
public const int ON_GOTO = 8;
public const int ON_NEXTDISPATCH = 9;
public const int ON_VARLOOKUP = 10;
public const int ON_WARNING = 11;
// ON_VARLOOKUP is kinda special, it's not used for exceptions
// but rather for $*FOO and the like; goto = the variable index
[Immutable]
private static string[] controls = new string[] { "unknown", "next",
"last", "redo", "return", "die", "succeed", "proceed", "goto",
"nextsame/nextwith", "varlookup", "warning" };
public static string DescribeControl(int type, Frame tgt,
string name) {
string ty = (type < controls.Length) ? controls[type] : "unknown";
if (name != null) {
return ty + "(" + name + (tgt != null ? ", lexotic)" : ", dynamic)");
//return ty + "(" + name + (tgt != null ? string.Format(", lexotic [{0:X}])", tgt.GetHashCode()) : ", dynamic)");
} else {
return ty;
}
}
public int GetInlineSlot(int ip, string name, int depth) {
int end = 0;
// ON_VARLOOKUP nodes are created in a set of 0 or more,
// followed by a nameless one that marks the depth.
// Find the correct end-node for the passed depth
while (end < edata.Length) {
if (edata[end+2] == ON_VARLOOKUP && edata[end+4] < 0 &&
edata[end+3] == depth && ip >= edata[end] &&
ip < edata[end+1])
break;
end += 5;
}
if (end == edata.Length) return -1;
while (true) {
end -= 5;
if (end < 0 || edata[end+2] != ON_VARLOOKUP ||
edata[end+4] < 0) // we've passed the end
return -1;
if (name.Equals(label_names[edata[end+4]]))
return edata[end+3];
}
}
public int FindControlEnt(int ip, int ty, string name) {
if (edata == null)
return -1;
for (int i = 0; i < edata.Length; i+=5) {
if (ip < edata[i] || ip >= edata[i+1])
continue;
if (ty != edata[i+2])
continue;
if (name != null && (edata[i+4] < 0 || !name.Equals(label_names[edata[i+4]])))
continue;
if (name == null && ty == ON_VARLOOKUP && edata[i+4] >= 0)
continue;
return edata[i+3];
}
return -1;
}
internal List<SubInfo> GetPhasers(int t1) {
int t2 = (t1 == Kernel.PHASER_KEEP) ? Kernel.PHASER_LEAVE : t1;
List<SubInfo> r = new List<SubInfo>();
foreach (SubInfo z in children) {
if (z.phaser >= t1 && z.phaser <= t2) r.Add(z);
}
return r;
}
public Frame SetupCall(Frame caller, Frame outer, P6any sub,
Variable[] pos, VarHash named, bool quiet, DispatchEnt de) {
Frame th;
if ((special & RUN_ONCE) != 0) {
th = protopad;
th.caller = caller;
th.ip = 0;
if (Frame.TraceCalls)
Console.WriteLine("{0}\t{1}", caller.info.name, name);
} else {
th = caller.MakeChild(outer, this, sub);
}
th.curDisp = de;
th.pos = pos;
th.named = named;
// If we call an inferior runloop from the binder, we need for the
// runloop to not overwrite th, which it would if the top frame
// stayed 'caller'.
Kernel.SetTopFrame(th);
if (quiet) th.flags |= Frame.CHECK_ONLY;
return th;
}
internal Variable GetOuterTopic(Frame f) {
for (int i = 0; i < outer_topic_rank; i++) f = f.outer;
return (Variable)f.GetDynamic(outer_topic_key);
}
internal static Frame AutoThreadSubC(Frame th) {
Variable[] src = (Variable[]) th.lex4;
Variable[] dst = (Variable[]) th.lex8;
if (th.lexi1 > 0)
dst[th.lexi1 - 1] = (Variable)th.resultSlot;
if (th.lexi1 == dst.Length) {
P6opaque nj = new P6opaque(Compartment.Top.JunctionMO);
nj.slots[0] = th.lex3;
nj.slots[1] = Kernel.BoxRaw(dst, Compartment.Top.ParcelMO);
// restore, in case our caller is using this
if (th.lexi0 == -2)
th.named[(string)th.lex2] = (Variable)th.lex9;
else
th.pos[th.lexi0] = (Variable)th.lex9;
th.caller.resultSlot = nj;
return th.Return();
}
if (th.lexi0 == -2)
th.named[(string)th.lex2] = src[th.lexi1++];
else
th.pos[th.lexi0] = src[th.lexi1++];
return ((SubInfo)th.lex1).SetupCall(th, (Frame)th.lex5, (P6any)th.lex6,
th.pos, th.named, false, (DispatchEnt)th.lex7);
}
internal bool IsInlinable() {
if (sig == null)
return false;
if ((special & CANNOT_INLINE) != 0)
return false;
if (children.Count != 0)
return false;
foreach (KeyValuePair<string,LexInfo> kv in dylex) {
if (!(kv.Value is LISimple))
return false;
if ((kv.Key.Length > 0 &&
(kv.Key[0] == '?' || kv.Key[0] == '*')) ||
(kv.Key.Length > 1 &&
(kv.Key[1] == '?' || kv.Key[1] == '*')))
return false;
}
foreach (Parameter p in sig.parms) {
int fl = p.flags;
if ((fl & Parameter.POSITIONAL) == 0)
return false;
if ((fl & Parameter.HASDEFAULT) != 0)
return false;
}
return true;
}
internal void SetInlined() {
special |= INLINED;
outer.children.Remove(this);
unit.our_subs.Remove(this);
foreach (KeyValuePair<string, LexInfo> kv in outer.dylex)
if (kv.Value is LISub && ((LISub)kv.Value).def == this) {
outer.dylex.Remove(kv.Key);
break;
}
}
internal Variable RunBEGIN() {
return Kernel.RunInferior(protosub.Invoke(Kernel.GetInferiorRoot(),
Variable.None, null));
}
internal bool IsTopicalizer() {
if (sig == null)
return false;
LexInfo topic;
dylex.TryGetValue("$_", out topic);
foreach (Parameter p in sig.parms) {
if (p.slot >= 0 && topic != null && topic.SigIndex() == p.slot)
return true;
}
return false;
}
internal void AddLexical(string name, LexInfo li) {
li.owner = this;
li.name = name;
dylex[name] = li;
dylex_filter |= FilterForName(name);
li.BindFields();
if (name == "self")
self_key = li.SigIndex();
if (protopad != null) {
li.Init(protopad);
int ix = name.LastIndexOf(':');
LexInfo disp;
//Console.WriteLine("AddLexical {0}", name);
if (ix >= 0 && dylex.TryGetValue(name.Substring(0, ix),
out disp) && disp is LIDispatch) {
((LIDispatch)disp).MakeDispatch(protopad);
}
}
if (li is LISub && ((LISub)li).def.outer != this)
Console.WriteLine("Eeep! Adding a LISub to the wrong place");
}
// ofr != null is used ONLY with evals to set up a protopad pointing
// to a specific runtime pad. I'm not 100% sure this design is right...
internal void CreateProtopad(Frame ofr) {
if (protopad != null)
return;
if (outer != null)
outer.CreateProtopad(null);
// protosub is set when the parent's protopad is created,
// or when we are
protopad = new Frame(null, ofr != null ? ofr : outer != null ?
outer.protopad : null, this, protosub);
// make sure there's room for installing the current lexicals
// may be grown later for more lexicals, or when compiling
// spill-slots
protopad.EnsureSpills(num_lex_slots - 10);
foreach (SubInfo z in children) {
z.protosub = Kernel.MakeSub(z, protopad);
}
foreach (LexInfo li in dylex.Values)
li.Init(protopad);
}
public static uint FilterForName(string name) {
if (name.Length < 2 || name[1] != '*') return 0;
uint hash = (uint)(name.GetHashCode() * FILTER_SALT);
return 1u << (int)(hash >> 27);
}
private SubInfo() { }
public override string ToString() { return name ?? "ANON"; }
// This is a _shallow_ clone. The children wind up shared, as do
// a lot of immutable-ish objects that are referenced.
internal SubInfo(SubInfo o) {
code = o.code;
nspill = o.nspill;
sig = o.sig;
lines = o.lines;
dylex = o.dylex;
dylex_filter = o.dylex_filter;
name = o.name;
num_lex_slots = o.num_lex_slots;
ltm = o.ltm;
special = o.special;
phaser = -1; // not in any phaser chains
outervar = null; // in no pads
unit = o.unit;
outer = o.outer;
protosub = null;
protopad = null; // no compile-time existance!
cur_pkg = o.cur_pkg;
methodof = o.methodof;
body_of = o.body_of;
in_class = o.in_class;
mo = o.mo;
outer_topic_rank = o.outer_topic_rank;
outer_topic_key = o.outer_topic_key;
self_key = o.self_key;
catch_ = o.catch_;
control = o.control;
used_in_scope = null;
nam_str = null;
nam_refs = null;
param = o.param;
children = null;
extend = o.extend;
rx_compile_cache = null;
edata = o.edata;
label_names = o.label_names;
prototype = o.prototype ?? o;
}
public SubInfo(string name, int[] lines, DynBlockDelegate code,
SubInfo outer, LAD ltm, int[] edata, string[] label_names,
int nspill) {
this.lines = lines;
this.code = code;
this.outer = outer;
this.ltm = ltm;
this.name = name;
this.edata = edata;
this.label_names = label_names;
this.nspill = nspill;
this.dylex = new Dictionary<string,LexInfo>();
for (int i = 0; i < edata.Length; i += 5)
if (edata[i+2] == ON_VARLOOKUP && edata[i+4] >= 0)
dylex_filter |= FilterForName(label_names[edata[i+4]]);
}
public SubInfo(RuntimeUnit unit, string name, SubInfo outer,
STable cls, STable pkg, bool once, Frame ofr) {
edata = new int[0];
this.name = name;
this.unit = unit;
this.mo = cls;
this.outer = outer;
this.cur_pkg = pkg;
SubInfo sc = outer;
LexInfo li = null;
for (outer_topic_rank = 1; sc != null; sc = sc.outer) {
if (sc.dylex != null && sc.dylex.TryGetValue("$_", out li))
break;
outer_topic_rank++;
}
outer_topic_key = (li is LIVarish) ? (li as LIVarish).index : -1;
used_in_scope = new Dictionary<string,UsedInScopeInfo>();
dylex = new Dictionary<string,LexInfo>();
if (outer == null || outer.protopad != null)
protosub = Kernel.MakeSub(this, outer == null ?
null : outer.protopad);
if (once) {
special |= RUN_ONCE;
CreateProtopad(ofr);
}
}
public SubInfo(string name, DynBlockDelegate code) :
this(name, null, code, null, null, new int[0], null, 0) { }
void IFreeze.Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.SubInfo);
string mn = null;
string tn = null;
if (code != null) {
Type t = code.Method.DeclaringType;
if (t.Assembly == typeof(Kernel).Assembly) {
fb.Byte(1);
} else {
fb.Byte(0);
if (t.Assembly.GetName().Name != t.FullName)
throw new NieczaException("violation of naming protocols? {0} != {1}", t.Assembly.GetName().Name, t.FullName);
}
tn = t.FullName;
mn = code.Method.Name;
}
fb.String(mn);
fb.String(tn);
fb.Int(nspill);
fb.ObjRef(sig);
fb.Ints(lines);
fb.Ints(edata);
fb.Strings(label_names);
fb.Int(num_lex_slots);
fb.Int(dylex.Count);
foreach (KeyValuePair<string, LexInfo> kv in dylex) {
fb.String(kv.Key);
kv.Value.DoFreeze(fb);
}
// not saving dylex_filter as it is a cache
fb.String(name);
fb.ObjRef(ltm);
fb.Int(special);
fb.Int(phaser);
fb.String(outervar);
fb.ObjRef(unit);
fb.ObjRef(outer);
fb.ObjRef(protosub);
fb.ObjRef(protopad);
fb.ObjRef(prototype);
fb.ObjRef(cur_pkg);
fb.ObjRef(methodof);
fb.ObjRef(body_of);
fb.ObjRef(in_class);
fb.ObjRef(mo);
// not storing caches here
// also not storing used_in_scope, it's compiler only data
// TODO: we want to store the compiled code not this
fb.String(nam_str);
if (nam_str != null)
fb.Refs(nam_refs);
fb.Refs(param);
fb.Refs(children);
if (extend == null)
fb.Int(0);
else {
fb.Int(extend.Count);
foreach(KeyValuePair<string,object[]> kv in extend) {
fb.String(kv.Key);
fb.Refs(kv.Value);
}
}
}
internal static SubInfo Thaw(ThawBuffer tb) {
SubInfo n = new SubInfo();
tb.Register(n);
bool kernel = tb.Byte() != 0;
string mn = tb.String();
string tn = tb.String();
if (mn != null && (kernel || !Backend.cross_level_load)) {
MethodInfo mi;
object obj = null;
if (kernel) {
mi = typeof(Kernel).Assembly.GetType(tn, true)
.GetMethod(mn, BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Static);
} else {
Dictionary<string,MethodInfo> t1;
if (!Compartment.Top.reg.methods.TryGetValue(tn, out t1) ||
!t1.TryGetValue(mn, out mi)) {
throw new Exception("Thawed sub references nonexistant method " + tn + "::" + mn);
}
obj = Compartment.Top.reg.instances[tn];
}
n.code = (DynBlockDelegate) Delegate.CreateDelegate(
typeof(DynBlockDelegate), obj, mi);
}
n.nspill = tb.Int();
n.sig = (Signature)tb.ObjRef();
n.lines = tb.Ints();
n.edata = tb.Ints();
n.label_names = tb.Strings();
for (int i = 0; i < n.edata.Length; i += 5)
if (n.edata[i+2] == ON_VARLOOKUP && n.edata[i+4] >= 0)
n.dylex_filter |=FilterForName(n.label_names[n.edata[i+4]]);
n.num_lex_slots = tb.Int();
int dyct = tb.Int();
n.dylex = new Dictionary<string, LexInfo>();
for (int i = 0; i < dyct; i++) {
string key = tb.String();
int type = tb.Byte();
LexInfo li = null;
switch (type) {
case (int) LexInfo.LexSerCode.Simple:
li = new LISimple(tb.Int(), tb.Byte(), (STable)tb.ObjRef());
break;
case (int) LexInfo.LexSerCode.Sub:
li = new LISub(tb.Int(), (SubInfo)tb.ObjRef());
break;
case (int) LexInfo.LexSerCode.Label:
li = new LILabel(tb.Int());
break;
case (int) LexInfo.LexSerCode.Dispatch:
li = new LIDispatch(tb.Int());
break;
case (int) LexInfo.LexSerCode.Common:
li = new LICommon(tb.String());
break;
case (int) LexInfo.LexSerCode.Constant:
li = new LIConstant((Variable) tb.ObjRef());
break;
case (int) LexInfo.LexSerCode.Package:
li = new LIPackage((STable) tb.ObjRef());
break;
case (int) LexInfo.LexSerCode.Alias:
li = new LIAlias(tb.String());
break;
case (int) LexInfo.LexSerCode.AttrAlias:
li = new LIAttrAlias((STable)tb.ObjRef(), tb.String());
break;
default:
throw new ArgumentException(type.ToString());
}
n.dylex[key] = li;
li.owner = n;
li.name = key;
n.dylex_filter |= FilterForName(key);
}
// not saving dylex_filter as it is a cache
n.name = tb.String();
n.ltm = (LAD) tb.ObjRef();
n.special = tb.Int();
n.phaser = tb.Int();
n.outervar = tb.String();
n.unit = (RuntimeUnit)tb.ObjRef();
n.outer = (SubInfo)tb.ObjRef();
n.protosub = (P6any)tb.ObjRef();
n.protopad = (Frame)tb.ObjRef();
n.prototype = (SubInfo)tb.ObjRef();
n.cur_pkg = (STable)tb.ObjRef();
n.methodof = (STable)tb.ObjRef();
n.body_of = (STable)tb.ObjRef();
n.in_class = (STable)tb.ObjRef();
n.mo = (STable)tb.ObjRef();
// not storing caches here
// also not storing used_in_scope, it's compiler only data
// TODO: we want to store the compiled code not this
n.nam_str = tb.String();
if (n.nam_str != null)
n.nam_refs = tb.RefsA<object>();
n.param = tb.RefsA<object>();
n.children = tb.RefsL<SubInfo>();
int nex = tb.Int();
if (nex != 0) n.extend = new Dictionary<string,object[]>();
for (int i = 0; i < nex; i++)
n.extend[tb.String()] = tb.RefsA<object>();
tb.PushFixup(n);
return n;
}
void IFixup.Fixup() {
SubInfo sc = outer;
LexInfo li = null;
for (outer_topic_rank = 1; sc != null; sc = sc.outer) {
if (sc.dylex != null && sc.dylex.TryGetValue("$_", out li))
break;
outer_topic_rank++;
}
outer_topic_key = (li is LIVarish) ? (li as LIVarish).index : -1;
if (dylex.TryGetValue("self", out li)) {
self_key = li.SigIndex();
} else {
self_key = -1;
}
if (children != null) {
foreach (SubInfo z in children) {
if (z.phaser == Kernel.PHASER_CATCH)
catch_ = z;
if (z.phaser == Kernel.PHASER_CONTROL)
control = z;
}
}
if (phaser == Kernel.PHASER_CHECK)
Compartment.Top.check.Add(this, false);
if (phaser == Kernel.PHASER_INIT)
Compartment.Top.init.Add(this, false);
if (phaser == Kernel.PHASER_END)
Compartment.Top.end.Add(this, false);
if ((special & UNSAFE) != 0)
Kernel.CheckUnsafe(this);
}
}
// This object fills the combined roles of Parrot LexPad and CallContext
// objects.
public class Frame: P6any, IFixup {
// Used by trampoline to find destination
public int ip = 0;
public DynBlockDelegate code;
// Storage for lexicals: most subs have a smallish number of lexicals,
// and inlining them into the frame helps a lot. Since Frame objects
// are reused, bloating them doesn't hurt much
public object lex0;
public object lex1;
public object lex2;
public object lex3;
public object lex4;
public object lex5;
public object lex6;
public object lex7;
public object lex8;
public object lex9;
// special slot that is filled by the callee before returning
// used in some cases as a scratch slot by the codegen
public object resultSlot = null;
public int lexi0;
public int lexi1;
public object[] lexn; // Overflow for large subs
// Link fields for other related frames, objects
public Frame caller;
public Frame outer;
public SubInfo info; // the executing sub
// a doubly-linked list of frames being used by a given coroutine
public Frame reusable_child;
public Frame reuser;
// by being non-null marks the root frame of a coroutine, also
// stores the Frame to return to when take is called if != this
public Frame coro_return;
// used by nextsame to find where to go
public DispatchEnt curDisp;
// stores extra data needed in regex frames
public RxFrame rx;
// for CALLER::<&?BLOCK>
public P6any sub;
// linked list of LEAVE phasers to call at return time
public LeaveHook on_leave;
// stores original capture for callframe.args
public Variable[] pos;
public VarHash named;
// after MakeSub, GatherHelper
public const int SHARED = 1;
public const int CHECK_ONLY = 2;
public const int NO_JUNCTION = 4;
public int flags;
public Frame(Frame caller_, Frame outer_,
SubInfo info_, P6any sub_) {
caller = caller_;
outer = outer_;
code = info_.code;
info = info_;
sub = sub_;
mo = Compartment.Top.CallFrameMO;
lexn = (info_.nspill > 0) ? new object[info_.nspill] : null;
}
public Frame() { mo = Compartment.Top.CallFrameMO; }
public static readonly bool TraceCalls =
Environment.GetEnvironmentVariable("NIECZA_TRACE_CALLS") != null;
public static readonly bool VerboseExceptions =
Environment.GetEnvironmentVariable("NIECZA_VERBOSE_EXCEPTIONS") != null;
public static readonly bool AllExceptions =
Environment.GetEnvironmentVariable("NIECZA_ALL_EXCEPTIONS") != null;
public override string ReprName() { return "P6frame"; }
public Frame MakeChild(Frame outer, SubInfo info, P6any sub) {
if (reusable_child == null) {
reusable_child = new Frame();
reusable_child.reuser = this;
}
if (TraceCalls)
Console.WriteLine("{0}\t{1}", this.info.name, info.name);
reusable_child.ip = 0;
reusable_child.flags = 0;
reusable_child.resultSlot = null;
reusable_child.lexn = (info.nspill != 0) ? new object[info.nspill] : null;
reusable_child.on_leave = null;
reusable_child.coro_return = null;
reusable_child.lex0 = null;
reusable_child.lex1 = null;
reusable_child.lex2 = null;
reusable_child.lex3 = null;
reusable_child.lex4 = null;
reusable_child.lex5 = null;
reusable_child.lex6 = null;
reusable_child.lex7 = null;
reusable_child.lex8 = null;
reusable_child.lex9 = null;
reusable_child.curDisp = null;
reusable_child.caller = this;
reusable_child.outer = outer;
reusable_child.info = info;
reusable_child.sub = sub;
reusable_child.code = info.code;
reusable_child.pos = null;
reusable_child.named = null;
reusable_child.rx = null;
return reusable_child;
}
public Frame Continue() {
return code(this);
}
public Variable ExtractNamed(string n) {
Variable r;
if (named != null && named.TryGetValue(n, out r)) {
named.Remove(n);
return r;
} else {
return null;
}
}
internal void EnsureSpills(int nr) {
if (nr > 0 && (lexn == null || lexn.Length < nr))
Array.Resize(ref lexn, nr);
}
public void MarkShared() {
if (0 == (flags & SHARED)) {
flags |= SHARED;
if (reuser != null) reuser.reusable_child = reusable_child;
if (reusable_child != null) reusable_child.reuser = reuser;
reuser = reusable_child = null;
}
}
// when control might re-enter a function
public void MarkSharedChain() {
for (Frame x = this; x != null; x = x.caller)
x.MarkShared();
}
public int ExecutingLine() {
if (info != null && info.lines != null) {
return ip >= info.lines.Length ? 0 : info.lines[ip];
} else {
return 0;
}
}
public Variable GetArgs() {
P6any nw = new P6opaque(Compartment.Top.CaptureMO);
var poscap = pos ?? new Variable[0];
if (sub != null && poscap.Length > 0 && sub.Does(Compartment.Top.MethodMO)) {
// Hide the self value so that using |callframe.args in a
// nextwith call will DTRT
poscap = new Variable[pos.Length - 1];
Array.Copy(pos, 1, poscap, 0, poscap.Length);
}
nw.SetSlot(Compartment.Top.CaptureMO, "$!positionals", poscap);
nw.SetSlot(Compartment.Top.CaptureMO, "$!named", named);
return nw;
}
public string DescribeArgs() {
string ret = null;
try {
Variable a = GetArgs();
Variable sa = Kernel.RunInferior(a.Fetch().InvokeMethod(
Kernel.GetInferiorRoot(), "perl", new Variable[] { a },
null));
ret = sa.Fetch().mo.mro_raw_Str.Get(sa);
} catch (Exception ex) {
ret = "[cannot display arguments: " + ex + "]";
}
return ret;
}
public string ExecutingFile() {
if (info != null && info.unit != null && info.unit.filename != null)
return info.unit.filename;
else
return "<unknown>";
}
public void SetDynamic(int ix, object v) {
switch(ix) {
case 0: lex0 = v; break;
case 1: lex1 = v; break;
case 2: lex2 = v; break;
case 3: lex3 = v; break;
case 4: lex4 = v; break;
case 5: lex5 = v; break;
case 6: lex6 = v; break;
case 7: lex7 = v; break;
case 8: lex8 = v; break;
case 9: lex9 = v; break;
default: lexn[ix-10] = v; break;
}
}
public bool TryBindDynamic(string name, uint mask, object to) {
if ((info.dylex_filter & mask) != mask)
return false;
int ix;
LexInfo li;
if ((ix = info.FindControlEnt(ip, SubInfo.ON_VARLOOKUP, name)) >= 0)
SetDynamic(ix, to);
else if (info.dylex.TryGetValue(name, out li))
li.Set(this, to);
else
return false;
return true;
}
public bool TryGetDynamic(string name, uint mask, out object v) {
v = null;
if ((info.dylex_filter & mask) != mask)
return false;
int ix;
LexInfo li;
if ((ix = info.FindControlEnt(ip, SubInfo.ON_VARLOOKUP, name))>=0)
v = GetDynamic(ix);
else if (info.dylex.TryGetValue(name, out li))
v = li.Get(this);
else
return false;
return true;
}
public object GetDynamic(int ix) {
switch(ix) {
case 0: return lex0;
case 1: return lex1;
case 2: return lex2;
case 3: return lex3;
case 4: return lex4;
case 5: return lex5;
case 6: return lex6;
case 7: return lex7;
case 8: return lex8;
case 9: return lex9;
default: return lexn[ix-10];
}
}
public Variable LexicalFind(string name) {
Frame csr = this;
uint m = SubInfo.FilterForName(name);
while (csr != null) {
object o;
if (csr.TryGetDynamic(name, m, out o)) {
return (Variable)o;
}
csr = csr.outer;
}
return Compartment.Top.AnyP;
}
public void LexicalBind(string name, Variable to) {
Frame csr = this;
uint m = SubInfo.FilterForName(name);
while (csr != null) {
if (csr.TryBindDynamic(name, m, to))
return;
csr = csr.outer;
}
if (name == "$!" || name == "$/") return;
throw new NieczaException("cannot bind " + name + " in " + info.name);
}
public static Frame Binder(Frame th) {
if (th.info.sig == null || th.pos == null) return th;
int jun_pivot = -1;
int jun_rank = int.MaxValue;
string jun_pivot_n = null;
bool rawc = BindSignature(th, th.info.sig, th.flags, th.info.name,
th.pos, th.named, ref jun_pivot, ref jun_pivot_n,
ref jun_rank);
if (rawc && jun_pivot != -1 && (th.flags & CHECK_ONLY) == 0) {
Variable jct = (jun_pivot == -2 ? th.named[jun_pivot_n] :
th.pos[jun_pivot]);
var sav_named = th.named;
var sav_pos = th.pos;
var sav_info = th.info;
var sav_outer = th.outer;
var sav_sub = th.sub;
var sav_disp = th.curDisp;
Frame nth = th.Return().MakeChild(null, Compartment.Top.AutoThreadSubSI, Compartment.Top.AnyP);
P6opaque jo = (P6opaque) jct.Fetch();
nth.named = sav_named;
nth.pos = sav_pos;
nth.lex1 = sav_info;
nth.lex2 = jun_pivot_n;
nth.lex3 = jo.slots[0];
nth.lex4 = Kernel.UnboxAny<Variable[]>((P6any)jo.slots[1]);
nth.lex5 = sav_outer;
nth.lex6 = sav_sub;
nth.lex7 = sav_disp;
nth.lex8 = new Variable[((Variable[])nth.lex4).Length];
nth.lex9 = jct;
nth.lexi0 = jun_pivot;
nth.lexi1 = 0;
return nth;
}
if ((th.flags & CHECK_ONLY) != 0) {
th.caller.resultSlot = rawc ? Compartment.Top.TrueV : Compartment.Top.FalseV;
return th.Return();
} else {
return th;
}
}
static bool RunConstraint(Frame th, string signame, Parameter param,
bool quiet, Variable arg, object c) {
if (c is Signature) {
// Sub-signature
// dummy values; no junctional operation in subsigs
int jun_pivot = -1, jun_rank = int.MaxValue;
string jun_pivot_n = null;
P6any cap = Kernel.RunInferior(arg.Fetch().InvokeMethod(
Kernel.GetInferiorRoot(), "Capture", new Variable[] { arg },
null)).Fetch();
return BindSignature(th, (Signature)c,
NO_JUNCTION + (quiet ? CHECK_ONLY : 0), param.name,
(Variable[])cap.GetSlot(Compartment.Top.CaptureMO, "$!positionals"),
(VarHash)cap.GetSlot(Compartment.Top.CaptureMO, "$!named"),
ref jun_pivot, ref jun_pivot_n, ref jun_rank);
}
if (c is SubInfo) {
Frame thn = Kernel.GetInferiorRoot()
.MakeChild(th, (SubInfo)c, Compartment.Top.AnyP);
var sm = Kernel.RunInferior(thn);
bool res = Kernel.ACCEPTS(arg, sm);
if (!res && !quiet)
throw new NieczaException("Constraint type check failed for parameter '" + param.name + "' in '" + signame + "'");
return res;
}
if (c is Variable) {
bool res = Kernel.ACCEPTS(arg, (Variable)c);
if (!res && !quiet)
throw new NieczaException("Constraint type check failed for parameter '" + param.name + "' in '" + signame + "'");
return res;
}
throw new NieczaException("funny object in post-constraint list");
}
public static bool BindSignature(Frame th, Signature sig, int mode,
string signame, Variable[] pos, VarHash named,
ref int jun_pivot, ref string jun_pivot_n, ref int jun_rank) {
Parameter[] pbuf = sig.parms;
int posc = 0;
HashSet<string> namedc = null;
bool quiet = (mode & CHECK_ONLY) != 0;
if (named != null)
namedc = new HashSet<string>(named.Keys);
int pend = pbuf.Length;
int pix = 0;
int obj_src = -1;
string obj_src_n = null;
while (pix != pend) {
Parameter param = pbuf[pix++];
int flags = param.flags;
int slot = param.slot;
string[] names = param.names;
STable type = param.type;
obj_src = -1;
Variable src = null;
if ((flags & Parameter.SLURPY_PCL) != 0) {
src = (slot >= 0) ? Kernel.BoxAnyMO(pos, Compartment.Top.ParcelMO) :
Compartment.Top.AnyP;
posc = pos.Length;
goto gotit;
}
if ((flags & Parameter.SLURPY_CAP) != 0) {
if (slot < 0) {
src = Compartment.Top.AnyP;
named = null; namedc = null; posc = pos.Length;
goto gotit;
}
P6any nw = new P6opaque(Compartment.Top.CaptureMO);
Variable[] spos = new Variable[pos.Length - posc];
Array.Copy(pos, posc, spos, 0, spos.Length);
VarHash snamed = null;
if (named != null) {
snamed = new VarHash();
foreach (string k in named.Keys) {
if (namedc.Contains(k))
snamed[k] = named[k];
}
}
nw.SetSlot(Compartment.Top.CaptureMO, "$!positionals", spos);
nw.SetSlot(Compartment.Top.CaptureMO, "$!named", snamed);
src = nw;
named = null; namedc = null; posc = pos.Length;
goto gotit;
}
if ((flags & Parameter.SLURPY_POS) != 0) {
P6any l = new P6opaque(Compartment.Top.ListMO);
Kernel.IterToList(l, Kernel.IterFlatten(
Kernel.SlurpyHelper(pos, posc)));
src = Kernel.NewRWListVar(l);
posc = pos.Length;
goto gotit;
}
if ((flags & Parameter.SLURPY_NAM) != 0) {
VarHash nh = new VarHash();
if (named != null) {
foreach (KeyValuePair<string,Variable> kv in named)
if (namedc.Contains(kv.Key))
nh[kv.Key] = kv.Value;
named = null;
namedc = null;
}
src = Kernel.BoxAnyMO(nh, Compartment.Top.HashMO);
goto gotit;
}
if (names != null && named != null) {
for (int ni = 0; ni < names.Length; ni++) {
string n = names[ni];
if (namedc.Contains(n)) {
namedc.Remove(n);
src = named[n];
obj_src_n = n;
obj_src = -2;
goto gotit;
}
}
}
if ((flags & Parameter.POSITIONAL) != 0 && posc != pos.Length) {
obj_src = posc;
src = pos[posc++];
goto gotit;
}
get_default:
if ((flags & Parameter.HASDEFAULT) != 0) {
if (param.def is Variable) {
src = (Variable)param.def;
} else {
Frame thn = Kernel.GetInferiorRoot()
.MakeChild(th, (SubInfo)param.def, Compartment.Top.AnyP);
src = Kernel.RunInferior(thn);
if (src == null)
throw new Exception("Improper null return from sub default for '" + param.name + "' in '" + signame + "'");
}
goto gotit;
}
if ((flags & Parameter.DEFOUTER) != 0) {
Frame f = th;
if (th.info.outer_topic_key < 0) {
src = Compartment.Top.AnyP;
goto gotit;
}
for (int i = 0; i < th.info.outer_topic_rank; i++) f = f.outer;
src = (Variable)f.GetDynamic(th.info.outer_topic_key);
goto gotit;
}
if ((flags & Parameter.OPTIONAL) != 0) {
// Array is the "default" Positional -masak
if ((flags & Parameter.IS_LIST) != 0)
src = Kernel.CreateArray();
else if ((flags & Parameter.IS_HASH) != 0)
src = Kernel.CreateHash();
else
src = type.initObj;
goto gotit;
}
if (quiet) return false;
throw new NieczaException("No value for parameter '" + param.name + "' in '" + signame + "'");
gotit:
switch ((flags & Parameter.DEF_MASK) >> Parameter.DEF_SHIFT) {
// TODO: Failure will make these cases different
case Parameter.TYPE_ONLY >> Parameter.DEF_SHIFT:
case Parameter.UNDEF_ONLY >> Parameter.DEF_SHIFT:
if (!src.Fetch().IsDefined())
break;
if (quiet) return false;
throw new NieczaException("Parameter '" + param.name + "' in '" + signame + "' requires an undefined argument");
case Parameter.DEF_ONLY >> Parameter.DEF_SHIFT:
if (src.Fetch().IsDefined())
break;
if (quiet) return false;
throw new NieczaException("Parameter '" + param.name + "' in '" + signame + "' requires a defined argument");
default:
break;
}
if ((flags & Parameter.RWTRANS) != 0) {
} else if ((flags & Parameter.IS_COPY) != 0) {
if ((flags & Parameter.IS_HASH) != 0)
src = Kernel.Assign(Kernel.CreateHash(),
Kernel.NewRWListVar(src.Fetch()));
else if ((flags & Parameter.IS_LIST) != 0)
src = Kernel.Assign(Kernel.CreateArray(),
Kernel.NewRWListVar(src.Fetch()));
else
src = Kernel.Assign(Kernel.NewTypedScalar(type), src);
} else {
bool islist = ((flags & (Parameter.IS_HASH | Parameter.IS_LIST)) != 0);
bool rw = ((flags & Parameter.READWRITE) != 0) && !islist;
P6any srco = src.Fetch();
// XXX: in order for calling methods on Compartment.Top.Nil to work,
// self needs to be ignored here.
if (srco == Compartment.Top.NilP && obj_src != -1 &&
(flags & Parameter.INVOCANT) == 0) {
obj_src = -1;
goto get_default;
}
if ((flags & Parameter.IS_LIST) != 0)
type = Compartment.Top.PositionalMO;
if ((flags & Parameter.IS_HASH) != 0)
type = Compartment.Top.AssociativeMO;
if ((flags & Parameter.CALLABLE) != 0)
type = Compartment.Top.CallableMO;
if (!srco.Does(type)) {
if (quiet) return false;
if (srco.mo.HasType(Compartment.Top.JunctionMO) && obj_src != -1 && (mode & NO_JUNCTION) == 0) {
int jrank = Kernel.UnboxAny<int>((P6any) ((P6opaque)srco).slots[0]) / 2;
if (jrank < jun_rank) {
jun_rank = jrank;
jun_pivot = obj_src;
jun_pivot_n = obj_src_n;
}
continue;
}
throw new NieczaException("Nominal type check failed in binding '" + param.name + "' in '" + signame + "'; got " + srco.mo.name + ", needed " + type.name);
}
if (rw) {
if (src.Rw) {
// this will be a functional RW binding
src.Vivify();
goto bound;
} else {
if (quiet) return false;
throw new NieczaException("Binding '" + param.name + "' in '" + signame + "', cannot bind read-only value to is rw parameter");
}
}
else {
if (src.Mode == (islist ? Variable.LIST : Variable.RO))
goto bound;
src = islist ? (Variable)new ListVariable(srco) : srco;
}
bound: ;
}
if (param.attribute != null) {
object self;
if (!th.TryGetDynamic("self", 0, out self))
throw new NieczaException("No 'self' available for attributive?");
Variable selfv = (Variable)self;
if (param.attribute[1] == '!') {
Kernel.Assign(((Variable)(selfv.Fetch().
GetSlot(param.attribute_type, param.attribute))),
src);
} else {
Variable dest = Kernel.RunInferior(selfv.Fetch().
InvokeMethod(Kernel.GetInferiorRoot(),
param.attribute.Substring(2),
new Variable[] { selfv }, null));
Kernel.Assign(dest, src);
}
}
if ((flags & Parameter.INVOCANT) != 0 && th.info.self_key >= 0)
th.SetDynamic(th.info.self_key, src);
switch (slot + 1) {
case 0: break;
case 1: th.lex0 = src; break;
case 2: th.lex1 = src; break;
case 3: th.lex2 = src; break;
case 4: th.lex3 = src; break;
case 5: th.lex4 = src; break;
case 6: th.lex5 = src; break;
case 7: th.lex6 = src; break;
case 8: th.lex7 = src; break;
case 9: th.lex8 = src; break;
case 10: th.lex9 = src; break;
default: th.lexn[slot - 10] = src; break;
}
// Really, this should be earlier (next to the :D/:U checks);
// it's not right to be binding variables before doing
// constraint checks. We're compromising a bit of purity here
// for the sake of making 'Int $odd where { $odd % 2 }' DWIM.
if (param.post_constraints != null) {
foreach (object c in param.post_constraints)
if (!RunConstraint(th, signame, param, quiet, src, c))
return false;
}
}
if (posc != pos.Length || namedc != null && namedc.Count != 0) {
if (quiet) return false;
string m = "Excess arguments to " + signame;
if (posc != pos.Length)
m += string.Format(", used {0} of {1} positionals",
posc, pos.Length);
if (namedc != null && namedc.Count != 0)
m += ", unused named " + Kernel.JoinS(", ", namedc);
throw new NieczaException(m);
}
return true;
}
public void PushLeave(int type, P6any thunk) {
LeaveHook l = new LeaveHook();
l.next = on_leave; on_leave = l;
l.thunk = thunk; l.type = type;
}
public Frame DynamicCaller() {
if (coro_return == null)
return caller;
return (Frame) coro_return;
}
[TrueGlobal]
private static List<string> spacey = new List<string>();
public string DepthMark() {
Frame f = this;
int ix = 0;
while (f != null) { ix++; f = f.caller; }
while (spacey.Count <= ix) { spacey.Add(new String(' ', spacey.Count * 2)); }
return spacey[ix];
}
public override string ToString() {
return "frame:" + info + "/" + GetHashCode();
}
public Frame Return() {
if (on_leave != null) {
Variable ret = (Variable) caller.resultSlot;
bool ok = ret.Fetch().IsDefined();
LeaveHook c = on_leave;
// if exception is thrown, do not rerun LEAVEs
on_leave = null;
for (; c != null; c = c.next) {
if (0 == ((ok ? LeaveHook.KEEP : LeaveHook.UNDO) & c.type))
continue;
Variable r = Kernel.RunInferior(c.thunk.Invoke(
Kernel.GetInferiorRoot(), new Variable[] {ret}, null));
if ((c.type & LeaveHook.DIE) != 0 &&
!r.Fetch().mo.mro_raw_Bool.Get(r))
throw new NieczaException("Post-constraint failed for " + info.name);
}
}
return caller;
}
public override void Freeze(FreezeBuffer fb) {
fb.Byte((byte) SerializationCode.Frame);
fb.Int(ip);
// code will be reloaded from info.code
fb.ObjRef(lex0);
fb.ObjRef(lex1);
fb.ObjRef(lex2);
fb.ObjRef(lex3);
fb.ObjRef(lex4);
fb.ObjRef(lex5);
fb.ObjRef(lex6);
fb.ObjRef(lex7);
fb.ObjRef(lex8);
fb.ObjRef(lex9);
fb.ObjRef(resultSlot); // probably not necessary
fb.Int(lexi0);
fb.Int(lexi1);
fb.Refs(lexn);
fb.ObjRef(caller); // XXX this might serialize too much
fb.ObjRef(outer);
fb.ObjRef(info);
// we won't store reuse info :)
fb.ObjRef(coro_return);
fb.ObjRef(curDisp);
fb.ObjRef(rx);
fb.ObjRef(sub);
for (LeaveHook l = on_leave; l != null; l = l.next) {
fb.Byte(checked((byte)l.type));
fb.ObjRef(l.thunk);
}
fb.Byte(LeaveHook.SENTINEL);
fb.Refs(pos);
fb.ObjRef(named);
fb.Byte(checked((byte)flags));
}
internal static Frame Thaw(ThawBuffer tb) {
Frame n = new Frame();
tb.Register(n);
n.ip = tb.Int();
// code will be reloaded from info.code
n.lex0 = tb.ObjRef();
n.lex1 = tb.ObjRef();
n.lex2 = tb.ObjRef();
n.lex3 = tb.ObjRef();
n.lex4 = tb.ObjRef();
n.lex5 = tb.ObjRef();
n.lex6 = tb.ObjRef();
n.lex7 = tb.ObjRef();
n.lex8 = tb.ObjRef();
n.lex9 = tb.ObjRef();
n.resultSlot = tb.ObjRef(); // probably not necessary
n.lexi0 = tb.Int();
n.lexi1 = tb.Int();
n.lexn = tb.RefsA<object>();
n.caller = (Frame)tb.ObjRef(); // XXX this might serialize too much
n.outer = (Frame)tb.ObjRef();
n.info = (SubInfo)tb.ObjRef();
// we won't store reuse info :)
n.coro_return = (Frame)tb.ObjRef();
n.curDisp = (DispatchEnt)tb.ObjRef();
n.rx = (RxFrame)tb.ObjRef();
n.sub = (P6any)tb.ObjRef();
LeaveHook fin = null;
int type = tb.Byte();
while (type != LeaveHook.SENTINEL) {
if (fin == null) {
fin = n.on_leave = new LeaveHook();
} else {
fin.next = new LeaveHook();
fin = fin.next;
}
fin.type = type;
fin.thunk = (P6any) tb.ObjRef();
type = tb.Byte();
}
n.pos = tb.RefsA<Variable>();
n.named = (VarHash)tb.ObjRef();
n.flags = tb.Byte();
tb.PushFixup(n);
return n;
}
void IFixup.Fixup() {
code = info.code;
}
}
public class NieczaException: Exception {
// hide clr stack trace for these
public override string ToString() { return Message; }
public NieczaException(string detail) : base(detail) {}
public NieczaException(string fmt, params object[] args) : base(string.Format(fmt, args)) {}
public NieczaException() : base() {}
}
public class ResumeUnwindException: Exception {
public readonly int type, to_ip;
public readonly Frame to_frame;
public readonly object to_data;
public readonly string p6backtrace;
public override string ToString() { return p6backtrace; }
public ResumeUnwindException(int type, Frame tf, int tip, object td,
string bt) : base(bt) {
p6backtrace = bt;
to_data = td;
to_ip = tip;
to_frame = tf;
this.type = type;
}
}
class InvokeSub : InvokeHandler {
public override Frame Invoke(P6any th, Frame caller,
Variable[] pos, VarHash named) {
if (!th.IsDefined())
return Kernel.Die(caller, "Cannot invoke an undef sub");
P6opaque dyo = ((P6opaque) th);
Frame outer = (Frame) dyo.slots[0];
SubInfo info = (SubInfo) dyo.slots[1];
return info.SetupCall(caller, outer, th, pos, named, false, null);
}
}
class InvokeCallMethod : InvokeHandler {
public override Frame Invoke(P6any th, Frame caller,
Variable[] pos, VarHash named) {
Variable[] np = new Variable[pos.Length + 1];
Array.Copy(pos, 0, np, 1, pos.Length);
np[0] = th;
return th.InvokeMethod(caller, "postcircumfix:<( )>", np, named);
}
}
class PushyCallMethod : PushyHandler {
string method;
public PushyCallMethod(string method) { this.method = method; }
public PushyCallMethod() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override Variable Invoke(Variable obj, Variable[] args) {
Variable[] rargs = new Variable[args.Length + 1];
Array.Copy(args, 0, rargs, 1, args.Length);
rargs[0] = obj;
return Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, rargs, null));
}
}
class CtxCallMethodUnbox<T> : ContextHandler<T> {
string method;
public CtxCallMethodUnbox(string method) { this.method = method; }
public CtxCallMethodUnbox() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override T Get(Variable obj) {
Variable v = Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, new Variable[] { obj }, null));
return Kernel.UnboxAny<T>(v.Fetch());
}
}
class CtxCallMethodUnboxBool : ContextHandler<bool> {
string method;
public CtxCallMethodUnboxBool(string method) { this.method = method; }
public CtxCallMethodUnboxBool() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override bool Get(Variable obj) {
Variable v = Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, new Variable[] { obj }, null));
return Kernel.UnboxAny<int>(v.Fetch()) != 0;
}
}
class CtxCallMethodUnboxNumeric : ContextHandler<double> {
string method;
public CtxCallMethodUnboxNumeric(string method) { this.method = method; }
public CtxCallMethodUnboxNumeric() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override double Get(Variable obj) {
Variable v = method == null ? obj : Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, new Variable[] { obj }, null));
P6any o = v.Fetch();
if (o.mo.HasType(Compartment.Top.NumMO)) {
return Kernel.UnboxAny<double>(o);
} else if (o.mo.HasType(Compartment.Top.IntMO)) {
if (o is BoxObject<int>) {
return (double)Kernel.UnboxAny<int>(o);
} else {
return (double)Kernel.UnboxAny<BigInteger>(o);
}
} else if (o.mo.HasType(Compartment.Top.RatMO)) {
Rat r = Kernel.UnboxAny<Rat>(o);
return (double)r.num / (double)r.den;
} else if (o.mo.HasType(Compartment.Top.FatRatMO)) {
FatRat r = Kernel.UnboxAny<FatRat>(o);
return (double)r.num / (double)r.den;
} else if (o.mo.HasType(Compartment.Top.ComplexMO)) {
Complex r = Kernel.UnboxAny<Complex>(o);
if (r.im != 0)
throw new NieczaException("coercion would discard nonzero imaginary part");
return r.re;
} else {
throw new NieczaException("Numeric failed to return core numeric type");
}
}
}
class CtxCallMethod : ContextHandler<Variable> {
string method;
public CtxCallMethod(string method) { this.method = method; }
public CtxCallMethod() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override Variable Get(Variable obj) {
return Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, new Variable[] { obj }, null));
}
}
class CtxCallMethodFetch : ContextHandler<P6any> {
string method;
public CtxCallMethodFetch(string method) { this.method = method; }
public CtxCallMethodFetch() { }
protected override object[] GetData() { return new object[]{method};}
protected override void SetData(object[]o) { method = (string)o[0]; }
public override P6any Get(Variable obj) {
return Kernel.RunInferior(obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), method, new Variable[] { obj }, null)).Fetch();
}
}
class CtxJustUnbox<T> : ContextHandler<T> {
T dflt;
public CtxJustUnbox(T dflt) { this.dflt = dflt; }
public CtxJustUnbox() { }
protected override object[] GetData() { return new object[]{dflt};}
protected override void SetData(object[]o) { dflt = (T)o[0]; }
public override T Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return dflt;
return Kernel.UnboxAny<T>(o);
}
}
class CtxBoolUnbox : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return false;
return Kernel.UnboxAny<int>(o) != 0;
}
}
class CtxReturnSelf : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
return obj.Fetch();
}
}
class CtxReturnSelfList : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
if (obj.List) return obj;
return Kernel.NewRWListVar(obj.Fetch());
}
}
class CtxReturnSelfItem : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
if (!obj.List) return obj;
return obj.Fetch();
}
}
class CtxAnyList : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
VarDeque itr = new VarDeque(obj.List ? obj.Fetch() : obj);
P6any l = new P6opaque(Compartment.Top.ListMO);
Kernel.IterToList(l, itr);
return Kernel.NewRWListVar(l);
}
}
class IxParcelLISTSTORE : IndexHandler {
public override Variable Get(Variable lhs, Variable rhs) {
VarDeque src = Builtins.start_iter(rhs);
P6any lhs_o = lhs.Fetch();
if (!lhs_o.IsDefined())
throw new NieczaException("assigning to undefined parcel");
Variable[] dsts = Kernel.UnboxAny<Variable[]>(lhs_o);
P6any[] srcs = new P6any[dsts.Length];
for (int i = 0; i < dsts.Length; i++) {
Variable d = dsts[i];
if (d.List) {
srcs[i] = new P6opaque(Compartment.Top.ListMO);
Kernel.IterToList(srcs[i], src);
src = new VarDeque();
} else {
srcs[i] = Kernel.IterHasFlat(src, true) ?
src.Shift().Fetch() : Compartment.Top.AnyP;
}
}
for (int i = 0; i < dsts.Length; i++) {
dsts[i].AssignO(srcs[i], true);
}
return lhs;
}
}
class CtxParcelList : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
P6any o = obj.Fetch();
VarDeque itr = o.IsDefined() ? new VarDeque(Kernel.UnboxAny<Variable[]>(o)) : new VarDeque();
P6any l = new P6opaque(Compartment.Top.ListMO);
Kernel.IterToList(l, itr);
return Kernel.NewRWListVar(l);
}
}
// used for both
class CtxParcelListStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
VarDeque itr = o.mo.mro_raw_iterator.Get(obj);
StringBuilder sb = new StringBuilder();
while (Kernel.IterHasFlat(itr, true)) {
var n = itr.Shift();
sb.Append(n.Fetch().mo.mro_raw_Str.Get(n)).Append(' ');
}
if (sb.Length != 0) sb.Length--;
return sb.ToString();
}
}
class CtxBoxify<T> : ContextHandler<Variable> {
ContextHandler<T> inner;
STable box;
public CtxBoxify() { }
protected override object[] GetData() {
return new object[] { inner, box };
}
protected override void SetData(object[] o) {
inner = (ContextHandler<T>)o[0];
box = (STable)o[1];
}
public CtxBoxify(ContextHandler<T> inner, STable box) {
this.inner = inner;
this.box = box;
}
public override Variable Get(Variable obj) {
return Kernel.BoxAnyMO<T>(inner.Get(obj), box);
}
}
class CtxBoxifyInty : ContextHandler<Variable> {
ContextHandler<double> inner;
public CtxBoxifyInty() { }
protected override object[] GetData() {
return new object[] { inner };
}
protected override void SetData(object[] o) {
inner = (ContextHandler<double>)o[0];
}
public CtxBoxifyInty(ContextHandler<double> inner) {
this.inner = inner;
}
public override Variable Get(Variable obj) {
return Builtins.MakeInt((long)inner.Get(obj));
}
}
class CtxContainerize : ContextHandler<Variable> {
ContextHandler<P6any> inner;
public CtxContainerize() { }
protected override object[] GetData() {
return new object[] { inner };
}
protected override void SetData(object[] o) {
inner = (ContextHandler<P6any>)o[0];
}
public CtxContainerize(ContextHandler<P6any> inner) {
this.inner = inner;
}
public override Variable Get(Variable obj) {
return inner.Get(obj);
}
}
class CtxParcelIterator : ContextHandler<VarDeque> {
public override VarDeque Get(Variable obj) {
P6any o = obj.Fetch();
return o.IsDefined() ? new VarDeque(Kernel.UnboxAny<Variable[]>(o)) : new VarDeque();
}
}
class IxArrayLISTSTORE : IndexHandler {
public override Variable Get(Variable lhs, Variable rhs) {
P6any lhs_o = lhs.Fetch();
if (!lhs_o.IsDefined())
throw new NieczaException("LISTSTORE to undefined Array");
VarDeque iter = Builtins.start_iter(rhs);
VarDeque items = new VarDeque();
while (Kernel.IterHasFlat(iter, true))
items.Push(Kernel.NewMuScalar(iter.Shift().Fetch()));
lhs_o.SetSlot(Compartment.Top.ListMO, "$!items", items);
lhs_o.SetSlot(Compartment.Top.ListMO, "$!rest", iter); /*now empty*/
return lhs;
}
}
class CtxListIterator : ContextHandler<VarDeque> {
public override VarDeque Get(Variable obj) {
var d = obj.Fetch();
if (!d.IsDefined()) return new VarDeque();
VarDeque r = new VarDeque( (VarDeque) d.GetSlot(Compartment.Top.ListMO, "$!items" ));
r.PushD((VarDeque) d.GetSlot(Compartment.Top.ListMO, "$!rest"));
return r;
}
}
class PopList : ContextHandler<Variable> {
public override Variable Get(Variable v) {
P6any o = v.Fetch();
if (!o.IsDefined()) return Compartment.Top.AnyP;
VarDeque items = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!items");
VarDeque rest = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!rest");
while (Kernel.IterHasFlat(rest, false))
items.Push(rest.Shift());
return (items.Count() != 0) ? items.Pop() : Compartment.Top.AnyP;
}
}
class ShiftList : ContextHandler<Variable> {
public override Variable Get(Variable v) {
P6any o = v.Fetch();
if (!o.IsDefined()) return Compartment.Top.AnyP;
VarDeque items = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!items");
VarDeque rest = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!rest");
if (items.Count() != 0)
return items.Shift();
if (Kernel.IterHasFlat(rest, false))
return rest.Shift();
return Compartment.Top.AnyP;
}
}
class UnshiftList : PushyHandler {
public override Variable Invoke(Variable v, Variable[] args) {
P6any o = v.Fetch();
if (!o.IsDefined())
throw new NieczaException("Cannot push onto type object");
VarDeque iter = new VarDeque(args);
VarDeque targ = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!items");
VarDeque st = new VarDeque();
while (Kernel.IterHasFlat(iter, true))
st.Push(Kernel.NewMuScalar(iter.Shift().Fetch()));
targ.UnshiftD(st);
return v;
}
}
class PushList : PushyHandler {
public override Variable Invoke(Variable v, Variable[] args) {
P6any o = v.Fetch();
if (!o.IsDefined())
throw new NieczaException("Cannot push onto type object");
VarDeque iter = new VarDeque(args);
VarDeque targ = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!rest");
if (targ.Count() == 0) targ = (VarDeque)o.GetSlot(Compartment.Top.ListMO, "$!items");
while (Kernel.IterHasFlat(iter, true))
targ.Push(Kernel.NewMuScalar(iter.Shift().Fetch()));
return v;
}
}
class IxHashLISTSTORE : IndexHandler {
public override Variable Get(Variable lhs, Variable rhs) {
P6any lhs_o = lhs.Fetch();
if (!lhs_o.IsDefined())
throw new NieczaException("LISTSTORE to undefined Hash");
VarHash into = new VarHash();
VarDeque iter = Builtins.start_iter(rhs);
bool first = true;
while (Kernel.IterHasFlat(iter, true)) {
P6any elt = iter.Shift().Fetch();
if (first && elt.mo.HasType(Compartment.Top.HashMO)) {
foreach(KeyValuePair<string,Variable> kv in
Kernel.UnboxAny<VarHash>(elt)) {
into[kv.Key] = kv.Value;
}
} else if (elt.mo.HasType(Compartment.Top.PairMO)) {
Variable k = (Variable) elt.GetSlot(Compartment.Top.EnumMO, "$!key");
Variable v = (Variable) elt.GetSlot(Compartment.Top.EnumMO, "$!value");
into[k.Fetch().mo.mro_raw_Str.Get(k)] =
Kernel.NewMuScalar(v.Fetch());
} else {
if (!Kernel.IterHasFlat(iter, true))
throw new NieczaException("Unmatched key in Hash.LISTSTORE");
into[elt.mo.mro_raw_Str.Get(elt)] =
Kernel.NewMuScalar(iter.Shift().Fetch());
}
first = false;
}
Kernel.SetBox<VarHash>(lhs_o, into);
return lhs;
}
}
class CtxHashIterator : ContextHandler<VarDeque> {
public override VarDeque Get(Variable obj) {
return Builtins.HashIterRaw(3, obj);
}
}
class CtxHashBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
return o.IsDefined() && Kernel.UnboxAny<VarHash>(o).IsNonEmpty;
}
}
class CtxRawNativeDefined : ContextHandler<bool> {
public override bool Get(Variable obj) {
return obj.Fetch().IsDefined();
}
}
class CtxBoolNativeDefined : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
return obj.Fetch().IsDefined() ? Compartment.Top.TrueV : Compartment.Top.FalseV;
}
}
class CtxNumSuccish : ContextHandler<P6any> {
double amt;
public CtxNumSuccish() { }
protected override object[] GetData() { return new object[] { amt }; }
protected override void SetData(object[] o) { amt = (double) o[0]; }
public CtxNumSuccish(double amt) { this.amt = amt; }
public override P6any Get(Variable obj) {
P6any o = obj.Fetch();
double v = o.IsDefined() ? Kernel.UnboxAny<double>(o) : 0;
return Kernel.BoxRaw(v + amt, Compartment.Top.NumMO);
}
}
class CtxRawNativeNum2Str : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
return o.IsDefined() ? Utils.N2S(Kernel.UnboxAny<double>(o)) : "Num()";
}
}
class CtxNum2Bool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
return o.IsDefined() && Kernel.UnboxAny<double>(o) != 0;
}
}
class CtxIntSuccish : ContextHandler<P6any> {
int amt;
public CtxIntSuccish() { }
protected override object[] GetData() { return new object[] { amt }; }
protected override void SetData(object[] o) { amt = (int) o[0]; }
public CtxIntSuccish(int amt) { this.amt = amt; }
public override P6any Get(Variable obj) {
P6any o = obj.Fetch();
int v;
if (o is BoxObject<BigInteger>) {
BigInteger bn = Kernel.UnboxAny<BigInteger>(o) + amt;
if (bn.AsInt32(out v))
return Kernel.BoxRaw<int>(v, Compartment.Top.IntMO);
else
return Kernel.BoxRaw<BigInteger>(bn, Compartment.Top.IntMO);
}
v = o.IsDefined() ? Kernel.UnboxAny<int>(o) : 0;
if (v == (amt > 0 ? int.MaxValue : int.MinValue))
return Kernel.BoxRaw<BigInteger>(amt + (long)v, Compartment.Top.IntMO);
return Kernel.BoxRaw(v + amt, Compartment.Top.IntMO);
}
}
class CtxIntStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return o.mo.name + "()"; }
else if (o is BoxObject<int>) { return Utils.N2S(Kernel.UnboxAny<int>(o)); }
else { return Kernel.UnboxAny<BigInteger>(o).ToString(); }
}
}
class CtxIntBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return false; }
else if (o is BoxObject<int>) { return Kernel.UnboxAny<int>(o)!=0; }
else { return Kernel.UnboxAny<BigInteger>(o) != BigInteger.Zero; }
}
}
class CtxRatSuccish : ContextHandler<P6any> {
bool up;
public CtxRatSuccish() { }
protected override object[] GetData() { return new object[] { up }; }
protected override void SetData(object[] o) { up = (bool) o[0]; }
public CtxRatSuccish(bool up) { this.up = up; }
public override P6any Get(Variable obj) {
P6any o = obj.Fetch();
Rat rr;
if (o.IsDefined()) {
Rat ir = Kernel.UnboxAny<Rat>(o);
rr = new Rat(up ? ir.num + ir.den : ir.num - ir.den, ir.den);
} else {
rr = new Rat(up ? BigInteger.One : BigInteger.MinusOne, 1);
}
return Kernel.BoxRaw<Rat>(rr, Compartment.Top.RatMO);
}
}
class CtxRatStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return o.mo.name + "()"; }
Rat r = Kernel.UnboxAny<Rat>(o);
return Utils.N2S(((double)r.num) / ((double)r.den));
}
}
class CtxRatBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return false; }
Rat r = Kernel.UnboxAny<Rat>(o);
return r.num != BigInteger.Zero;
}
}
class CtxFatRatSuccish : ContextHandler<P6any> {
bool up;
public CtxFatRatSuccish(bool up) { this.up = up; }
public CtxFatRatSuccish() { }
protected override object[] GetData() { return new object[] { up }; }
protected override void SetData(object[] o) { up = (bool) o[0]; }
public override P6any Get(Variable obj) {
P6any o = obj.Fetch();
FatRat rr;
if (o.IsDefined()) {
FatRat ir = Kernel.UnboxAny<FatRat>(o);
rr = new FatRat(up ? ir.num + ir.den : ir.num - ir.den, ir.den);
} else {
rr = new FatRat(up ? BigInteger.One : BigInteger.MinusOne, BigInteger.One);
}
return Kernel.BoxRaw<FatRat>(rr, Compartment.Top.FatRatMO);
}
}
class CtxFatRatStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return o.mo.name + "()"; }
FatRat r = Kernel.UnboxAny<FatRat>(o);
return Utils.N2S(((double)r.num) / ((double)r.den));
}
}
class CtxFatRatBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return false; }
FatRat r = Kernel.UnboxAny<FatRat>(o);
return r.num != BigInteger.Zero;
}
}
class CtxComplexSuccish : ContextHandler<P6any> {
double amt;
public CtxComplexSuccish(double amt) { this.amt = amt; }
public CtxComplexSuccish() { }
protected override object[] GetData() { return new object[] { amt }; }
protected override void SetData(object[] o) { amt = (double) o[0]; }
public override P6any Get(Variable obj) {
P6any o = obj.Fetch();
Complex c = o.IsDefined() ? Kernel.UnboxAny<Complex>(o) : null;
c = (c == null) ? new Complex(amt, 0) : new Complex(c.re+amt, c.im);
return Kernel.BoxRaw(c, Compartment.Top.ComplexMO);
}
}
class CtxComplexStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return o.mo.name + "()"; }
Complex r = Kernel.UnboxAny<Complex>(o);
var sb = Utils.N2S(r.re) + (r.im < 0 ? "-" : "+") + Utils.N2S(Math.Abs(r.im));
return char.IsLetter(sb[sb.Length-1]) ? sb + "\\i" : sb + "i";
}
}
class CtxComplexBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) { return false; }
Complex r = Kernel.UnboxAny<Complex>(o);
return r.re != 0 || r.im != 0;
}
}
class CtxStrBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return false;
string s = Kernel.UnboxAny<string>(o);
return !(s == "" || s == "0");
}
}
class CtxStrSuccish : ContextHandler<P6any> {
bool succ;
public CtxStrSuccish(bool succ) { this.succ = succ; }
public CtxStrSuccish() { }
protected override object[] GetData() { return new object[] { succ }; }
protected override void SetData(object[] o) { succ = (bool) o[0]; }
// note that most of this table is katakana. Perhaps there
// is a better way.
[Immutable]
static ushort[] table = {
48, 57, 57, 48, 65, 90, 90, 65, 97, 122, 122, 97, 913, 929,
937, 931, 931, 937, 929, 913, 945, 961, 969, 963, 963, 969,
961, 945, 8544, 8555, 8555, 8544, 8560, 8571, 8571, 8560,
9312, 9331, 9331, 9312, 9332, 9351, 9351, 9332, 9372, 9397,
9397, 9372, 9856, 9861, 9861, 9856, 12450, 12450, 12531,
12452, 12452, 12452, 12450, 12454, 12454, 12454, 12452,
12456, 12456, 12456, 12454, 12458, 12458, 12459, 12456,
12461, 12461, 12461, 12459, 12463, 12463, 12463, 12461,
12465, 12465, 12465, 12463, 12467, 12467, 12467, 12465,
12469, 12469, 12469, 12467, 12471, 12471, 12471, 12469,
12473, 12473, 12473, 12471, 12475, 12475, 12475, 12473,
12477, 12477, 12477, 12475, 12479, 12479, 12479, 12477,
12481, 12481, 12481, 12479, 12484, 12484, 12484, 12481,
12486, 12486, 12486, 12484, 12488, 12488, 12488, 12486,
12490, 12490, 12495, 12488, 12498, 12498, 12498, 12495,
12501, 12501, 12501, 12498, 12504, 12504, 12504, 12501,
12507, 12507, 12507, 12504, 12510, 12510, 12514, 12507,
12516, 12516, 12516, 12514, 12518, 12518, 12518, 12516,
12520, 12520, 12525, 12518, 12527, 12527, 12527, 12525,
12530, 12530, 12531, 12527, 12450
};
// would perfect hashing be better?
void TableGet(char it, out char prev, out char next) {
int al = 0;
int ah = table.Length / 4;
while (true) {
if (al >= ah) {
prev = next = it;
return;
}
int am = (al + ah) / 2;
if (it < (char)table[am*4]) {
ah = am;
} else if (it <= (char)table[am*4+1]) {
prev = (it == (char)table[am*4]) ? (char)table[am*4+2] : (char)(it-1);
next = (it == (char)table[am*4+1]) ? (char)table[am*4+3] : (char)(it+1);
return;
} else {
al = am+1;
}
}
}
bool Digitish(char it) {
char next, prev;
TableGet(it, out prev, out next);
return (next != it);
}
public override P6any Get(Variable obj) {
P6any obj_o = obj.Fetch();
if (!obj_o.IsDefined()) return Kernel.BoxRaw("WTF", Compartment.Top.StrMO);
string src = Kernel.UnboxAny<string>(obj_o);
int right = src.Length;
tryagain:
while (right != 0 && !Digitish(src[right-1])) right--;
if (right == 0) return Kernel.BoxRaw("WTF", Compartment.Top.StrMO);
int left = right;
while (left != 0 && Digitish(src[left-1])) left--;
if (left != 0 && src[left-1] == '.') {
right--;
goto tryagain;
}
char[] nbuf = new char[src.Length + 1];
for (int i = right; i < src.Length; i++) nbuf[i+1] = src[i];
int delta = 0;
if (succ) {
bool carry = true;
char zero = '\0';
while (carry && right != left) {
char next, prev;
TableGet(src[right-1], out prev, out next);
carry = (next < src[right-1]);
zero = next;
nbuf[right] = next;
right--;
}
if (carry) {
delta++;
nbuf[left] = zero == '0' ? '1' : zero;
}
} else {
bool borrow = true;
while (borrow && right != left) {
char next, prev;
TableGet(src[right-1], out prev, out next);
borrow = (src[right-1] < prev);
nbuf[right] = prev;
right--;
}
if (borrow)
throw new NieczaException("Magical string decrement underflowed");
}
for (int i = 0; i < right; i++) nbuf[i+1-delta] = src[i];
return Kernel.BoxRaw(new string(nbuf, 1-delta, src.Length+delta),
Compartment.Top.StrMO);
}
}
class CtxListBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return false;
P6opaque dob = (P6opaque) o;
VarDeque items = (VarDeque) dob.slots[0];
if (items.Count() != 0) return true;
VarDeque rest = (VarDeque) dob.slots[1];
if (rest.Count() == 0) return false;
if (Kernel.IterHasFlat(rest, false)) {
items.Push(rest.Shift());
return true;
} else {
return false;
}
}
}
class CtxListNum : ContextHandler<double> {
public override double Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return 0;
P6opaque dob = (P6opaque) o;
VarDeque items = (VarDeque) dob.slots[0];
VarDeque rest = (VarDeque) dob.slots[1];
if (rest.Count() == 0) return items.Count();
while (Kernel.IterHasFlat(rest, false)) {
items.Push(rest.Shift());
}
return items.Count();
}
}
class CtxJunctionBool : ContextHandler<bool> {
public override bool Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return false;
P6opaque o_ = (P6opaque)o;
int jtype = Kernel.UnboxAny<int>((P6any) o_.slots[0]);
if (jtype == 4) return true; // XXX