/
RakudoContainerSpec.java
170 lines (151 loc) Β· 6.94 KB
/
RakudoContainerSpec.java
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package org.perl6.rakudo;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
import org.perl6.nqp.runtime.*;
import org.perl6.nqp.sixmodel.*;
public class RakudoContainerSpec extends ContainerSpec {
/* Container related hints. */
public static final int HINT_descriptor = 0;
public static final int HINT_value = 1;
public static final int HINT_whence = 2;
/* Callsite descriptor for WHENCEs. */
private static final CallSiteDescriptor WHENCE = new CallSiteDescriptor(
new byte[] { }, null);
/* Fetches a value out of a container. Used for decontainerization. */
public SixModelObject fetch(ThreadContext tc, SixModelObject cont) {
return cont.get_attribute_boxed(tc, RakOps.key.getGC(tc).Scalar, "$!value", HINT_value);
}
public long fetch_i(ThreadContext tc, SixModelObject cont) {
return fetch(tc, cont).get_int(tc);
}
public double fetch_n(ThreadContext tc, SixModelObject cont) {
return fetch(tc, cont).get_num(tc);
}
public String fetch_s(ThreadContext tc, SixModelObject cont) {
return fetch(tc, cont).get_str(tc);
}
/* Stores a value in a container. Used for assignment. */
private static final CallSiteDescriptor storeThrower = new CallSiteDescriptor(
new byte[] { CallSiteDescriptor.ARG_STR, CallSiteDescriptor.ARG_OBJ, CallSiteDescriptor.ARG_OBJ }, null);
private void checkStore(ThreadContext tc, SixModelObject cont, SixModelObject value) {
RakOps.GlobalExt gcx = RakOps.key.getGC(tc);
long rw = 0;
SixModelObject desc = cont.get_attribute_boxed(tc, gcx.Scalar,
"$!descriptor", HINT_descriptor);
if (desc != null) {
desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!rw", RakOps.HINT_CD_RW);
rw = tc.native_i;
}
if (rw == 0)
if (desc != null) {
desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!name", RakOps.HINT_CD_NAME);
throw ExceptionHandling.dieInternal(tc,
"Cannot assign to a readonly variable (" + tc.native_s + ") or a value");
}
else {
throw ExceptionHandling.dieInternal(tc,
"Cannot assign to a readonly variable or a value");
}
if (value.st.WHAT == gcx.Nil) {
value = desc.get_attribute_boxed(tc,
gcx.ContainerDescriptor, "$!default", RakOps.HINT_CD_DEFAULT);
}
SixModelObject of = desc.get_attribute_boxed(tc,
gcx.ContainerDescriptor, "$!of", RakOps.HINT_CD_OF);
long ok = of == gcx.Mu ? 1 : Ops.istype(value, of, tc);
if (ok == 0) {
desc.get_attribute_native(tc, gcx.ContainerDescriptor, "$!name", RakOps.HINT_CD_NAME);
String name = tc.native_s;
SixModelObject thrower = RakOps.getThrower(tc, "X::TypeCheck::Assignment");
if (thrower == null)
throw ExceptionHandling.dieInternal(tc,
"Type check failed in assignment to '" + name + "'");
else
Ops.invokeDirect(tc, thrower,
storeThrower, new Object[] { name, value, of });
}
}
public void store(ThreadContext tc, SixModelObject cont, SixModelObject value) {
checkStore(tc, cont, value);
RakOps.GlobalExt gcx = RakOps.key.getGC(tc);
SixModelObject whence = cont.get_attribute_boxed(tc, gcx.Scalar, "$!whence", HINT_whence);
if (whence != null)
Ops.invokeDirect(tc, whence,
WHENCE, new Object[] { });
if (value.st.WHAT == gcx.Nil) {
SixModelObject desc = cont.get_attribute_boxed(tc, gcx.Scalar,
"$!descriptor", HINT_descriptor);
value = desc.get_attribute_boxed(tc,
gcx.ContainerDescriptor, "$!default", RakOps.HINT_CD_DEFAULT);
}
cont.bind_attribute_boxed(tc, gcx.Scalar, "$!value", HINT_value, value);
}
public void store_i(ThreadContext tc, SixModelObject cont, long value) {
store(tc, cont, RakOps.p6box_i(value, tc));
}
public void store_n(ThreadContext tc, SixModelObject cont, double value) {
store(tc, cont, RakOps.p6box_n(value, tc));
}
public void store_s(ThreadContext tc, SixModelObject cont, String value) {
store(tc, cont, RakOps.p6box_s(value, tc));
}
/* Stores a value in a container, without any checking of it (this
* assumes an optimizer or something else already did it). Used for
* assignment. */
public void storeUnchecked(ThreadContext tc, SixModelObject cont, SixModelObject obj) {
SixModelObject Scalar = RakOps.key.getGC(tc).Scalar;
SixModelObject whence = cont.get_attribute_boxed(tc, Scalar, "$!whence", HINT_whence);
if (whence != null)
Ops.invokeDirect(tc, whence,
WHENCE, new Object[] { });
cont.bind_attribute_boxed(tc, Scalar, "$!value", HINT_value, obj);
}
/* Name of this container specification. */
public String name() {
return "rakudo_scalar";
}
/* Serializes the container data, if any. */
public void serialize(ThreadContext tc, STable st, SerializationWriter writer) {
/* No data to serialize. */
}
/* Deserializes the container data, if any. */
public void deserialize(ThreadContext tc, STable st, SerializationReader reader) {
/* No data to deserialize. */
}
/* Atomic operations. */
private Unsafe unsafe;
private long scalarValueOffset;
@SuppressWarnings("restriction")
private void ensureAtomicsReady(SixModelObject cont) {
if (unsafe == null) {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe)unsafeField.get(null);
scalarValueOffset = unsafe.objectFieldOffset(
cont.getClass().getDeclaredField("field_1"));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public SixModelObject cas(ThreadContext tc, SixModelObject cont,
SixModelObject expected, SixModelObject value) {
ensureAtomicsReady(cont);
checkStore(tc, cont, value);
return unsafe.compareAndSwapObject(cont, scalarValueOffset, expected, value)
? expected
: (SixModelObject)unsafe.getObjectVolatile(cont, scalarValueOffset);
}
public SixModelObject atomic_load(ThreadContext tc, SixModelObject cont) {
ensureAtomicsReady(cont);
return (SixModelObject)unsafe.getObjectVolatile(cont, scalarValueOffset);
}
public void atomic_store(ThreadContext tc, SixModelObject cont,
SixModelObject value) {
ensureAtomicsReady(cont);
checkStore(tc, cont, value);
unsafe.putObjectVolatile(cont, scalarValueOffset, cont);
}
}