From d4638e157a76690ef751e7b359b2a7c7d4df809d Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Mon, 27 Dec 2010 22:50:27 -0800 Subject: [PATCH] Implement attribute initializers --- lib/CLRBackend.cs | 52 +++++++++++++++++++++++++++---------------- lib/JSYNC.cs | 4 ++-- lib/Kernel.cs | 48 ++++++++++++++++++++++++++------------- lib/NieczaCLR.cs | 3 +-- lib/SAFE.setting | 4 ++-- src/Metamodel.pm | 31 ++++++++++++++++++-------- src/NAMBackend.pm | 2 +- src/Niecza/Actions.pm | 8 ++++++- src/Op.pm | 1 + test2.pl | 13 +++++++++++ 10 files changed, 114 insertions(+), 52 deletions(-) diff --git a/lib/CLRBackend.cs b/lib/CLRBackend.cs index f3a8063d..91ed6e27 100644 --- a/lib/CLRBackend.cs +++ b/lib/CLRBackend.cs @@ -13,7 +13,7 @@ using Niecza; namespace Niecza.CLRBackend { - // The portable format is a subset of JSON, and is current read + // The portable format is a subset of JSON, and is currently read // into a matching internal form. sealed class JScalar { string text; @@ -387,9 +387,15 @@ class Method { class Attribute { public readonly string name; + public readonly bool publ; + public readonly string ivar; + public readonly Xref ibody; public Attribute(object[] x) { - name = JScalar.S(x[0]); + name = JScalar.S(x[0]); + publ = JScalar.B(x[1]); + ivar = JScalar.S(x[2]); + ibody = Xref.from(x[3]); } public static Attribute[] fromArray(object x) { @@ -912,6 +918,8 @@ sealed class Tokens { n["sub"] = DynMetaObject.GetMethod("AddSubMethod"); return n; } + public static readonly MethodInfo DMO_AddAttribute = + typeof(DynMetaObject).GetMethod("AddAttribute"); public static readonly MethodInfo DMO_Invalidate = typeof(DynMetaObject).GetMethod("Invalidate"); public static readonly MethodInfo DMO_FillParametricRole = @@ -3204,13 +3212,8 @@ class NamProcessor { CpsOp[] supers = new CpsOp[pr.superclasses.Length]; for (int i = 0; i < supers.Length; i++) supers[i] = CpsOp.GetSField(pr.superclasses[i].Resolve().metaObject); - string[] anames = new string[pr.attributes.Length]; - for (int i = 0; i < anames.Length; i++) - anames[i] = pr.attributes[i].name; - build.Add( CpsOp.MethodCall(null, Tokens.DMO_FillRole, new CpsOp[] { - mo, CpsOp.StringArray(false, anames), - CpsOp.NewArray(Tokens.DynMetaObject, supers), + mo, CpsOp.NewArray(Tokens.DynMetaObject, supers), CpsOp.NewArray(Tokens.DynMetaObject, new CpsOp[0]) }) ); foreach (Method m in pr.methods) { @@ -3223,6 +3226,15 @@ class NamProcessor { CpsOp.MethodCall(null, Tokens.Variable_Fetch, new CpsOp[] { var }) })); } + foreach (Attribute a in pr.attributes) { + CpsOp name = CpsOp.StringLiteral(a.name); + CpsOp publ = CpsOp.BoolLiteral(a.publ); + CpsOp init = a.ivar == null ? CpsOp.Null(Tokens.IP6) : + RawAccessLex("scopedlex", a.ivar, null); + build.Add(CpsOp.MethodCall(null, Tokens.DMO_AddAttribute, + new CpsOp[] { mo, name, publ, init })); + } + build.Add(CpsOp.MethodCall(null, Tokens.DMO_Invalidate, new CpsOp[] { mo })); if (sub.sig != null) { object[] rsig = (object[]) sub.sig; @@ -3449,26 +3461,19 @@ public class CLRBackend { if (m is Role) { Role r = (Role) m; - string[] anames = new string[r.attributes.Length]; - for (int i = 0; i < anames.Length; i++) - anames[i] = r.attributes[i].name; CpsOp[] super = new CpsOp[ r.superclasses.Length ]; for (int i = 0; i < super.Length; i++) super[i] = CpsOp.GetSField(r.superclasses[i].Resolve().metaObject); thaw.Add(CpsOp.MethodCall(null, Tokens.DMO_FillRole, new CpsOp[] { CpsOp.GetSField(r.metaObject), - CpsOp.StringArray(false, anames), CpsOp.NewArray(Tokens.DynMetaObject, super), CpsOp.NewArray(Tokens.DynMetaObject, new CpsOp[0]) })); } else if (m is ParametricRole) { // The heavy lifting is done in WrapBody } else if (m is Class) { Class r = (Class) m; - List all_attr = new List(); - string[] anames = new string[r.attributes.Length]; - for (int i = 0; i < anames.Length; i++) - anames[i] = r.attributes[i].name; + List all_slot = new List(); CpsOp[] super = new CpsOp[ r.superclasses.Length ]; CpsOp[] mro = new CpsOp[ r.linearized_mro.Length ]; for (int i = 0; i < super.Length; i++) @@ -3477,13 +3482,12 @@ public class CLRBackend { Class p = r.linearized_mro[i].Resolve(); mro[i] = CpsOp.GetSField(p.metaObject); foreach (Attribute a in p.attributes) - all_attr.Add(a.name); + all_slot.Add(a.name); } thaw.Add(CpsOp.MethodCall(null, Tokens.DMO_FillClass, new CpsOp[] { CpsOp.GetSField(r.metaObject), - CpsOp.StringArray(false, anames), - CpsOp.StringArray(false, all_attr.ToArray()), + CpsOp.StringArray(false, all_slot.ToArray()), CpsOp.NewArray(Tokens.DynMetaObject, super), CpsOp.NewArray(Tokens.DynMetaObject, mro) })); } @@ -3556,6 +3560,8 @@ public class CLRBackend { if (m is ParametricRole) return; Method[] methods = (m is Class) ? ((Class)m).methods : ((Role)m).methods; + Attribute[] attrs = (m is Class) ? ((Class)m).attributes : + ((Role)m).attributes; foreach (Method me in methods) { MethodInfo mi = Tokens.DMO_AddFooMethod[me.kind]; thaw.Add(CpsOp.MethodCall(null, mi, new CpsOp[] { @@ -3564,6 +3570,14 @@ public class CLRBackend { CpsOp.GetSField(me.body.Resolve().protosub) })); } + foreach (Attribute a in attrs) { + CpsOp init = a.ibody == null ? CpsOp.Null(Tokens.IP6) : + CpsOp.GetSField(a.ibody.Resolve().protosub); + thaw.Add(CpsOp.MethodCall(null, Tokens.DMO_AddAttribute, + new CpsOp[] { CpsOp.GetSField(m.metaObject), + CpsOp.StringLiteral(a.name), + CpsOp.BoolLiteral(a.publ), init })); + } thaw.Add(CpsOp.MethodCall(null, Tokens.DMO_Invalidate, new CpsOp [] { CpsOp.GetSField(m.metaObject) })); thaw.Add(CpsOp.SetField(Tokens.DMO_how, CpsOp.GetSField(m.metaObject), diff --git a/lib/JSYNC.cs b/lib/JSYNC.cs index 4c861a15..cba86a7b 100644 --- a/lib/JSYNC.cs +++ b/lib/JSYNC.cs @@ -107,7 +107,7 @@ public class JsyncWriter { for (int i = 0; i < mo.nslots; i++) { o.Append(','); - WriteStr(true, mo.all_attr[i]); + WriteStr(true, mo.all_slot[i]); o.Append(':'); WriteObj(((Variable)dyo.slots[i]).Fetch()); } @@ -588,7 +588,7 @@ public class JsyncReader { } else { DynObject dyo = new DynObject(p_cursor.mo); for (int i = 0; i < dyo.mo.nslots; i++) { - string sn = dyo.mo.all_attr[i]; + string sn = dyo.mo.all_slot[i]; if (!zyg.ContainsKey(sn)) Err("No value for attribute " + sn + " in thawed value of class " + dyo.mo.name); dyo.slots[i] = zyg[sn]; diff --git a/lib/Kernel.cs b/lib/Kernel.cs index 0c156f64..873daa3e 100644 --- a/lib/Kernel.cs +++ b/lib/Kernel.cs @@ -1036,6 +1036,12 @@ class IxListAtPos : IndexHandler { // NOT IP6; these things should only be exposed through a ClassHOW-like // façade public class DynMetaObject { + public struct AttrInfo { + public string name; + public IP6 init; + public bool publ; + } + public static readonly ContextHandler CallStr = new CtxCallMethod("Str"); public static readonly ContextHandler CallBool @@ -1116,11 +1122,11 @@ public List superclasses = new Dictionary(); public Dictionary submethods = new Dictionary(); - public List local_attr = new List(); + public List local_attr = new List(); public Dictionary slotMap = new Dictionary(); public int nslots = 0; - public string[] all_attr; + public string[] all_slot; private WeakReference wr_this; // protected by static lock @@ -1294,6 +1300,14 @@ public List superclasses submethods[name] = code; } + public void AddAttribute(string name, bool publ, IP6 init) { + AttrInfo ai; + ai.name = name; + ai.publ = publ; + ai.init = init; + local_attr.Add(ai); + } + public IP6 GetPrivateMethod(string name) { IP6 code = priv[name]; if (code == null) { throw new NieczaException("private method lookup failed for " + name + " in class " + this.name); } @@ -1301,32 +1315,30 @@ public List superclasses } - public void FillProtoClass(string[] attr) { - FillClass(attr, attr, new DynMetaObject[] {}, + public void FillProtoClass(string[] slots) { + FillClass(slots, new DynMetaObject[] {}, new DynMetaObject[] { this }); } - public void FillClass(string[] local_attr, string[] all_attr, - DynMetaObject[] superclasses, DynMetaObject[] mro) { + public void FillClass(string[] all_slot, DynMetaObject[] superclasses, + DynMetaObject[] mro) { this.superclasses = new List(superclasses); SetMRO(mro); - this.local_attr = new List(local_attr); this.butCache = new Dictionary(); - this.all_attr = all_attr; + this.all_slot = all_slot; this.local_does = new DynMetaObject[0]; nslots = 0; - foreach (string an in all_attr) { + foreach (string an in all_slot) { slotMap[an] = nslots++; } Invalidate(); } - public void FillRole(string[] attr, DynMetaObject[] superclasses, + public void FillRole(DynMetaObject[] superclasses, DynMetaObject[] cronies) { this.superclasses = new List(superclasses); - this.local_attr = new List(attr); this.local_does = cronies; this.isRole = true; Revalidate(); // need to call directly as we aren't in any mro list @@ -1690,13 +1702,16 @@ public class Kernel { return NewROScalar(AnyP); } - public static Variable DefaultNew(IP6 proto) { + public static Variable DefaultNew(IP6 proto, VarHash args) { DynObject n = new DynObject(((DynObject)proto).mo); DynMetaObject[] mro = n.mo.mro; for (int i = mro.Length - 1; i >= 0; i--) { - foreach (string s in mro[i].local_attr) { - n.SetSlot(s, NewRWScalar(AnyMO, AnyP)); + foreach (DynMetaObject.AttrInfo a in mro[i].local_attr) { + IP6 val = a.init == null ? AnyP : + RunInferior(a.init.Invoke(GetInferiorRoot(), + Variable.None, null)).Fetch(); + n.SetSlot(a.name, NewRWScalar(AnyMO, val)); } } @@ -1937,10 +1952,11 @@ public class Kernel { DynMetaObject[] nmro = new DynMetaObject[b.mro.Length + 1]; Array.Copy(b.mro, 0, nmro, 1, b.mro.Length); nmro[0] = n; - n.FillClass(b.local_attr.ToArray(), b.all_attr, - new DynMetaObject[] { b }, nmro); + n.FillClass(b.all_slot, new DynMetaObject[] { b }, nmro); foreach (KeyValuePair kv in role.priv) n.AddPrivateMethod(kv.Key, kv.Value); + foreach (DynMetaObject.AttrInfo ai in role.local_attr) + n.AddAttribute(ai.name, ai.publ, ai.init); foreach (KeyValuePair kv in role.ord_methods) n.AddMethod(kv.Key, kv.Value); n.Invalidate(); diff --git a/lib/NieczaCLR.cs b/lib/NieczaCLR.cs index 81eaadad..b1242e96 100644 --- a/lib/NieczaCLR.cs +++ b/lib/NieczaCLR.cs @@ -69,8 +69,7 @@ public class NieczaCLR { DynMetaObject[] mro = new DynMetaObject[pm.mro.Length + 1]; Array.Copy(pm.mro, 0, mro, 1, pm.mro.Length); mro[0] = m; - m.FillClass(new string[] { }, new string[] { }, - new DynMetaObject[] { pm }, mro); + m.FillClass(new string[] { }, new DynMetaObject[] { pm }, mro); m.loc_to_clr = CLRToCLR.Instance; if (NieczaCLROpts.Debug) Console.WriteLine("Setting up wrapper for {0}", t.FullName); diff --git a/lib/SAFE.setting b/lib/SAFE.setting index da788560..1cee266a 100644 --- a/lib/SAFE.setting +++ b/lib/SAFE.setting @@ -38,8 +38,8 @@ my class Mu { [l i (+ (l i) (int 2))])] [ns (l obj)]) } } - method CREATE() { Q:CgOp { (default_new (@ (l self))) } } - method new() { Q:CgOp { (default_new (@ {self})) } } + method CREATE(*%_) { Q:CgOp { (default_new (@ {self}) (unbox varhash (@ {%_}))) } } + method new(*%_) { Q:CgOp { (default_new (@ {self}) (unbox varhash (@ {%_}))) } } } my class Any is Mu { diff --git a/src/Metamodel.pm b/src/Metamodel.pm index 3bf13c6e..176b5702 100644 --- a/src/Metamodel.pm +++ b/src/Metamodel.pm @@ -236,7 +236,7 @@ our %units; has exports => (is => 'rw', isa => 'ArrayRef[ArrayRef[Str]]'); sub add_attribute { - my ($self, $name) = @_; + my ($self, $name, $public, $ivar, $ibody) = @_; die "attribute $name defined in a lowly package"; } @@ -281,8 +281,9 @@ our %units; has _closing => (isa => 'Bool', is => 'rw'); sub add_attribute { - my ($self, $name) = @_; - push @{ $self->attributes }, Metamodel::Attribute->new(name => $name); + my ($self, $name, $public, $ivar, $ibody) = @_; + push @{ $self->attributes }, Metamodel::Attribute->new(name => $name, + public => $public, ivar => $ivar, ibody => $ibody); } sub add_method { @@ -368,8 +369,9 @@ our %units; default => sub { [] }); sub add_attribute { - my ($self, $name) = @_; - push @{ $self->attributes }, Metamodel::Attribute->new(name => $name); + my ($self, $name, $public, $ivar, $ibody) = @_; + push @{ $self->attributes }, Metamodel::Attribute->new(name => $name, + public => $public, ivar => $ivar, ibody => $ibody); } sub add_method { @@ -404,8 +406,9 @@ our %units; default => sub { [] }); sub add_attribute { - my ($self, $name) = @_; - push @{ $self->attributes }, Metamodel::Attribute->new(name => $name); + my ($self, $name, $public, $ivar, $ibody) = @_; + push @{ $self->attributes }, Metamodel::Attribute->new(name => $name, + public => $public, ivar => $ivar, ibody => $ibody); } sub add_method { @@ -455,7 +458,10 @@ our %units; package Metamodel::Attribute; use Moose; - has name => (isa => 'Str', is => 'ro', required => 1); + has name => (isa => 'Str', is => 'ro', required => 1); + has public => (isa => 'Bool', is => 'ro'); + has ivar => (isa => 'Maybe[Str]', is => 'ro'); + has ibody => (isa => 'Maybe[ArrayRef]', is => 'ro'); no Moose; __PACKAGE__->meta->make_immutable; @@ -1004,8 +1010,15 @@ sub Op::Attribute::begin { " declared outside of any class"); die "attribute $self->name declared in an augment" if $opensubs[-1]->augmenting; + my ($ibref, $ibvar); + if ($self->initializer) { + my $ibody = $self->initializer->begin; + $ibvar = Niecza::Actions->gensym; + $opensubs[-1]->add_my_sub($ibvar, $ibody); + $ibref = $ibody->xref; + } $ns = $unit->deref($ns); - $ns->add_attribute($self->name); + $ns->add_attribute($self->name, ($self->accessor ? 1 : 0), $ibvar, $ibref); my $nb = Metamodel::StaticSub->new( unit => $unit, outer => $opensubs[-1]->xref, diff --git a/src/NAMBackend.pm b/src/NAMBackend.pm index aa620803..0c385309 100644 --- a/src/NAMBackend.pm +++ b/src/NAMBackend.pm @@ -124,7 +124,7 @@ sub Metamodel::Method::to_nam { } sub Metamodel::Attribute::to_nam { - [ $_[0]->name ] + [ $_[0]->name, $_[0]->public, $_[0]->ivar, $_[0]->ibody ] } sub Sig::Parameter::to_nam { diff --git a/src/Niecza/Actions.pm b/src/Niecza/Actions.pm index 16da910f..17a6ef0b 100644 --- a/src/Niecza/Actions.pm +++ b/src/Niecza/Actions.pm @@ -1245,7 +1245,7 @@ sub INFIX { my ($cl, $M) = @_; $M->{_ast} = Op::CallSub->new(node($M), invocant => $fn, positionals => [ $l, $r ]); - if ($s eq '&infix:<=>' || $s eq '&assignop') { + if ($s eq '&infix:<=>') { # Assignments to has and state declarators are rewritten into # an appropriate phaser if ($l->isa('Op::Lexical') && $l->state_decl) { @@ -1254,6 +1254,12 @@ sub INFIX { my ($cl, $M) = @_; Op::Start->new(condvar => $cv, body => $M->{_ast}), Op::Lexical->new(name => $l->name)]); } + elsif ($l->isa('Op::Attribute') && !$l->initializer) { + $l->initializer( + $cl->sl_to_block('bare', $r, subname => $l->name . " init") + ); + $M->{_ast} = $l; + } elsif ($l->isa('Op::ConstantDecl') && !$l->init) { $l->init($r); $M->{_ast} = $l; diff --git a/src/Op.pm b/src/Op.pm index 4d47dff8..c86a39c3 100644 --- a/src/Op.pm +++ b/src/Op.pm @@ -846,6 +846,7 @@ use CgOp; has name => (isa => 'Str', is => 'ro'); has accessor => (isa => 'Bool', is => 'ro'); + has initializer => (isa => 'Maybe[Body]', is => 'rw'); sub code { my ($self, $body) = @_; diff --git a/test2.pl b/test2.pl index fc7f2f14..3dd82da8 100644 --- a/test2.pl +++ b/test2.pl @@ -26,6 +26,19 @@ ok !$i, "submethods are not inherited"; } +{ + my class X1 { + has $.a = 123; + has $.b; + } + + my $x = X1.new; + ok !defined($x.b), "without initializer, attr is undefined"; + is $x.a, 123, "initializers work"; + $x.a = 456; + is $x.a, 456, "initialized attrs can still be reset"; +} + { my $str = ''; $str ~= 1;