-
-
Notifications
You must be signed in to change notification settings - Fork 372
/
Stash.pm6
155 lines (145 loc) Β· 5.51 KB
/
Stash.pm6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
my class Stash { # declared in BOOTSTRAP
# class Stash is Hash
# has str $!longname;
# has $!lock;
multi method new(Stash: --> Stash:D) {
nqp::p6bindattrinvres(nqp::create(self), Stash, '$!lock', Lock.new)
}
method clone(Stash:D:) is raw {
my $cloned := callsame();
nqp::bindattr_s(
$cloned, Stash, '$!longname',
nqp::getattr_s(self, Stash, '$!longname'));
nqp::bindattr($cloned, Stash, '$!lock', Lock.new);
$cloned
}
multi method AT-KEY(Stash:D: Str:D $key) is raw {
my \storage := nqp::getattr(self,Map,'$!storage');
nqp::existskey(storage,$key)
?? nqp::atkey(storage,$key)
!! nqp::p6scalarfromdesc(
ContainerDescriptor::BindHashKey.new(Mu, self, $key)
)
}
multi method AT-KEY(Stash:D: Str() $key, :$global_fallback!) is raw {
my \storage := nqp::getattr(self,Map,'$!storage');
nqp::if(
nqp::existskey(storage,$key),
nqp::atkey(storage,$key),
nqp::if(
$global_fallback,
nqp::if(
nqp::existskey(GLOBAL.WHO,$key),
nqp::atkey(GLOBAL.WHO,$key),
"Could not find symbol '$key' in '{self}'".Failure
),
nqp::p6scalarfromdesc(
ContainerDescriptor::BindHashKey.new(Mu, self, $key)
)
)
)
}
# New proto is introduced here in order to cut off Hash candidates completely. There are few reasons to do so:
# 1. Hash is not thread-safe whereas Stash claims to be
# 2. Stash candidates are fully overriding their counterparts from Hash
# 3. Minor: could result in some minor improvement in multi-dispatch lookups due to lesser number of candidates
proto method ASSIGN-KEY(Stash:D: $, $) {*}
multi method ASSIGN-KEY(Stash:D: Str:D $key, Mu \assignval) is raw {
my $storage := nqp::getattr(self,Map,'$!storage');
my \existing-key := nqp::atkey($storage, $key);
if nqp::isnull(existing-key) {
$!lock.protect: {
my \scalar := nqp::bindkey(
($storage := nqp::clone($storage)),
$key,
nqp::p6assign(
nqp::p6bindattrinvres(
nqp::create(Scalar),
Scalar,
'$!descriptor',
nqp::getattr(self, Hash, '$!descriptor')),
assignval)
);
#?if moar
nqp::atomicbindattr(self, Map, '$!storage', $storage);
#?endif
#?if !moar
# XXX Here and below: nqp::atomicbindattr works on JVM but still breaks building CORE.d. Guess, it is
# not serializing properly, but a quick fix, similar to MoarVM's
# https://github.com/MoarVM/MoarVM/commit/a9fcd5a74e8c530b4baa8fdc348b82a71bc0824d, where this problem
# was observed too, didn't fix the situation. So, let's stick to the unsafe path for now.
nqp::bindattr(self, Map, '$!storage', $storage);
#?endif
scalar
};
}
else {
nqp::p6assign(existing-key, assignval);
}
}
multi method ASSIGN-KEY(Stash:D: \key, Mu \assignval) is raw {
nextwith(key.Str, assignval)
}
# See the comment for ASSIGN-KEY on proto.
proto method BIND-KEY(|) {*}
multi method BIND-KEY(Stash:D: Str:D $key, Mu \bindval) is raw {
$!lock.protect: {
my $storage := nqp::clone(nqp::getattr(self,Map,'$!storage'));
nqp::bindkey($storage, $key, bindval);
#?if moar
nqp::atomicbindattr(self, Map, '$!storage', $storage);
#?endif
#?if !moar
nqp::bindattr(self, Map, '$!storage', $storage);
#?endif
}
bindval
}
multi method BIND-KEY(Stash:D: \key, Mu \bindval) is raw {
nextwith(key.Str, bindval)
}
method package_at_key(Stash:D: str $key) {
my $storage := nqp::getattr(self,Map,'$!storage');
nqp::ifnull(
nqp::atkey($storage,$key),
$!lock.protect({
my $pkg := Metamodel::PackageHOW.new_type(:name("{$!longname}::$key"));
$pkg.^compose;
$storage := nqp::clone($storage);
nqp::bindkey($storage,$key,$pkg);
#?if moar
nqp::atomicbindattr(self, Map, '$!storage', $storage);
#?endif
#?if !moar
nqp::bindattr(self, Map, '$!storage', $storage);
#?endif
$pkg
})
)
}
multi method gist(Stash:D:) {
self.Str
}
multi method Str(Stash:D:) {
nqp::isnull_s($!longname) ?? '<anon>' !! $!longname
}
method merge-symbols(Stash:D: Mu \globalish) { # NQP gives a Hash, not a Stash
if nqp::defined(globalish) {
$!lock.protect: {
# For thread safety, ModuleLoader's merge_globals is calling this method when its target is a Stash.
# Therefore we call it on cloned symbol hash. This prevents recursion and works slightly faster.
nqp::gethllsym('Raku','ModuleLoader').merge_globals(
(my $storage := nqp::clone(my $old-storage := nqp::getattr(self,Map,'$!storage'))),
globalish
);
#?if moar
nqp::atomicbindattr(self, Map, '$!storage', $storage);
#?endif
#?if !moar
nqp::bindattr(self, Map, '$!storage', $storage);
#?endif
}
}
}
}
# vim: expandtab shiftwidth=4