Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: serialize
Fetching contributors…

Cannot retrieve contributors at this time

809 lines (704 sloc) 27.712 kb
class Metamodel;
method locstr($fo, $lo, $fn, $ln) {
$fo := $fo // '???';
$lo := $lo // '???';
$fn := $fn // '???';
$ln := $ln // '???';
$fn eq $fo ?? " (see line $lo)" !! " (see $fo line $lo)";
}
# keep synchronized with the list of Kernel.PHASER_XXX values
enum Phaser < INIT END UNIT_INIT KEEP UNDO LEAVE ENTER PRE POST CATCH CONTROL >;
### NIECZA COMPILER METAMODEL
# The metamodel exists to create a timeline inside the compiler. Previously,
# the compiler operated as a pure tree transformer with no conception of how
# PRE-INIT code would play out, thus precluding a lot of important
# optimizations (based on precomputing immutable objects and optimizing
# references to them, mostly).
#
# The metamodel has two main life stages. First, it is built; an incremental
# process logically called BEGIN. Then, it is processed to perform closed-
# world optimizations and generate code; this is (UNIT)CHECK.
#
# Kinds of objects which exist in the metamodel
# - Static subs
# - Packages (incl. classes, modules, grammars)
# - Stashes (Foo::)
#
# This graph is a lot more random than the old trees were...
# While manipulating metamodel bits during BEGIN, these contextuals are needed:
# $*unit: current unit for new objects to attach to
# %*units: maps unit names to unit objects
# $*CURSUB<!sub>: the top non-transparent sub
# Almost all longname and most identifier uses in Perl6 can be divided into
# two groups.
#
# DECLARATIVE references, like class Foo::Bar::Baz {}, have an ending token,
# and the remainder identifies a stash. Leading :: is ignored; if 0 tokens,
# anon is forced, if 1, scope-sensitive special behavior, if 2+, our required.
# Evaluating a declarative reference returns a (stash,name) pair.
#
# REFERENTIAL names, like $Foo::Bar::baz, are interpreted as referring to a
# single variable; in many cases this is used to look for a type object.
# Referential names default to MY:: if 1 token and 0 leading colon.
# Evaluating a referential name returns or binds a variable.
#
# The one exception seems to be method calls, which take a referential name
# plus an extra identifier to name the method.
#
# Trailing :: is forbidden when declaring and means .WHO when referencing.
#
# Functions for handling names in actions:
#
# package_var: Basic function for handling referential names, produces Op.
#
# immed_ref: Like package_var in a BEGIN context.
#
# decl_expr:
#
# immed_decl:
# A stash is an object like Foo::. Stashes are named to allow them to be
# sensibly named across merges.
#
# 'my' stashes are really 'our' stashes with gensym mergable names. Because
# stashes have no identity beyond their contents and set of names, they don't
# mind being copied around a lot.
#
# Stashes are not referencable objects in precompilation mode. You need to
# keep the paths around, instead.
#
# This object holds the stash universe for a unit.
# XXX forward decls are a little broken
my $Package;
class Namespace {
# all maps stash names to stashes. Stashes are represented as simple
# hashes here; the values are always arrays like [$xref, $file, $line].
# $xref may be undefined to indicate a stash entry with no compile-time
# value (our $x, my $x is export).
#
# Stash names are keyed like "GLOBAL::Foo::Bar" or "MAIN:15". Stashes
# outside GLOBAL or PROCESS are anonymous packages, for my aliasing.
has %.all;
# Records *local* operations, so they may be stored and used to
# set up the runtime stashes. Read-only log access is part of the
# public API.
#
# Each entry is an arrayref of the form [$who, $name, $xref, $file, $line].
has @.log;
# This is set up post-creation by NieczaGrammar. It points to a package
# with a who of ''.
has $.root is rw;
method _merge_item($i1, $i2, $who, $name) {
# supress absent entries
return $i2 unless defined $i1;
return $i1 unless defined $i2;
# suppress simple COMMONs if no absent
return $i2 unless defined $i1[0];
return $i1 unless defined $i2[0];
# ooh, we now know we have no COMMONs
my $item1 = $*unit.deref($i1[0]);
my $item2 = $*unit.deref($i2[0]);
return $i1 if $item1 === $item2;
if $item1.^isa($Package) && $item2.^isa($Package) &&
$item1.who eq $item2.who &&
($item1.WHAT === $Package || $item2.WHAT === $Package) {
return $i1;
}
die "Two definitions found for symbol {$who}::$name\n\n" ~
" first at $i1[1] line $i1[2]\n" ~
" second at $i2[1] line $i2[2]";
}
method exists($who, $item) {
return ?(%!all{$who}{$item});
}
method get($who, $item) {
return %!all{$who}{$item}[0]
}
method bind($who, $name, $item, :$file, :$line, :$pos) { #OK not used
my $slot := %!all{$who}{$name};
$slot = self._merge_item($slot, [ $item,
$file // '???', $line // '???' ], $who, $name);
push @!log, [ $who, $name, $item, $file, $line ];
}
method get_pkg($from is copy, *@names, :$auto) {
for @names {
my $sl = self.get($from.who, $_);
my $pkg;
if $sl && $sl[0] && ($pkg = $*unit.deref($sl)).^isa($Package) {
} elsif !$auto {
die "Name component $_ not found in $from.who()";
} else {
$pkg = $Package.new(name => $_, who => $from.who ~ '::' ~ $_);
self.bind($from.who, $_, $pkg.xref);
}
$from = $pkg;
}
$from;
}
# Add a new unit set to the from-set and checks mergability
method add_from($from) {
for %*units{$from}.ns.log -> $logent {
# not using bind since we don't want this in the log
my $slot := %!all{$logent[0]}{$logent[1]};
$slot = self._merge_item($slot, [ $logent[2], $logent[3],
$logent[4] ], $logent[0], $logent[1]);
}
}
# List objects in a stash for use by the importer; returns pairs
# of [name, xref]
method list_stash($who) {
my $h = %!all{$who};
map -> $a { $a => $h{$a}[0] }, sort keys $h;
}
}
class RefTarget {
has $.xref;
has $.name = 'ANON';
# TODO BUILD
method new(:$no_xref, *%_) {
my $n = callwith(self, |%_);
return $n if $no_xref;
$n.xref = [ $*unit.name, +$*unit.xref, $n.name ];
push $*unit.xref, $n;
$n
}
method set_name($name) {
$!xref[2] = $!name = $name;
}
}
class Package is RefTarget {
has $.closed;
has $.who;
method close() { $!closed = True; }
}
$Package = Package;
class Module is Package {
}
class Method {
# normally a Str, but may be Op for param roles
has $.name = die "Method.name is required";
# normal, private, meta, sub
has $.kind = die "Method.kind is required"; # Str
has $.multi = die "Method.multi is required"; # Str
has $.var; # Str
has $.body; # Xref
has $.file;
has $.line;
}
class Attribute {
has $.name; # Str, required
has $.sigil; # Str, required
has $.public; # Bool
has $.ivar; # Str
has $.ibody; # Xref
has $.typeconstraint; # Xref
has $.file;
has $.line;
}
class Class is Module {
has $.attributes = [];
has $.methods = [];
has $.superclasses = [];
has $.linearized_mro; # is rw
has $!closing;
method add_attribute($name, $sigil, $public, $ivar, $ibody,
$typeconstraint, :$file, :$line, :$pos) { #OK not used
if grep $name eq *.name, @($!attributes) -> $O {
die "Two definitions of attribute $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
push $.attributes, Metamodel::Attribute.new(:$name, :$sigil,
:$public, :$ivar, :$ibody, :$typeconstraint, :$file, :$line);
$.attributes.[*-1];
}
method add_method($multi, $kind, $name, $var, $body, :$file, :$line, :$pos) { #OK not used
if $name ~~ Str && $multi eq 'only' &&
grep { $name eq .name && $kind eq .kind }, @($!methods) -> $O {
die "Two definitions of method $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
push $.methods, Metamodel::Method.new(:$name, :$body, :$kind, :$multi,
:$file, :$line);
}
method add_super($targ) {
die "bad attempt to add null super" unless $targ;
push $.superclasses, $targ;
}
sub c3clear($item, @lists) {
for @lists -> $l {
my $i = 1;
while $i < $l {
return False if $*unit.deref($l[$i]) === $*unit.deref($item);
$i++;
}
}
for @lists -> $l {
$l.shift if $l && $*unit.deref($l[0]) === $*unit.deref($item);
}
True;
}
sub c3merge(@onto, @lists) {
my $ix = 0;
while $ix < @lists {
#say "C3 MRO status ($ix):";
#say "Onto: ", @onto.map({ $*unit.deref($_).name }).join(" <- ");
#say $_.map({ $*unit.deref($_).name }).join(" <- ") for @lists;
#say "---";
my $l = @lists[$ix];
if !$l || !c3clear((my $item = $l[0]), @lists) {
$ix++;
next;
}
push @onto, $item;
$ix = 0;
}
my $bad = False;
for @lists -> $l { $bad ||= $l }
if $bad {
my @hrl = @lists.grep(*.Bool).map(
{ $^l.map({ $*unit.deref($^i).name }).join(" <- ") });
die "C3-MRO wedged! @hrl.join(" | ")";
}
}
method close() {
return if $.closed;
if ($!closing) {
die "Class hierarchy circularty detected at $.name\n";
}
$!closing = True;
if (($.name ne 'Mu' || !$*unit.is_true_setting)
&& !$.superclasses) {
self.add_super($*CURLEX<!sub>.compile_get_pkg(self._defsuper).xref);
}
my @merge;
push @merge, [ $.xref ];
for @$.superclasses -> $x {
my $d = $*unit.deref($x);
$d.close unless $d.linearized_mro;
push @merge, [ @( $d.linearized_mro ) ];
}
push @merge, [ @( $.superclasses ) ];
my @mro;
c3merge(@mro, @merge);
$.linearized_mro = @mro;
nextsame;
}
method _defsuper() { 'CORE', 'Any' }
}
# roles come in two types; Role objects are used for simple roles, while roles
# with parameters get ParametricRole. Instantiations of parametric roles
# would get ConcreteRole, but that won't be implemented in Niecza A since it
# requires evaluating role parameters, unless we restrict it to typenames or
# something.
class Role is Module {
has $.attributes = [];
has $.methods = [];
has $.superclasses = [];
method add_attribute($name, $sigil, $public, $ivar, $ibody, $typeconstraint, :$file, :$line, :$pos) { #OK not used
if grep $name eq *.name, @($!attributes) -> $O {
die "Two definitions of attribute $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
push $.attributes, Metamodel::Attribute.new(:$name, :$sigil,
:$public, :$ivar, :$ibody, :$typeconstraint, :$file, :$line);
$.attributes.[*-1];
}
method add_method($multi, $kind, $name, $var, $body, :$file, :$line, :$pos) { #OK not used
if $name ~~ Str && $multi eq 'only' &&
grep { $name eq .name && $kind eq .kind }, @($!methods) -> $O {
die "Two definitions of method $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
if $name !~~ Str {
die "Computed names are legal only in parametric roles";
}
push $.methods, Metamodel::Method.new(:$name, :$body, :$kind,
:$multi, :$file, :$line);
}
method add_super($targ) {
die "bad attempt to add null super" unless $targ;
push $.superclasses, $targ;
}
}
class ParametricRole is Module {
has $.attributes = [];
has $.methods = [];
has $.superclasses = [];
method add_attribute($name, $sigil, $public, $ivar, $ibody, $typeconstraint, :$file, :$line, :$pos) { #OK not used
if grep $name eq *.name, @($!attributes) -> $O {
die "Two definitions of attribute $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
push $.attributes, Metamodel::Attribute.new(:$name, :$sigil,
:$public, :$ivar, :$ibody, :$typeconstraint, :$file, :$line);
$.attributes.[*-1];
}
method add_method($multi, $kind, $name, $var, $body, :$file, :$line, :$pos) { #OK not used
if $name ~~ Str && $multi eq 'only' &&
grep { $name eq .name && $kind eq .kind }, @($!methods) -> $O {
die "Two definitions of method $name" ~ Metamodel.locstr($O[0].file, $O[0].line, $file, $line);
}
push $.methods, ::Metamodel::Method.new(:$name, :$body, :$var, :$kind, :$multi, :$file, :$line);
}
method add_super($targ) {
die "bad attempt to add null super" unless $targ;
push $.superclasses, $targ;
}
}
class Grammar is Class {
method _defsuper() { 'CORE', 'Grammar' }
}
# subsets are a bit simpler than roles/grammars/classes, as they have
# no body and so attributes &c cannot be added to them directly.
class Subset is Module {
# subset <longname>? <trait>* [where <EXPR>]?
has $.basetype is rw;
# Xref to a sub which will be called once the first time the subset
# is used.
has $.where is rw;
}
#####
# This is a static lexical; they exist in finite number per unit. They may
# occupy specific slots in pads, or globals, or something else entirely.
class Lexical {
has $.file;
has $.line;
has $.pos;
# my $foo, @foo, %foo, &foo
class Simple is Lexical {
has Bool $.list = False;
has Bool $.hash = False;
has Bool $.noinit = False;
has Bool $.defouter = False;
has Bool $.roinit = False;
has $.typeconstraint; # Xref
}
# These are used for $?foo et al, and should be inaccessible until assigned,
# although the current code won't enforce that well.
class Hint is Lexical {
}
# These store destinations for lexotic control transfers, and clone like
# subs to handle recursion properly.
class Label is Lexical {
}
class Dispatch is Lexical {
}
# our...
class Common is Lexical {
has $.pkg = die "M:L:Common.path required"; # Xref to Package
has $.name = die "M:L:Common.name required"; # Str
}
# mostly for state
class Alias is Lexical {
has $.to = die "M:L:Alias.to required"; # Str
}
# sub foo { ... }
class SubDef is Lexical {
has $.body; # Metamodel::StaticSub
}
# my class Foo { } or our class Foo { }; either case, the true
# stash lives in stashland. Actually this points at a package now.
class Stash is Lexical {
has $.pkg; # Xref
}
}
# The life cycle of a static sub has three phases.
# 1. Open - the end of the sub hasn't been seen, so the full code is absent.
# 2. Closing - all attributes are available but no references exist. The
# perfect time for most optimizations, especially ones that look like
# escape analyses.
# 3. Closed - references exist, possibly even from BEGIN-run code. The sub
# must be treated as semantically immutable. The code can probably still
# be changed to reflect new information, though.
# figure out how post-declared lexicals should interact with codegen
# std accepts: sub foo() { bar }; BEGIN { foo }; sub bar() { }
# DONE: TimToady says bar can be compiled to a runtime search
class StaticSub is RefTarget {
has $.unit; # Metamodel::Unit
has $.outerx; # Xref
# points directly to the outer so that explain_mystery doesn't need
# to worry about inlining...
has $.outer_direct is rw;
has Bool $.run_once is rw = False;
has Bool $.spad_exists is rw = False;
has Bool $.transparent = False; # ignored by OUTER::
has %.lexicals;
has $.code is rw; # Op
has $.signature is rw; # Sig
has $.zyg = []; # Array of Metamodel::StaticSub
# inject a take EMPTY
has Bool $.gather_hack is rw = False;
# inject a role constructor (Xref)
has $.parametric_role_hack is rw;
# some tuples for method definitions; munged into a phaser
has $.augment_hack is rw;
# emit code to assign to a hint; [ $subref, $name ]
has $.hint_hack is rw;
has $.is_phaser is rw; # Int
has Bool $.strong_used is rw = False; # prevents elision
has $.body_of is rw; # Xref of Package
has $.in_class is rw; # Xref of Package
has $.cur_pkg is rw; # Xref of Package
has Bool $.returnable is rw = False; # catches &return
has Bool $.augmenting is rw = False; # traps add_attribute
has Bool $.unsafe is rw = False; # disallowed in safe mode
has Str $.class is rw = 'Sub';
has $.ltm is rw;
# a place to hang off extra stuff that's not used for most subs
# currently: "prec" for operators, "builtin" for primitives
has $.extend is rw;
# used during parse only
has Str $.outervar is rw; # Xref, used during parse
has $.methodof is rw; # Xref, used during parse
has %.lexicals-used;
# not just local lexicals, but all in parse; from current or any inside
# block
method outer() {
$!outer_direct //= ($!outerx ?? $*unit.deref($!outerx) !! StaticSub)
}
method true_setting() {
my $cursor = self;
while $cursor && !$cursor.unit.is_true_setting {
$cursor = $cursor.outer;
}
$cursor || self;
}
method to_unit() {
my $cursor = self;
my $unit = self.unit;
my $outer;
while ($outer = $cursor.outer) && $outer.unit === $unit {
$cursor = $outer
}
$cursor;
}
method is_routine() {
state %routine = (:Routine, :Sub, :Submethod, :Method, :Regex);
%routine{$!class}
}
method add_child($z) { push $.zyg, $z }
method children() { @$.zyg }
method clear_optree() {
$.code = Any;
$.ltm = Any;
}
method create_static_pad() {
return Nil if $!spad_exists;
$!spad_exists = True;
$.outer.create_static_pad if $.outer && $.outer.unit === $!unit;
}
method noninlinable() {
loop (my $c = self; $c && $c.unit === $*unit; $c = $c.outer) {
$c.strong_used = True;
}
}
method topicalizer() {
$.signature && ?( grep { .slot && .slot eq '$_' }, @( $.signature.params ) )
}
# helper for compile_get_pkg; handles stuff like SETTING::OUTER::Foo,
# recursively.
method _lexy_ref(*@names, :$auto) {
@names || die "Cannot use a lexical pseudopackage as a compile time package reference";
self // die "Passed top of lexical tree";
given shift @names {
when 'OUTER' { return self.outer._lexy_ref(@names, :$auto) }
when 'SETTING' { return self.to_unit.outer._lexy_ref(@names, :$auto) }
when 'UNIT' { return self.to_unit._lexy_ref(@names, :$auto) }
when 'CALLER' { die "Cannot use CALLER in a compile time name" }
default {
my $lex = self.find_lex($_);
$lex // die "No lexical found for $_";
$lex.^isa(Metamodel::Lexical::Stash) || die "Lexical $_ is not a package";
return $*unit.get_pkg($*unit.deref($lex.pkg), @names, :$auto);
}
}
}
# returns direct reference to package, or dies
method compile_get_pkg(*@names, :$auto) {
@names || die "Cannot make a compile time reference to the semantic root package";
my $n0 = shift(@names);
if $n0 eq 'OUR' {
return $*unit.get_pkg($*unit.deref($!cur_pkg), @names, :$auto);
} elsif $n0 eq 'PROCESS' or $n0 eq 'GLOBAL' {
return $*unit.abs_pkg($n0, @names, :$auto);
} elsif $n0 eq any < COMPILING DYNAMIC CLR CALLER > {
# Yes, COMPILING is right here. Because COMPILING is only valid
# when recursively running code within the compiler, but this
# function is only called directly from the compiler. The closest
# it comes to making sense is if you use eval in a macro. Don't
# do that, okay?
die "Pseudo package $n0 may not be used in compile time reference";
} elsif $n0 eq 'MY' {
return self._lexy_ref(@names, :$auto);
} elsif $n0 eq 'CORE' {
return self.true_setting._lexy_ref(@names, :$auto);
} elsif $n0 eq 'OUTER' or $n0 eq 'SETTING' or $n0 eq 'UNIT' {
return self._lexy_ref($n0, @names, :$auto);
} elsif $n0 ne 'PARENT' && self.find_lex($n0) {
return self._lexy_ref($n0, @names, :$auto);
} elsif $n0 ~~ /^\W/ {
return $*unit.get_pkg($*unit.deref($!cur_pkg), $n0, @names, :$auto);
} else {
return $*unit.abs_pkg('GLOBAL', $n0, @names, :$auto);
}
}
method bind_our_name($path, $name, $item, *%_) {
my $pkg = self.compile_get_pkg($path ?? @$path !! 'OUR', :auto);
$*unit.bind($pkg, $name, $item, |%_);
}
method find_lex($name) {
my $l = %!lexicals{$name};
if $l {
return $l.^isa(Metamodel::Lexical::Alias) ??
self.find_lex($l.to) !! $l;
}
return ($.outer ?? $.outer.find_lex($name) !! Metamodel::Lexical);
}
method delete_lex($name) {
my $l = %!lexicals{$name};
if $l {
if $l.^isa(Metamodel::Lexical::Alias) { self.delete_lex($l.to) }
else { %!lexicals{$name}:delete }
} else {
$.outer && $.outer.unit === $.unit && $.outer.delete_lex($name);
}
}
method add_lex($slot, $item) {
if %!lexicals{$slot} -> $o {
my $l = Metamodel.locstr($o.file, $o.line, $item.file, $item.line);
if $slot ~~ /^\w/ {
die "Illegal redeclaration of symbol '$slot'$l";
} elsif $slot ~~ /^\&/ {
die "Illegal redeclaration of routine '$slot.substr(1)'$l";
} else {
$*worry.("Useless redeclaration of variable $slot$l");
return;
}
}
# We don't know in advance if $_ exists. This is OK.
# TODO: The semantics are off here. $_ should be in every block.
elsif $slot ne '$_' && %!lexicals-used{$slot} -> $p {
my $truename = $slot;
my $c = self;
while $c && !$c.lexicals{$slot} {
$truename ~~ s/<?before \w>/OUTER::/;
$c = $c.outer;
}
die "Lexical tracking inconsistency" unless $c;
my $o = $c.lexicals{$slot};
die "Lexical symbol '$slot' is already bound to an outer symbol{Metamodel.locstr($o.file, $o.line, $item.file, $item.line)};\n the implicit outer binding at line $p.value() must be rewritten as $truename\n before you can unambiguously declare a new '$slot' in this scope";
}
%!lexicals{$slot} = $item;
if substr($slot,0,1) eq '&' && (%*MYSTERY{substr($slot,1)}:exists) {
%!lexicals-used{$slot} = True;
}
}
method add_my_name($slot, *%param) {
self.add_lex($slot, Metamodel::Lexical::Simple.new(|%param));
}
method add_hint($slot, *%params) {
self.add_lex($slot, Metamodel::Lexical::Hint.new(|%params));
}
method add_label($slot, *%params) {
self.add_lex($slot, Metamodel::Lexical::Label.new(|%params));
}
method add_dispatcher($slot, *%params) {
self.add_lex($slot, Metamodel::Lexical::Dispatch.new(|%params));
}
method add_common_name($slot, $pkg, $name, :$file, :$line, :$pos) {
$*unit.bind($*unit.deref($pkg), $name, Any, :$file, :$line)
unless $*unit.ns.exists($*unit.deref($pkg).who, $name);
self.add_lex($slot, Metamodel::Lexical::Common.new(:$pkg, :$name,
:$file, :$line, :$pos));
}
method add_state_name($slot, $back, *%param) {
# outermost sub isn't cloned so a fallback to my is safe
my $up = (self === self.to_unit) ?? self !! self.outer;
$up.lexicals{$back} = Metamodel::Lexical::Simple.new(|%param);
if defined($slot) {
self.add_lex($slot, Metamodel::Lexical::Alias.new(to => $back,
|%param));
}
}
method add_my_stash($slot, $pkg, *%params) {
self.add_lex($slot, Metamodel::Lexical::Stash.new(:$pkg, |%params));
}
method add_my_sub($slot, $body, *%params) {
self.add_lex($slot, Metamodel::Lexical::SubDef.new(:$body, |%params));
}
method add_exports($name, $xref, $tags) {
for @$tags -> $tag {
$*unit.bind($*unit.get_pkg($*unit.deref($!cur_pkg), 'EXPORT',
$tag, :auto), $name, $xref);
}
+$tags;
}
method close() { }
}
class Unit {
has Metamodel::StaticSub $.mainline is rw;
has Str $.name;
has Metamodel::Namespace $.ns;
has $.setting_ref is rw;
has $.bottom_ref is rw;
has $.xref = [];
has $.tdeps = {};
has Str $.filename is rw;
has $.modtime is rw; # Numeric
has Int $.next_anon_stash is rw = 0; # is rw, Int
has @.stubbed_stashes; # Pair[Stash,Cursor]
method bind($pkg,$name,$item,*%_) { $!ns.bind($pkg.who,$name,$item,|%_) }
method list_stash($pkg) { $!ns.list_stash($pkg.who) }
method get($pkg,$name) { $!ns.get($pkg.who,$name) }
method get_pkg($pkg,*@names,:$auto) { $!ns.get_pkg($pkg,@names,:$auto) }
method abs_pkg(*@names, :$auto) {
$!ns.get_pkg($*unit.deref($!ns.root),@names,:$auto)
}
method is_true_setting() { $!name eq 'CORE' }
method get_unit($name) { %*units{$name} }
method anon_stash() { "{$.name}:{$.next_anon_stash++}" }
method deref($thing) {
die "trying to dereference null" unless $thing;
self.get_unit($thing[0]).xref[$thing[1]] // die "invalid ref @$thing";
}
method visit_units_preorder($cb) {
my %seen;
sub rec {
return Nil if %seen{$_};
%seen{$_} = True;
for sort keys self.get_unit($_).tdeps { rec($_) }
$cb(self.get_unit($_));
}
rec($.name);
}
method visit_local_packages($cb) {
for @$.xref -> $x {
$cb($x) if defined($x) && $x.^isa(Metamodel::Package);
}
}
method clear_optrees() {
self.visit_local_subs_postorder({ $_.clear_optree })
}
method visit_local_subs_postorder($cb) {
sub rec {
for $_.children { rec($_) }
$cb($_);
}
rec($.mainline);
}
method visit_local_subs_preorder($cb) {
sub rec {
$cb($_);
for $_.children { rec($_) }
}
rec($.mainline);
}
method need_unit($u2name) {
return %*units{$u2name} if $.tdeps{$u2name};
my $u2 = %*units{$u2name} //= $*module_loader.($u2name);
$.tdeps{$u2name} = [ $u2.filename, $u2.modtime ];
my @new = $u2name;
for keys $u2.tdeps -> $k {
next if $.tdeps{$k};
push @new, $k;
%*units{$k} //= $*module_loader.($k);
$.tdeps{$k} = $u2.tdeps{$k};
}
$!ns.add_from($_) for @new;
$u2;
}
}
Jump to Line
Something went wrong with that request. Please try again.