Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 122ac5d042
Fetching contributors…

Cannot retrieve contributors at this time

6084 lines (5466 sloc) 237.85 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];
}
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 ::=.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public abstract class Variable : IFreeze {
public ViviHook whence;
// these should be treated as ro for the life of the variable
public bool rw;
public bool islist;
public abstract P6any Fetch();
public abstract void Store(P6any v);
public abstract Variable GetVar();
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, Kernel.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("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(Kernel.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 SimpleVariable: Variable {
STable type; // null for Any/Mu variables, and roish
P6any val;
private SimpleVariable() { }
public SimpleVariable(bool rw, bool islist, STable type, ViviHook whence, P6any val) {
this.val = val; this.whence = whence; this.rw = rw;
this.islist = islist; this.type = type;
}
public SimpleVariable(P6any val) { this.val = val; }
public SimpleVariable(bool islist, P6any val) {
this.islist = islist; this.val = val;
}
public override P6any Fetch() { return val; }
public override void Store(P6any v) {
if (!rw) {
throw new NieczaException("Writing to readonly scalar");
}
if (v == Kernel.NilP) {
v = type == null ? Kernel.AnyP : type.initObject;
}
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 Variable GetVar() {
return Kernel.BoxAnyMO<Variable>(this, Kernel.ScalarMO);
}
const int S_RO = 0;
const int S_LIST = 1;
const int S_RW = 2;
const int S_VIV = 3;
public override void Freeze(FreezeBuffer fb) {
int code = ((int)SerializationCode.SimpleVariable) +
(islist ? S_LIST : !rw ? S_RO : whence == null ? S_RW : S_VIV);
fb.Byte((byte)code);
if (whence != null) fb.ObjRef(whence);
if (rw) fb.ObjRef(type);
fb.ObjRef(val);
}
internal static SimpleVariable Thaw(ThawBuffer tb, int subcode) {
SimpleVariable n = new SimpleVariable();
tb.Register(n);
switch (subcode) {
default: throw new ArgumentException(subcode.ToString());
case S_RO:
// rw = false islist = false whence = null type = null
break;
case S_LIST:
n.islist = true;
break;
case S_VIV:
n.whence = (ViviHook) tb.ObjRef();
goto case S_RW;
case S_RW:
n.rw = true;
n.type = (STable) tb.ObjRef();
break;
}
n.val = (P6any) tb.ObjRef();
return n;
}
}
public sealed class TiedVariable: Variable {
P6any fetch;
P6any store;
private TiedVariable() { }
public TiedVariable(P6any whsub, P6any fetch, P6any store) {
this.fetch = fetch;
this.store = store;
this.whence = whsub.IsDefined() ? new SubViviHook(whsub) : null;
this.rw = true;
}
public override P6any Fetch() {
Variable vr = Kernel.RunInferior(fetch.Invoke(
Kernel.GetInferiorRoot(), None, 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 Variable[] { Kernel.NewROScalar(v) }, null));
}
public override Variable GetVar() {
return Kernel.BoxAnyMO<Variable>(this, Kernel.ScalarMO);
}
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; will eventually be
// the only way to share a bvalue
public sealed class BValue {
public Variable v;
public BValue(Variable v) { this.v = v; }
}
public class StashEnt : IFreeze {
public Variable v;
public string file;
public int line;
void IFreeze.Freeze(FreezeBuffer fb) {
fb.Byte((byte)SerializationCode.StashEnt);
fb.ObjRef(v);
fb.String(file);
fb.Int(line);
}
internal static StashEnt Thaw(ThawBuffer tb) {
StashEnt r = new StashEnt();
tb.Register(r);
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.
//
// [CompartmentGlobal] fields are unrestricted in usage, but are always
// automatically saved and restored when manipulating the container stack.
//
// [CORESaved] fields are the same as CompartmentGlobal but are additionally
// saved in CORE.ser.
[AttributeUsage(AttributeTargets.Field)]
class CORESavedAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
class ImmutableAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
class CompartmentGlobalAttribute : 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;
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 :
Config.CGForceSave ? AssemblyBuilderAccess.RunAndSave :
AssemblyBuilderAccess.Save),
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.Abstract | 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) {
EmitUnit oc = Current;
if (Config.CGVerbose > 0)
Console.WriteLine("generating code for: {0}", sub.name);
Current = this;
try {
var n = new NamProcessor(new CpsBuilder(this,
"C" + fill.Count + sub.name, true), sub);
n.MakeBody(Reader.Read(sub.nam_str, sub.nam_refs));
fill.Add(n);
} finally {
Current = oc;
}
if (erase) {
sub.nam_str = null;
sub.nam_refs = null;
}
}
public Type Finish() {
var type = type_builder.CreateType();
if (dll_name != null)
asm_builder.Save(dll_name);
foreach (NamProcessor th in fill)
th.FillSubInfo(type);
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.typeObject, Tokens.P6any);
}
internal CpsOp TypeConstantV(STable s) {
return RefConstant(s.name, "V", s.typeVar, Tokens.Variable);
}
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, "P", 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,
val is Variable ? typeof(Variable) : val.GetType());
return CpsOp.IsConst(CpsOp.GetSField(fi));
}
internal CpsOp ValConstant(string key, object val) {
CpsOp r;
if (!val_constants.TryGetValue(key, out r))
val_constants[key] = r = RefConstant(key, "", val, 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 Variable ExactNum(int numbase, string digits) {
BigInteger num = BigInteger.Zero;
BigInteger den = BigInteger.Zero;
bool neg = false;
foreach (char d in digits) {
if (d == '-') neg = true;
if (d == '_') continue;
if (d == '.') {
if (den != BigInteger.Zero)
throw new Exception("two dots in " + digits);
den = BigInteger.One;
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); }
num *= numbase;
den *= 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 | FieldAttributes.Static);
}
}
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, and also
// implies that the type is potentially shared between compartments
// and globals must be reset in Compartment.Pop.
public Type type;
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.depended_units.Add(this);
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 (Config.CGVerbose > 0)
Console.WriteLine("Generating code for {0} now because it's about to be run", th.info.name);
EmitUnit eu = new EmitUnit(null, "Anon." + Interlocked.Increment(
ref anon_id), null, false);
eu.CgSub(th.info, false);
SetConstants(eu.Finish(), eu.constants);
th.code = th.info.code;
th.EnsureSpills(th.info.nspill);
return th;
}
[CompartmentGlobal]
internal static ObjectRegistry reg;
// 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();
constants = eu.constants;
// all co-saved units must remember that their code is here
foreach (RuntimeUnit zu in subordinates)
zu.type = type;
reg.SaveUnit(name, this);
}
internal void SetConstants() { SetConstants(type, constants); }
static void SetConstants(Type ty, Dictionary<object,FieldInfo> consts) {
if (ty != null) {
foreach (KeyValuePair<object, FieldInfo> kv in consts)
kv.Value.SetValue(null, 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);
SetConstants(eu.Finish(), eu.constants);
}
// InitTime is called:
// * on MAIN when running a program
// * on evals if global INIT time has already passed
// * when linking units in an INITed container
public void InitTime() {
if (inited) return;
inited = true;
foreach (RuntimeUnit zu in subordinates)
zu.InitTime();
foreach (RuntimeUnit zu in depended_units)
zu.InitTime();
// XXX this is WRONG and will need to be changed with BEGIN
foreach (SubInfo z in our_subs) {
if ((z.special & SubInfo.UNSAFE) != 0)
Kernel.CheckUnsafe(z);
}
Kernel.FirePhasers(this, Kernel.PHASER_UNIT_INIT, false);
Kernel.FirePhasers(this, Kernel.PHASER_INIT, false);
}
internal void RunMainline() {
RuntimeUnit csr = this;
Builtins.setting_path = new Dictionary<string,SubInfo>();
while (csr.mainline.outer != null) {
RuntimeUnit o = csr.mainline.outer.unit;
Builtins.setting_path[o.name] = csr.mainline;
csr = o;
}
Kernel.RunInferior(Kernel.GetInferiorRoot().
MakeChild(null, csr.mainline, Kernel.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;
}
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.islist) return false;
P6any p = v.Fetch();
if (p.mo == Kernel.ArrayMO) {
return ((VarDeque)p.GetSlot("items")).Count() == 0 &&
((VarDeque)p.GetSlot("rest")).Count() == 0;
}
else if (p.mo == Kernel.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();
// lowest priority are empty common symbols
if (nse.v.rw && !nseod || IsEmptyAggr(nse.v)) {
nse = ose;
return null;
}
if (ose.v.rw && !oseod || IsEmptyAggr(ose.v)) {
return null;
}
// no conflict if items are identical
if (ose.v == nse.v || !ose.v.rw && !nse.v.rw && oseo == nseo) {
nse = ose;
return null;
}
// no conflict if items are simple packages with the same who
if (!ose.v.rw && !oseod && !nse.v.rw && !nseod &&
(oseo.mo.mo.isPackage || nseo.mo.mo.isPackage) &&
oseo.mo.who.Isa(Kernel.StashMO) &&
nseo.mo.who.Isa(Kernel.StashMO) &&
Kernel.UnboxAny<string>(oseo.mo.who) ==
Kernel.UnboxAny<string>(nseo.mo.who)) {
if (nseo.mo.mo.isPackage)
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();
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(Kernel).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) {
fb.ObjRef(f.GetValue(null));
}
}
}
}
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";
string result = (string) Builtins.UpCall(args);
if (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.constants = new Dictionary<object,FieldInfo>();
var fields = new Dictionary<string,FieldInfo>();
foreach (FieldInfo fi in n.type.GetFields())
fields[fi.Name] = fi;
tb.methods = new Dictionary<string,MethodInfo>();
foreach (MethodInfo mi in n.type.GetMethods())
tb.methods[mi.Name] = mi;
while (ncon-- > 0) {
FieldInfo fi = fields[tb.String()];
object val = tb.ObjRef();
n.constants[val] = fi;
fi.SetValue(null, 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(Kernel).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(null, tb.ObjRef());
}
}
}
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 abstract object Get(Frame f);
public virtual void Set(Frame f, object to) {
throw new NieczaException("Variable cannot be bound");
}
internal virtual ClrOp SetCode(int up, ClrOp head) {
throw new Exception("Lexicals of type " + this + " cannot be bound");
}
internal abstract ClrOp GetCode(int 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, Hint, Package, Alias
}
}
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.currentGlobals[hkey].v;
}
public override void Set(Frame f, object to) {
Kernel.currentGlobals[hkey].v = (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 LIHint : LexInfo {
public StashEnt var;
public LIHint() { }
public LIHint(StashEnt var) { this.var = var; }
public override void Init(Frame f) { }
public override void BindFields() {
var = new StashEnt();
}
public override object Get(Frame f) { return var.v; }
public override void Set(Frame f, object to) { var.v = (Variable)to; }
internal override ClrOp GetCode(int up) {
return new ClrGetField(Tokens.StashEnt_v,
EmitUnit.Current.RefConstant(name, "E", var, null).head);
}
internal override ClrOp SetCode(int up, ClrOp to) {
return new ClrSetField(Tokens.StashEnt_v,
EmitUnit.Current.RefConstant(name, "E", var, null).head, to);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Hint);
fb.ObjRef(var);
}
}
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, Kernel.NewROScalar(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, Kernel.AnyMO.typeVar);
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);
}
internal void MakeDispatch(Frame into) {
HashSet<string> names = new HashSet<string>();
List<P6any> cands = new List<P6any>();
string filter = name + ":";
string pn = name + ":(!proto)";
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 &&
kp.Value is LISub &&
!names.Contains(kp.Key)) {
names.Add(kp.Key);
brk = true;
cands.Add(((LISub)kp.Value).def.protosub);
}
}
if (csr.outer == null) break;
// don't go above nearest proto
if (csr.dylex.ContainsKey(pn)) break;
if (brk) cands.Add(null);
f = f.outer;
}
Set(into, Kernel.NewROScalar(Kernel.MakeDispatcher(name, null,
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 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);
else { li.Set(cr, bind); return null; }
}
public override object Get(Frame f) { return Common(f,false,null); }
public override void Set(Frame f, object bind) { Common(f,true,bind); }
internal override ClrOp GetCode(int up) {
LexInfo real;
SubInfo sc = owner;
while (!sc.dylex.TryGetValue(to, out real)) {
sc = sc.outer;
up++;
}
return real.GetCode(up);
}
internal override ClrOp SetCode(int up, ClrOp bind) {
LexInfo real;
SubInfo sc = owner;
while (!sc.dylex.TryGetValue(to, out real)) {
sc = sc.outer;
up++;
}
return real.SetCode(up, bind);
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Alias);
fb.String(to);
}
}
public class LIPackage : LexInfo {
public STable pkg;
public LIPackage(STable pkg) { this.pkg = pkg; }
public override object Get(Frame f) { return pkg.typeVar; }
public override void Init(Frame f) { }
internal override ClrOp GetCode(int up) {
return EmitUnit.Current.TypeConstantV(pkg).head;
}
internal override void DoFreeze(FreezeBuffer fb) {
fb.Byte((byte)LexSerCode.Package);
fb.ObjRef(pkg);
}
}
// 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;
public int nspill;
public int[] sig_i;
public object[] sig_r;
// 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 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;
// 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 int SIG_I_RECORD = 3;
public const int SIG_I_FLAGS = 0;
public const int SIG_I_SLOT = 1;
public const int SIG_I_NNAMES = 2;
// R records are variable size, but contain canonical name,
// usable names (in order), default SubInfo (if present),
// type STable (if present)
// Value processing
public const int SIG_F_HASTYPE = 1; // else Kernel.AnyMO
public const int SIG_F_MULTI_IGNORED = 16384;
// Value binding
public const int SIG_F_READWRITE = 2;
public const int SIG_F_RWTRANS = 8;
public const int SIG_F_BINDLIST = 16;
public const int SIG_F_INVOCANT = 8192;
public const int SIG_F_IS_COPY = 32768;
public const int SIG_F_IS_LIST = 65536;
public const int SIG_F_IS_HASH = 131072;
// Value source
public const int SIG_F_HASDEFAULT = 32;
public const int SIG_F_OPTIONAL = 64;
public const int SIG_F_DEFOUTER = 4096;
public const int SIG_F_POSITIONAL = 128;
public const int SIG_F_SLURPY_POS = 256;
public const int SIG_F_SLURPY_NAM = 512;
public const int SIG_F_SLURPY_CAP = 1024;
public const int SIG_F_SLURPY_PCL = 2048;
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
public int[] edata;
public string[] label_names;
[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)");
} 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) {
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;
}
private string PName(int rbase) {
return ((string)sig_r[rbase]) + " in " + name;
}
public unsafe Frame Binder(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);
int[] ibuf = sig_i;
if (ibuf == null) return th;
int posc = 0;
HashSet<string> namedc = null;
int jun_pivot = -1;
string jun_pivot_n = null;
int jun_rank = int.MaxValue;
if (named != null)
namedc = new HashSet<string>(named.Keys);
if (ibuf.Length == 0) goto noparams;
fixed (int* ibase = ibuf) {
int* ic = ibase;
int* iend = ic + (ibuf.Length - 2);
object[] rbuf = sig_r;
int rc = 0;
int obj_src = -1;
string obj_src_n = null;
while (ic < iend) {
int flags = *(ic++);
int slot = *(ic++);
int names = *(ic++);
int rbase = rc;
rc += (1 + names);
if ((flags & SIG_F_HASDEFAULT) != 0) rc++;
STable type = Kernel.AnyMO;
obj_src = -1;
if ((flags & SIG_F_HASTYPE) != 0)
type = (STable)rbuf[rc++];
Variable src = null;
if ((flags & SIG_F_SLURPY_PCL) != 0) {
src = Kernel.BoxAnyMO(pos, Kernel.ParcelMO);
posc = pos.Length;
goto gotit;
}
if ((flags & SIG_F_SLURPY_CAP) != 0) {
P6any nw = new P6opaque(Kernel.CaptureMO);
nw.SetSlot("positionals", pos);
nw.SetSlot("named", named);
src = Kernel.NewROScalar(nw);
named = null; namedc = null; posc = pos.Length;
goto gotit;
}
if ((flags & SIG_F_SLURPY_POS) != 0) {
P6any l = new P6opaque(Kernel.ListMO);
Kernel.IterToList(l, Kernel.IterFlatten(
Kernel.SlurpyHelper(th, posc)));
src = Kernel.NewRWListVar(l);
posc = pos.Length;
goto gotit;
}
if ((flags & SIG_F_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, Kernel.HashMO);
goto gotit;
}
if (names != 0 && named != null) {
for (int ni = 1; ni <= names; ni++) {
string n = (string)rbuf[rbase+ni];
if (namedc.Contains(n)) {
namedc.Remove(n);
src = named[n];
obj_src_n = n;
obj_src = -2;
goto gotit;
}
}
}
if ((flags & SIG_F_POSITIONAL) != 0 && posc != pos.Length) {
obj_src = posc;
src = pos[posc++];
goto gotit;
}
get_default:
if ((flags & SIG_F_HASDEFAULT) != 0) {
Frame thn = Kernel.GetInferiorRoot()
.MakeChild(th, (SubInfo) rbuf[rbase + 1 + names],
Kernel.AnyP);
src = Kernel.RunInferior(thn);
if (src == null)
throw new Exception("Improper null return from sub default for " + PName(rbase));
goto gotit;
}
if ((flags & SIG_F_DEFOUTER) != 0) {
Frame f = th;
if (outer_topic_key < 0) {
src = Kernel.AnyMO.typeVar;
goto gotit;
}
for (int i = 0; i < outer_topic_rank; i++) f = f.outer;
src = (Variable)f.GetDynamic(outer_topic_key);
goto gotit;
}
if ((flags & SIG_F_OPTIONAL) != 0) {
// Array is the "default" Positional -masak
if ((flags & SIG_F_IS_LIST) != 0)
src = Kernel.CreateArray();
else if ((flags & SIG_F_IS_HASH) != 0)
src = Kernel.CreateHash();
else
src = type.initVar;
goto gotit;
}
if (quiet) return null;
return Kernel.Die(th, "No value for parameter " + PName(rbase));
gotit:
if ((flags & SIG_F_RWTRANS) != 0) {
} else if ((flags & SIG_F_IS_COPY) != 0) {
if ((flags & SIG_F_IS_HASH) != 0)
src = Kernel.Assign(Kernel.CreateHash(),
Kernel.NewRWListVar(src.Fetch()));
else if ((flags & SIG_F_IS_LIST) != 0)
src = Kernel.Assign(Kernel.CreateArray(),
Kernel.NewRWListVar(src.Fetch()));
else
src = Kernel.Assign(Kernel.NewTypedScalar(type), src);
} else {
bool islist = ((flags & SIG_F_BINDLIST) != 0);
bool rw = ((flags & SIG_F_READWRITE) != 0) && !islist;
P6any srco = src.Fetch();
// XXX: in order for calling methods on Nil to work,
// self needs to be ignored here.
if (srco == Kernel.NilP && obj_src != -1 &&
(flags & SIG_F_INVOCANT) == 0) {
obj_src = -1;
goto get_default;
}
if (!srco.Does(type)) {
if (quiet) return null;
if (srco.mo.HasMRO(Kernel.JunctionMO) && obj_src != -1) {
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;
}
return Kernel.Die(th, "Nominal type check failed in binding " + PName(rbase) + "; got " + srco.mo.name + ", needed " + type.name);
}
if (rw) {
if (src.rw) {
// this will be a functional RW binding
if (src.whence != null)
Kernel.Vivify(src);
goto bound;
} else {
if (quiet) return null;
return Kernel.Die(th, "Binding " + PName(rbase) + ", cannot bind read-only value to is rw parameter");
}
}
else {
if (!src.rw && islist == src.islist)
goto bound;
src = new SimpleVariable(islist, srco);
}
bound: ;
}
if ((flags & SIG_F_INVOCANT) != 0 && self_key >= 0)
th.SetDynamic(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;
}
}
}
noparams:
if (posc != pos.Length || namedc != null && namedc.Count != 0) {
if (quiet) return null;
string m = "Excess arguments to " + name;
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);
return Kernel.Die(th, m);
}
if (jun_pivot != -1 && !quiet) {
Variable jct = (jun_pivot == -2 ? named[jun_pivot_n] :
pos[jun_pivot]);
th = caller.MakeChild(null, Kernel.AutoThreadSubSI, Kernel.AnyP);
P6opaque jo = (P6opaque) jct.Fetch();
th.named = named;
th.pos = pos;
th.lex1 = this;
th.lex2 = jun_pivot_n;
th.lex3 = jo.slots[0];
th.lex4 = Kernel.UnboxAny<Variable[]>((P6any)jo.slots[1]);
th.lex5 = outer;
th.lex6 = sub;
th.lex7 = de;
th.lex8 = new Variable[((Variable[])th.lex4).Length];
th.lex9 = jct;
th.lexi0 = jun_pivot;
th.lexi1 = 0;
return th;
}
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(Kernel.JunctionMO);
nj.slots[0] = th.lex3;
nj.slots[1] = Kernel.BoxRaw(dst, Kernel.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 = Kernel.NewROScalar(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).Binder(th, (Frame)th.lex5, (P6any)th.lex6,
th.pos, th.named, false, (DispatchEnt)th.lex7);
}
internal bool IsInlinable() {
if (sig_i == 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;
}
for (int i = 0; i < sig_i.Length; i += SIG_I_RECORD) {
int fl = sig_i[i + SIG_I_FLAGS];
if ((fl & SIG_F_POSITIONAL) == 0)
return false;
if ((fl & SIG_F_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 void RunBEGIN() {
Kernel.RunInferior(protosub.Invoke(Kernel.GetInferiorRoot(),
Variable.None, null));
SetInlined();
}
internal bool IsTopicalizer() {
if (sig_i == null)
return false;
LexInfo topic;
dylex.TryGetValue("$_", out topic);
for (int i = 0; i < sig_i.Length; i += SIG_I_RECORD) {
int slot = sig_i[i + SIG_I_SLOT];
if (slot >= 0 && topic != null && topic.SigIndex() == 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.IndexOf(':');
LexInfo disp;
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 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) {
tn = t.FullName;
}
mn = code.Method.Name;
}
fb.String(mn);
fb.String(tn);
fb.Int(nspill);
fb.Ints(sig_i);
if (sig_i != null)
fb.Refs(sig_r);
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(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);
string mn = tb.String();
string tn = tb.String();
if (mn != null) {
Type t = tn == null ? tb.type :
typeof(Kernel).Assembly.GetType(tn, true);
n.code = t == null ? null :
(DynBlockDelegate) Delegate.CreateDelegate(
typeof(DynBlockDelegate),
t == tb.type ? tb.methods[mn] :
t.GetMethod(mn, BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Static));
}
n.nspill = tb.Int();
n.sig_i = tb.Ints();
if (n.sig_i != null)
n.sig_r = tb.RefsA<object>();
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.Hint:
li = new LIHint((StashEnt) 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;
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.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;
}
foreach (SubInfo z in children) {
if (z.phaser == Kernel.PHASER_CATCH)
catch_ = z;
if (z.phaser == Kernel.PHASER_CONTROL)
control = z;
}
}
}
// 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 int flags;
public Frame(Frame caller_, Frame outer_,
SubInfo info_, P6any sub_) {
caller = caller_;
outer = outer_;
code = info_.code;
info = info_;
sub = sub_;
mo = Kernel.CallFrameMO;
lexn = (info_.nspill > 0) ? new object[info_.nspill] : null;
}
public Frame() { mo = Kernel.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 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.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.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(Kernel.CaptureMO);
nw.SetSlot("positionals", pos ?? new Variable[0]);
nw.SetSlot("named", named);
return Kernel.NewROScalar(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 Kernel.AnyMO.typeVar;
}
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 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 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() : 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.Binder(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] = Kernel.NewROScalar(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.HasMRO(Kernel.NumMO)) {
return Kernel.UnboxAny<double>(o);
} else if (o.mo.HasMRO(Kernel.IntMO)) {
if (o is BoxObject<int>) {
return (double)Kernel.UnboxAny<int>(o);
} else {
return (double)Kernel.UnboxAny<BigInteger>(o);
}
} else if (o.mo.HasMRO(Kernel.RatMO)) {
Rat r = Kernel.UnboxAny<Rat>(o);
return (double)r.num / (double)r.den;
} else if (o.mo.HasMRO(Kernel.FatRatMO)) {
FatRat r = Kernel.UnboxAny<FatRat>(o);
return (double)r.num / (double)r.den;
} else if (o.mo.HasMRO(Kernel.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 Kernel.NewROScalar(obj.Fetch());
}
}
class CtxReturnSelfList : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
if (obj.islist) return obj;
return Kernel.NewRWListVar(obj.Fetch());
}
}
class CtxReturnSelfItem : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
if (!obj.islist) return obj;
return Kernel.NewROScalar(obj.Fetch());
}
}
class CtxAnyList : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
VarDeque itr = new VarDeque(
obj.islist ? Kernel.NewROScalar(obj.Fetch()) : obj);
P6any l = new P6opaque(Kernel.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.islist) {
srcs[i] = new P6opaque(Kernel.ListMO);
Kernel.IterToList(srcs[i], src);
src = new VarDeque();
} else {
srcs[i] = Kernel.IterHasFlat(src, true) ?
src.Shift().Fetch() : Kernel.AnyP;
}
}
for (int i = 0; i < dsts.Length; i++) {
Variable d = dsts[i];
if (d.islist) {
d.Fetch().mo.mro_LISTSTORE.Get(d,
Kernel.NewRWListVar(srcs[i]));
} else {
d.Store(srcs[i]);
}
}
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(Kernel.ListMO);
Kernel.IterToList(l, itr);
return Kernel.NewRWListVar(l);
}
}
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 Kernel.NewROScalar(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("items", items);
lhs_o.SetSlot("rest", iter); /*now empty*/
return lhs;
}
}
class CtxListIterator : ContextHandler<VarDeque> {
public override VarDeque Get(Variable obj) {
P6opaque d = (P6opaque) obj.Fetch();
if (!d.IsDefined()) return new VarDeque();
VarDeque r = new VarDeque( (VarDeque) d.slots[0] );
r.PushD((VarDeque) d.slots[1]);
return r;
}
}
class PopList : ContextHandler<Variable> {
public override Variable Get(Variable v) {
P6any o = v.Fetch();
if (!o.IsDefined()) return Kernel.AnyMO.typeVar;
VarDeque items = (VarDeque)o.GetSlot("items");
VarDeque rest = (VarDeque)o.GetSlot("rest");
while (Kernel.IterHasFlat(rest, false))
items.Push(rest.Shift());
return (items.Count() != 0) ? items.Pop() : Kernel.AnyMO.typeVar;
}
}
class ShiftList : ContextHandler<Variable> {
public override Variable Get(Variable v) {
P6any o = v.Fetch();
if (!o.IsDefined()) return Kernel.AnyMO.typeVar;
VarDeque items = (VarDeque)o.GetSlot("items");
VarDeque rest = (VarDeque)o.GetSlot("rest");
if (items.Count() != 0)
return items.Shift();
if (Kernel.IterHasFlat(rest, false))
return rest.Shift();
return Kernel.AnyMO.typeVar;
}
}
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("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("rest");
if (targ.Count() == 0) targ = (VarDeque)o.GetSlot("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.HasMRO(Kernel.HashMO)) {
foreach(KeyValuePair<string,Variable> kv in
Kernel.UnboxAny<VarHash>(elt)) {
into[kv.Key] = kv.Value;
}
} else if (elt.mo.HasMRO(Kernel.PairMO)) {
Variable k = (Variable) elt.GetSlot("key");
Variable v = (Variable) elt.GetSlot("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(Kernel.NewROScalar(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() ? Kernel.TrueV : Kernel.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, Kernel.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, Kernel.IntMO);
else
return Kernel.BoxRaw<BigInteger>(bn, Kernel.IntMO);
}
v = o.IsDefined() ? Kernel.UnboxAny<int>(o) : 0;
if (v == (amt > 0 ? int.MaxValue : int.MinValue))
return Kernel.BoxRaw<BigInteger>(amt + (long)v, Kernel.IntMO);
return Kernel.BoxRaw(v + amt, Kernel.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, Kernel.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 r.num.ToString() + "/" + r.den.ToString();
}
}
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, Kernel.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 r.num.ToString() + "/" + r.den.ToString();
}
}
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, Kernel.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);
return Utils.N2S(r.re) + (r.im < 0 ? "" : "+") + Utils.N2S(r.im) + "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", Kernel.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", Kernel.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),
Kernel.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
Variable[] eigen = Kernel.UnboxAny<Variable[]>((P6any) o_.slots[1]);
int ix = 0;
Variable v;
// logic design taken from Rakudo here
switch(jtype) {
case 0: //all
if (ix == eigen.Length) return true;
v = eigen[ix++];
if (!v.Fetch().mo.mro_raw_Bool.Get(v)) return false;
goto case 0;
case 1: //none
if (ix == eigen.Length) return true;
v = eigen[ix++];
if (v.Fetch().mo.mro_raw_Bool.Get(v)) return false;
goto case 1;
case 2: //one, searching for first
if (ix == eigen.Length) return false;
v = eigen[ix++];
if (v.Fetch().mo.mro_raw_Bool.Get(v)) goto case 1;
goto case 2;
case 3: //any
if (ix == eigen.Length) return false;
v = eigen[ix++];
if (v.Fetch().mo.mro_raw_Bool.Get(v)) return true;
goto case 3;
default: throw new ArgumentException();
}
}
}
class CtxMatchStr : ContextHandler<string> {
public override string Get(Variable obj) {
P6any o = obj.Fetch();
if (!o.IsDefined()) return "";
Cursor c = (Cursor) o;
return c.GetBacking().Substring(c.from, c.pos - c.from);
}
}
class CtxStrNativeNum2Str : ContextHandler<Variable> {
public override Variable Get(Variable obj) {
P6any o = obj.Fetch();
return Kernel.BoxAnyMO<string>(o.IsDefined() ? Utils.N2S(Kernel.UnboxAny<double>(o)) : "Num()", Kernel.StrMO);
}
}
class IxCallMethod : IndexHandler {
string name;
VarHash named;
public IxCallMethod() { }
protected override object[] GetData() {
return new object[] { name, named };
}
protected override void SetData(object[] o) {
name = (string) o[0];
named = (VarHash) o[1];
}
public IxCallMethod(string name, string adv) {
this.name = name;
if (adv != null) {
named = new VarHash();
named[adv] = Kernel.TrueV;
}
}
public override Variable Get(Variable obj, Variable key) {
return (Variable) Kernel.RunInferior(
obj.Fetch().InvokeMethod(Kernel.GetInferiorRoot(), name,
new Variable[] { obj, key }, named));
}
}
class KeySlicer : IndexHandler {
int mode; IndexHandler bas;
public KeySlicer() { }
protected override object[] GetData() {
return new object[] { mode, bas };
}
protected override void SetData(object[] o) {
mode = (int) o[0];
bas = (IndexHandler) o[1];
}
public KeySlicer(int mode, object bas) {
this.mode = mode; this.bas = (IndexHandler)bas;
}
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
switch (mode) {
case 0: return key;
case 1: return Builtins.MakeParcel(key, bas.Get(obj, key));
default: return Builtins.pair(key, bas.Get(obj, key));
}
}
}
class IxAnyDeleteKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return Kernel.AnyMO.typeVar;
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"delete_key", new Variable[] { obj, key }, null));
}
}
class IxAnyExistsKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return Kernel.FalseV;
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"exists_key", new Variable[] { obj, key }, null));
}
}
class IxAnyBindKey : BindHandler {
public override Variable Bind(Variable obj, Variable key, Variable to) {
P6any os = obj.Fetch();
if (os.IsDefined())
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"bind_key", new Variable[] { obj, key, to }, null));
obj.Store(Kernel.BoxRaw(new VarHash(), Kernel.HashMO));
return Kernel.HashMO.mro_bind_key.Bind(obj, key, to);
}
}
class IxAnyBindPos : BindHandler {
public override Variable Bind(Variable obj, Variable key, Variable to) {
P6any os = obj.Fetch();
if (os.IsDefined())
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"bind_pos", new Variable[] { obj, key, to }, null));
obj.Store(Kernel.CreateArray().Fetch());
return Kernel.ArrayMO.mro_bind_key.Bind(obj, key, to);
}
}
class IxAnyAtKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return IndexHandler.ViviHash(obj, key);
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"at_key", new Variable[] { obj, key }, null));
}
}
class IxAnyAtPos : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return IndexHandler.ViviArray(obj, key);
if (ks.mo != Kernel.IntMO && ks.mo.HasMRO(Kernel.CodeMO)) {
Variable elts = Kernel.RunInferior(os.InvokeMethod(
Kernel.GetInferiorRoot(), "elems",
new Variable[] { obj }, null));
return Get(obj, Kernel.RunInferior(ks.Invoke(
Kernel.GetInferiorRoot(),
new Variable[] { elts }, null)));
}
return Kernel.RunInferior(os.InvokeMethod(Kernel.GetInferiorRoot(),
"at_pos", new Variable[] { obj, key }, null));
}
}
class IxCursorAtKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any o = obj.Fetch();
if (!o.IsDefined())
return Kernel.AnyMO.typeVar;
Cursor os = (Cursor)o;
return os.GetKey(ks.mo.mro_raw_Str.Get(key));
}
}
class IxCursorAtPos : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any o = obj.Fetch();
if (!o.IsDefined())
return Kernel.AnyMO.typeVar;
Cursor os = (Cursor)o;
return os.GetKey(Utils.N2S(ks.mo.mro_raw_Numeric.Get(key)));
}
}
class IxHashBindKey : BindHandler {
public override Variable Bind(Variable obj, Variable key, Variable to) {
P6any ks = key.Fetch();
P6any os = obj.Fetch();
if (!os.IsDefined())
obj.Store(os = Kernel.BoxRaw(new VarHash(), Kernel.HashMO));
string kss = ks.mo.mro_raw_Str.Get(key);
VarHash h = Kernel.UnboxAny<VarHash>(os);
return h[kss] = Kernel.NewBoundVar(Kernel.NBV_RW, Kernel.MuMO, to);
}
}
class IxHashAtKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return IndexHandler.ViviHash(obj, key);
string kss = ks.mo.mro_raw_Str.Get(key);
VarHash h = Kernel.UnboxAny<VarHash>(os);
Variable r;
if (h.TryGetValue(kss, out r))
return r;
return new SimpleVariable(true, false, null,
new HashViviHook(os, kss), Kernel.AnyP);
}
}
class IxHashExistsKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined()) return Kernel.FalseV;
string kss = ks.mo.mro_raw_Str.Get(key);
VarHash h = Kernel.UnboxAny<VarHash>(os);
return h.ContainsKey(kss) ? Kernel.TrueV : Kernel.FalseV;
}
}
class IxHashDeleteKey : IndexHandler {
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined()) return Kernel.AnyMO.typeVar;
string kss = ks.mo.mro_raw_Str.Get(key);
VarHash h = Kernel.UnboxAny<VarHash>(os);
Variable r;
if (h.TryGetValue(kss, out r)) {
h.Remove(kss);
return r;
} else {
return Kernel.AnyMO.typeVar;
}
}
}
class IxListAtPos : IndexHandler {
bool extend;
public IxListAtPos(bool extend) { this.extend = extend; }
public IxListAtPos() { }
protected override object[] GetData() { return new object[] { extend};}
protected override void SetData(object[] o) { extend = (bool)o[0]; }
public override Variable Get(Variable obj, Variable key) {
P6any ks = key.Fetch();
if (key.islist || !ks.mo.is_any && ks.mo.HasMRO(Kernel.JunctionMO))
return Slice(obj, key);
P6any os = obj.Fetch();
if (!os.IsDefined())
return IndexHandler.ViviArray(obj, key);
P6opaque dos = (P6opaque) os;
VarDeque items = (VarDeque) dos.slots[0];
VarDeque rest = (VarDeque) dos.slots[1];
if (ks.mo != Kernel.IntMO && ks.mo.HasMRO(Kernel.CodeMO)) {
Variable nr = os.mo.mro_Numeric.Get(obj);
return Get(obj, Kernel.RunInferior(ks.Invoke(
Kernel.GetInferiorRoot(),
new Variable[] { nr }, null)));
}
int ix = (int) key.Fetch().mo.mro_raw_Numeric.Get(key);
while (items.Count() <= ix && Kernel.IterHasFlat(rest, false)) {
items.Push(rest.Shift());
}
if (ix < 0)
return Kernel.AnyMO.typeVar;
if (items.Count() <= ix) {
if (extend) {
return new SimpleVariable(true, false, null,
new ArrayViviHook(os, ix), Kernel.AnyP);
} else {
return Kernel.AnyMO.typeVar;
}
}
return items[ix];
}
}
class IxListBindPos : BindHandler {
public override Variable Bind(Variable obj, Variable key, Variable to) {
P6any ks = key.Fetch();
P6any os = obj.Fetch();
if (!os.IsDefined())
obj.Store(os = Kernel.CreateArray().Fetch());
P6opaque dos = (P6opaque) os;
VarDeque items = (VarDeque) dos.slots[0];
VarDeque rest = (VarDeque) dos.slots[1];
if (ks.mo != Kernel.IntMO && ks.mo.HasMRO(Kernel.CodeMO)) {
Variable nr = os.mo.mro_Numeric.Get(obj);
key = Kernel.RunInferior(ks.Invoke(Kernel.GetInferiorRoot(),
new Variable[] { nr }, null));
}
int ix = (int) key.Fetch().mo.mro_raw_Numeric.Get(key);
while (items.Count() <= ix && Kernel.IterHasFlat(rest, false)) {
items.Push(rest.Shift());
}
if (ix < 0)
throw new NieczaException("binding to out of range slot " + ix);
while (items.Count() <= ix) {
items.Push(Kernel.NewTypedScalar(null));
}
return items[ix] = Kernel.NewBoundVar(Kernel.NBV_RW, Kernel.MuMO, to);
}
}
public struct StashCursor {
public const int WHO = 0; // use p1(P6any)
public const int LEX = 1; // p1(Frame) p2(int, depth)
public const int ROOT = 2; // p1(Frame) p2(int)
public const int DYNA = 3; // p1&p2
public const int CLR = 4; // p1(string)
int type;
object p1;
int p2;
public StashCursor(Frame fr, int depth) {
this.type = ROOT;
this.p1 = fr;
this.p2 = depth;
}
public static Variable MakePackage(string name, P6any who) {
STable st = new STable(name);
st.who = who;
st.typeObject = st.initObject = new P6opaque(st, 0);
((P6opaque)st.typeObject).slots = null;
st.typeVar = st.initVar = Kernel.NewROScalar(st.typeObject);
st.mo.isPackage = true;
st.mo.rtype = "package";
// XXX should be PackageHOW
st.how = new BoxObject<STable>(st, Kernel.ClassHOWMO, 0);
st.mo.Revalidate();
return st.typeVar;
}
bool HasCaller() {
Frame f = (Frame)p1;
if (p2 == 0) {
f = f.caller;
if (f != null && f.info == Kernel.ExitRunloopSI) f = f.caller;
if (f == null) return false;
}
return true;
}
StashCursor ToCaller() {
StashCursor r = this;
Frame f = (Frame)p1;
if (r.p2 > 0) r.p2--;
else {
f = f.caller;
if (f != null && f.info == Kernel.ExitRunloopSI) f = f.caller;
if (f == null)
throw new NieczaException("No more calling frames");
r.p1 = f;
r.p2 = f.info.FindControlEnt(f.ip, SubInfo.ON_VARLOOKUP, null);
if (r.p2 < 0) r.p2 = 0;
}
return r;
}
SubInfo ToInfo() { return (p1 as Frame).info; }
StashCursor ToOuter() {
StashCursor r = this;
if (r.p2 > 0) { r.p2--; }
else {
Frame f = ((Frame) r.p1).outer;
r.p1 = f;
if (f == null)
throw new NieczaException("No more outer frames");
r.p2 = 0;
}
return r;
}
bool TryLexOut(string key, bool rbar_w, ref Variable o) {
StashCursor sc = this;
if (key.Length >= 2 && key[1] == '*') {
return TryDynamic(key, rbar_w, ref o);
}
while (true) {
if (sc.TryLex(key, rbar_w, ref o)) return true;
if ((sc.p1 as Frame).outer == null && sc.p2 == 0)
return false;
sc = sc.ToOuter();
}
}
bool TryDynamic(string key, bool rbar_w, ref Variable o) {
StashCursor sc = this;
while (true) {
if (sc.TryLex(key, rbar_w, ref o)) {
return true;
}
if (!sc.HasCaller())
break;
sc = sc.ToCaller();
}
if (key.Length >= 2 && key[1] == '*') {
key = key.Remove(1,1);
StashEnt bv;
if (Kernel.currentGlobals.TryGetValue("\x8::GLOBAL" + key, out bv) ||
Kernel.currentGlobals.TryGetValue("\x9::PROCESS" + key, out bv)) {
if (rbar_w) { bv.v = o; } else { o = bv.v; }
return true;
}
}
return false;
}
bool TryLex(string key, bool rbar_w, ref Variable o) {
Frame f = (Frame)p1;
if (p2 > 0) {
int ix = f.info.GetInlineSlot(f.ip, key, p2);
if (ix < 0) return false;
if (rbar_w) f.SetDynamic(ix, o);
else o = (Variable)f.GetDynamic(ix);
}
else {
LexInfo li;
if (!f.info.dylex.TryGetValue(key, out li)) return false;
if (rbar_w) li.Set(f, o);
else o = (Variable)li.Get(f);
}
return true;
}
public static P6any MakeCLR_WHO(string name) {
StashCursor sc = default(StashCursor);
sc.type = CLR;
sc.p1 = name;
P6any who = Kernel.BoxRaw(sc, Kernel.PseudoStashMO);
who.SetSlot("name", Kernel.BoxAnyMO(name, Kernel.StrMO));
return who;
}
void Core(string key, bool final, out StashCursor sc, out Variable v,
Variable bind_to) {
v = null;
sc = this;
if (type == DYNA) {
// DYNAMIC::{key}, no special names used
v = bind_to;
if (TryDynamic(key, (bind_to != null), ref v)) {