nomen-lang 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOMEN_AGENTS.md +7 -1
- package/core/System/Arena.nm +7 -7
- package/core/System/Awaitable.nm +21 -0
- package/core/System/BigInt.nm +5 -15
- package/core/System/Buffer.nm +140 -33
- package/core/System/Channel.nm +80 -29
- package/core/System/ClassBuffer.nm +89 -32
- package/core/System/Console.nm +4 -2
- package/core/System/Controls/Button.nm +4 -4
- package/core/System/Controls/CheckBox.nm +1 -1
- package/core/System/Controls/Text.nm +2 -2
- package/core/System/Controls/TextBox.nm +6 -6
- package/core/System/Controls/Window.nm +2 -2
- package/core/System/Fiber.nm +121 -0
- package/core/System/Graph.nm +4 -4
- package/core/System/LinkedList.nm +4 -4
- package/core/System/List.nm +6 -6
- package/core/System/Map.nm +30 -30
- package/core/System/Mutex.nm +57 -61
- package/core/System/Nursery.nm +10 -8
- package/core/System/Set.nm +24 -24
- package/core/System/Stream/Directory.nm +4 -4
- package/core/System/Stream/File.nm +19 -12
- package/core/System/Stream/Http.nm +83 -434
- package/core/System/Stream/Tcp.nm +441 -0
- package/core/System/String.nm +47 -18
- package/core/System/Task.nm +103 -4
- package/core/System/Text/JsonTree.nm +15 -15
- package/core/System/Thread.nm +83 -0
- package/core/System/bool.nm +2 -1
- package/core/System/char.nm +2 -1
- package/core/System/float.nm +2 -1
- package/core/System/float32.nm +2 -1
- package/core/System/float64.nm +2 -1
- package/core/System/int.nm +4 -2
- package/core/System/int16.nm +2 -1
- package/core/System/int32.nm +2 -1
- package/core/System/int64.nm +2 -1
- package/core/System/int8.nm +2 -1
- package/core/System/ufloat.nm +2 -1
- package/core/System/ufloat32.nm +2 -1
- package/core/System/ufloat64.nm +2 -1
- package/core/System/uint.nm +2 -1
- package/core/System/uint16.nm +2 -1
- package/core/System/uint32.nm +2 -1
- package/core/System/uint64.nm +2 -1
- package/core/System/uint8.nm +2 -1
- package/core/docs/System/BigInt.md +0 -1
- package/dist/index.mjs +20401 -13796
- package/package.json +1 -1
package/core/System/Channel.nm
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Channel — thread-safe single-direction queue for passing values between
|
|
2
2
|
// tasks. Unbounded, FIFO, blocking receive.
|
|
3
3
|
//
|
|
4
|
-
// The queue node's payload is
|
|
5
|
-
// (value, len)
|
|
6
|
-
// `send_string`/`receive_string` move fat strings
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// The queue node's payload is three words wide: every message carries a
|
|
5
|
+
// (value, len, kind) triple. `send`/`receive` move uint64 values (len and
|
|
6
|
+
// kind stay 0); `send_string`/`receive_string` move fat strings (kind = 1)
|
|
7
|
+
// without truncating the len half. `#destroy` frees only string payloads —
|
|
8
|
+
// a raw uint64 payload is a value, not a pointer. A typed Channel<T>
|
|
9
|
+
// wrapper can come later — the storage story is identical, only the
|
|
10
|
+
// call-site types change.
|
|
9
11
|
//
|
|
10
12
|
// String payloads are copied in and moved out: `send_string` duplicates the
|
|
11
13
|
// value into the node (the sender keeps its own string), and
|
|
@@ -25,17 +27,20 @@ pub class Channel: Sendable {
|
|
|
25
27
|
pub var uint64 not_empty_cv = 0
|
|
26
28
|
pub var uint64 head = 0
|
|
27
29
|
pub var uint64 tail = 0
|
|
30
|
+
// Head of the parked-fiber wait list (guarded by mu). Send wakes it.
|
|
31
|
+
pub var uint64 waiters = 0
|
|
28
32
|
|
|
29
33
|
pub func #init = (self) {
|
|
30
34
|
```
|
|
31
35
|
#arch: c
|
|
32
36
|
#scope: file
|
|
33
37
|
#include <pthread.h>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
struct nomen_channel_node {
|
|
39
|
+
unsigned long long value;
|
|
40
|
+
unsigned long long len;
|
|
41
|
+
unsigned long long kind; // 0 = uint64 payload, 1 = owned string
|
|
42
|
+
struct nomen_channel_node *next;
|
|
43
|
+
};
|
|
39
44
|
```
|
|
40
45
|
```
|
|
41
46
|
#arch: c
|
|
@@ -45,6 +50,7 @@ pub class Channel: Sendable {
|
|
|
45
50
|
pthread_cond_init((pthread_cond_t *)self->not_empty_cv, NULL);
|
|
46
51
|
self->head = 0;
|
|
47
52
|
self->tail = 0;
|
|
53
|
+
self->waiters = 0;
|
|
48
54
|
```
|
|
49
55
|
```
|
|
50
56
|
#arch: aarch64
|
|
@@ -60,6 +66,7 @@ pub class Channel: Sendable {
|
|
|
60
66
|
bl _pthread_cond_init
|
|
61
67
|
str xzr, [x19, #24]
|
|
62
68
|
str xzr, [x19, #32]
|
|
69
|
+
str xzr, [x19, #40]
|
|
63
70
|
```
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -69,6 +76,7 @@ pub class Channel: Sendable {
|
|
|
69
76
|
struct nomen_channel_node *n = (struct nomen_channel_node *)malloc(sizeof(struct nomen_channel_node));
|
|
70
77
|
n->value = value;
|
|
71
78
|
n->len = 0;
|
|
79
|
+
n->kind = 0;
|
|
72
80
|
n->next = NULL;
|
|
73
81
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
74
82
|
if (self->tail) {
|
|
@@ -78,25 +86,27 @@ pub class Channel: Sendable {
|
|
|
78
86
|
}
|
|
79
87
|
self->tail = (unsigned long long)n;
|
|
80
88
|
pthread_cond_signal((pthread_cond_t *)self->not_empty_cv);
|
|
89
|
+
__nomen_fiber_waitq_wake((struct nomen_fiber **)&self->waiters);
|
|
81
90
|
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
82
91
|
```
|
|
83
92
|
```
|
|
84
93
|
#arch: aarch64
|
|
85
94
|
// x19 = self (set by prologue), x1 = value
|
|
86
95
|
str x1, [sp, #-16]! // save value on stack
|
|
87
|
-
mov x0, #
|
|
96
|
+
mov x0, #32 // node = { value, len, kind, next }
|
|
88
97
|
bl _malloc // x0 = node
|
|
89
98
|
ldr x1, [sp], #16 // restore value
|
|
90
99
|
str x1, [x0, #0] // node->value = value
|
|
91
100
|
str xzr, [x0, #8] // node->len = 0
|
|
92
|
-
str xzr, [x0, #16] // node->
|
|
101
|
+
str xzr, [x0, #16] // node->kind = 0 (uint64 payload)
|
|
102
|
+
str xzr, [x0, #24] // node->next = NULL
|
|
93
103
|
str x0, [sp, #-16]! // save node on stack
|
|
94
104
|
ldr x0, [x19, #8] // self->mu
|
|
95
105
|
bl _pthread_mutex_lock
|
|
96
106
|
ldr x2, [sp], #16 // restore node
|
|
97
107
|
ldr x1, [x19, #32] // tail
|
|
98
108
|
cbz x1, .Lchannel_send_empty
|
|
99
|
-
str x2, [x1, #
|
|
109
|
+
str x2, [x1, #24] // tail->next = node
|
|
100
110
|
b .Lchannel_send_enqueued
|
|
101
111
|
.Lchannel_send_empty:
|
|
102
112
|
str x2, [x19, #24] // head = node
|
|
@@ -104,6 +114,8 @@ pub class Channel: Sendable {
|
|
|
104
114
|
str x2, [x19, #32] // tail = node
|
|
105
115
|
ldr x0, [x19, #16] // not_empty_cv
|
|
106
116
|
bl _pthread_cond_signal
|
|
117
|
+
add x0, x19, #40 // &self->waiters
|
|
118
|
+
bl ___nomen_fiber_waitq_wake
|
|
107
119
|
ldr x0, [x19, #8] // self->mu
|
|
108
120
|
bl _pthread_mutex_unlock
|
|
109
121
|
```
|
|
@@ -120,6 +132,7 @@ pub class Channel: Sendable {
|
|
|
120
132
|
nomen_string _v = value.ptr ? nomen_str_dup(value) : (nomen_string){0, 0};
|
|
121
133
|
n->value = (unsigned long long)_v.ptr;
|
|
122
134
|
n->len = (unsigned long long)_v.len;
|
|
135
|
+
n->kind = 1;
|
|
123
136
|
n->next = NULL;
|
|
124
137
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
125
138
|
if (self->tail) {
|
|
@@ -129,13 +142,14 @@ pub class Channel: Sendable {
|
|
|
129
142
|
}
|
|
130
143
|
self->tail = (unsigned long long)n;
|
|
131
144
|
pthread_cond_signal((pthread_cond_t *)self->not_empty_cv);
|
|
145
|
+
__nomen_fiber_waitq_wake((struct nomen_fiber **)&self->waiters);
|
|
132
146
|
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
133
147
|
```
|
|
134
148
|
```
|
|
135
149
|
#arch: aarch64
|
|
136
150
|
// x19 = self (set by prologue), x1 = value.ptr, x2 = value.len
|
|
137
151
|
stp x1, x2, [sp, #-32]! // [sp]=ptr [sp+8]=len [sp+16]=node scratch
|
|
138
|
-
mov x0, #
|
|
152
|
+
mov x0, #32 // node = { value, len, kind, next }
|
|
139
153
|
bl _malloc // x0 = node
|
|
140
154
|
str x0, [sp, #16] // save node across the strdup call
|
|
141
155
|
ldr x1, [sp, #0] // value.ptr
|
|
@@ -150,13 +164,15 @@ pub class Channel: Sendable {
|
|
|
150
164
|
str x0, [x2, #0] // node->value = copied ptr
|
|
151
165
|
ldr x1, [sp, #8] // value.len
|
|
152
166
|
str x1, [x2, #8] // node->len = len
|
|
153
|
-
|
|
167
|
+
mov x1, #1
|
|
168
|
+
str x1, [x2, #16] // node->kind = 1 (owned string payload)
|
|
169
|
+
str xzr, [x2, #24] // node->next = NULL
|
|
154
170
|
ldr x0, [x19, #8] // self->mu
|
|
155
171
|
bl _pthread_mutex_lock
|
|
156
172
|
ldr x2, [sp, #16] // restore node
|
|
157
173
|
ldr x1, [x19, #32] // tail
|
|
158
174
|
cbz x1, .Lchannel_send_str_empty
|
|
159
|
-
str x2, [x1, #
|
|
175
|
+
str x2, [x1, #24] // tail->next = node
|
|
160
176
|
b .Lchannel_send_str_enqueued
|
|
161
177
|
.Lchannel_send_str_empty:
|
|
162
178
|
str x2, [x19, #24] // head = node
|
|
@@ -175,7 +191,13 @@ pub class Channel: Sendable {
|
|
|
175
191
|
#arch: c
|
|
176
192
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
177
193
|
while (!self->head) {
|
|
178
|
-
|
|
194
|
+
// Fibers park instead of blocking; a cancelled fiber returns the
|
|
195
|
+
// zero value rather than waiting forever (cooperative cancel).
|
|
196
|
+
if (!__nomen_fiber_waitq_park(
|
|
197
|
+
(struct nomen_fiber **)&self->waiters, (void *)self->mu, (void *)self->not_empty_cv)) {
|
|
198
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
179
201
|
}
|
|
180
202
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
181
203
|
self->head = (unsigned long long)n->next;
|
|
@@ -193,13 +215,21 @@ pub class Channel: Sendable {
|
|
|
193
215
|
.Lchannel_receive_wait:
|
|
194
216
|
ldr x1, [x19, #24] // head
|
|
195
217
|
cbnz x1, .Lchannel_receive_got
|
|
196
|
-
|
|
218
|
+
add x0, x19, #40 // &self->waiters
|
|
197
219
|
ldr x1, [x19, #8] // mu
|
|
198
|
-
|
|
220
|
+
ldr x2, [x19, #16] // not_empty_cv
|
|
221
|
+
bl ___nomen_fiber_waitq_park
|
|
222
|
+
cbz x0, .Lchannel_receive_cancelled
|
|
199
223
|
b .Lchannel_receive_wait
|
|
224
|
+
.Lchannel_receive_cancelled:
|
|
225
|
+
// Cancelled while waiting: return the zero value.
|
|
226
|
+
ldr x0, [x19, #8] // self->mu
|
|
227
|
+
bl _pthread_mutex_unlock
|
|
228
|
+
mov x0, #0
|
|
229
|
+
b .Lchannel_receive_end
|
|
200
230
|
.Lchannel_receive_got:
|
|
201
231
|
// x1 = node
|
|
202
|
-
ldr x2, [x1, #
|
|
232
|
+
ldr x2, [x1, #24] // node->next
|
|
203
233
|
str x2, [x19, #24] // head = node->next
|
|
204
234
|
cbnz x2, .Lchannel_receive_not_empty
|
|
205
235
|
str xzr, [x19, #32] // tail = 0
|
|
@@ -211,6 +241,7 @@ pub class Channel: Sendable {
|
|
|
211
241
|
ldr x0, [x19, #8] // self->mu
|
|
212
242
|
bl _pthread_mutex_unlock
|
|
213
243
|
ldr x0, [sp], #16 // pop value → x0 (return)
|
|
244
|
+
.Lchannel_receive_end:
|
|
214
245
|
```
|
|
215
246
|
}
|
|
216
247
|
|
|
@@ -223,7 +254,11 @@ pub class Channel: Sendable {
|
|
|
223
254
|
#arch: c
|
|
224
255
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
225
256
|
while (!self->head) {
|
|
226
|
-
|
|
257
|
+
if (!__nomen_fiber_waitq_park(
|
|
258
|
+
(struct nomen_fiber **)&self->waiters, (void *)self->mu, (void *)self->not_empty_cv)) {
|
|
259
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
260
|
+
return (nomen_string){ 0, 0 };
|
|
261
|
+
}
|
|
227
262
|
}
|
|
228
263
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
229
264
|
self->head = (unsigned long long)n->next;
|
|
@@ -243,13 +278,23 @@ pub class Channel: Sendable {
|
|
|
243
278
|
.Lchannel_receive_str_wait:
|
|
244
279
|
ldr x1, [x19, #24] // head
|
|
245
280
|
cbnz x1, .Lchannel_receive_str_got
|
|
246
|
-
|
|
281
|
+
add x0, x19, #40 // &self->waiters
|
|
247
282
|
ldr x1, [x19, #8] // mu
|
|
248
|
-
|
|
283
|
+
ldr x2, [x19, #16] // not_empty_cv
|
|
284
|
+
bl ___nomen_fiber_waitq_park
|
|
285
|
+
cbz x0, .Lchannel_receive_str_cancelled
|
|
249
286
|
b .Lchannel_receive_str_wait
|
|
287
|
+
.Lchannel_receive_str_cancelled:
|
|
288
|
+
// Cancelled while waiting: return the empty fat string (x0 = ptr,
|
|
289
|
+
// x1 = len).
|
|
290
|
+
ldr x0, [x19, #8] // self->mu
|
|
291
|
+
bl _pthread_mutex_unlock
|
|
292
|
+
mov x0, #0
|
|
293
|
+
mov x1, #0
|
|
294
|
+
b .Lchannel_receive_str_end
|
|
250
295
|
.Lchannel_receive_str_got:
|
|
251
296
|
// x1 = node
|
|
252
|
-
ldr x2, [x1, #
|
|
297
|
+
ldr x2, [x1, #24] // node->next
|
|
253
298
|
str x2, [x19, #24] // head = node->next
|
|
254
299
|
cbnz x2, .Lchannel_receive_str_not_empty
|
|
255
300
|
str xzr, [x19, #32] // tail = 0
|
|
@@ -264,6 +309,7 @@ pub class Channel: Sendable {
|
|
|
264
309
|
ldr x0, [x19, #8] // self->mu
|
|
265
310
|
bl _pthread_mutex_unlock
|
|
266
311
|
ldp x0, x1, [sp], #16 // pop pair → x0/x1 (return)
|
|
312
|
+
.Lchannel_receive_str_end:
|
|
267
313
|
```
|
|
268
314
|
}
|
|
269
315
|
|
|
@@ -271,12 +317,13 @@ pub class Channel: Sendable {
|
|
|
271
317
|
```
|
|
272
318
|
#arch: c
|
|
273
319
|
if (self->mu) {
|
|
274
|
-
// Drain any pending nodes. A string payload that was
|
|
275
|
-
// received still owns its buffer — free it with the node.
|
|
320
|
+
// Drain any pending nodes. A string payload (kind = 1) that was
|
|
321
|
+
// never received still owns its buffer — free it with the node.
|
|
322
|
+
// A uint64 payload (kind = 0) is a raw value, never a pointer.
|
|
276
323
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
277
324
|
while (n) {
|
|
278
325
|
struct nomen_channel_node *next = n->next;
|
|
279
|
-
if (n->value) free((void *)n->value);
|
|
326
|
+
if (n->kind == 1 && n->value) free((void *)n->value);
|
|
280
327
|
free(n);
|
|
281
328
|
n = next;
|
|
282
329
|
}
|
|
@@ -295,14 +342,18 @@ pub class Channel: Sendable {
|
|
|
295
342
|
// x19 = self (set by prologue)
|
|
296
343
|
ldr x0, [x19, #8] // self->mu
|
|
297
344
|
cbz x0, .Lchannel_destroy_done
|
|
298
|
-
// Drain nodes (freeing
|
|
345
|
+
// Drain nodes (freeing only owned-string payloads; a uint64
|
|
346
|
+
// payload is a raw value, never a pointer)
|
|
299
347
|
ldr x1, [x19, #24] // head
|
|
300
348
|
.Lchannel_destroy_drain:
|
|
301
349
|
cbz x1, .Lchannel_destroy_drained
|
|
302
|
-
ldr x2, [x1, #
|
|
350
|
+
ldr x2, [x1, #24] // next (before calls clobber x1)
|
|
303
351
|
stp x1, x2, [sp, #-16]! // save node + next
|
|
352
|
+
ldr x3, [x1, #16] // payload kind (1 = owned string)
|
|
304
353
|
ldr x0, [x1, #0] // payload ptr
|
|
305
354
|
cbz x0, .Lchannel_destroy_no_payload
|
|
355
|
+
cmp x3, #1
|
|
356
|
+
b.ne .Lchannel_destroy_no_payload
|
|
306
357
|
bl _free
|
|
307
358
|
.Lchannel_destroy_no_payload:
|
|
308
359
|
ldr x0, [sp, #0] // node
|
|
@@ -8,27 +8,28 @@
|
|
|
8
8
|
* A flat buffer of owned class pointers of type T — the backing store for collections of classes
|
|
9
9
|
**/
|
|
10
10
|
pub struct ClassBuffer<T> {
|
|
11
|
-
var uint64 data = 0
|
|
12
|
-
|
|
11
|
+
internal var uint64 data = 0
|
|
12
|
+
// Publicly readable; grown only by ClassBuffer's own methods (`readonly`).
|
|
13
|
+
readonly cap = 0
|
|
13
14
|
|
|
14
|
-
func alloc_int = (ref self, int size, out int: out >= size) {
|
|
15
|
+
internal func alloc_int = (ref self, int size, out int: out >= size) {
|
|
15
16
|
self.cap = size
|
|
16
17
|
self.data = calloc(size, 8)
|
|
17
18
|
return self.cap
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
// Semantic parity with Buffer<T>.
|
|
21
|
-
// `self.values.
|
|
21
|
+
// Semantic parity with Buffer<T>.alloc: generic code (e.g. Map#init's
|
|
22
|
+
// `self.values.alloc(N)`) forwards to either Buffer — where alloc is
|
|
22
23
|
// T_SIZE-aware (string slots are 16 bytes) — or this ClassBuffer, whose
|
|
23
24
|
// slots are always one pointer wide. The compiler routes value-struct
|
|
24
25
|
// element types to Buffer and class/trait elements to ClassBuffer
|
|
25
26
|
// (monomorphize's field rewrite), so a call arriving HERE is always
|
|
26
27
|
// pointer-sized: delegate to alloc_int.
|
|
27
|
-
func
|
|
28
|
+
func alloc = (ref self, int size, out int: out >= size) {
|
|
28
29
|
return self.alloc_int(size)
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
32
|
+
internal func grow_int = (ref self, int needed, out int: out >= needed) {
|
|
32
33
|
if self.cap >= needed {
|
|
33
34
|
return self.cap
|
|
34
35
|
}
|
|
@@ -43,7 +44,7 @@ pub struct ClassBuffer<T> {
|
|
|
43
44
|
return self.cap
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
47
|
+
internal inline func load_int = (self, int i: i >= 0 && i < self.cap, out int) {
|
|
47
48
|
```
|
|
48
49
|
#arch: c
|
|
49
50
|
return ((long*)(unsigned long long)self->data)[i];
|
|
@@ -57,14 +58,14 @@ pub struct ClassBuffer<T> {
|
|
|
57
58
|
```
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
//
|
|
61
|
-
// 8-byte class pointers (T_SIZE is 8), so these are
|
|
62
|
-
// to their _int counterparts — they exist so
|
|
63
|
-
// uniform
|
|
64
|
-
// (ClassBuffer, after the Buffer<T> field is
|
|
65
|
-
// slots, matching _int.
|
|
61
|
+
// Generic (T-typed) counterparts of the _int primitives. ClassBuffer only
|
|
62
|
+
// ever holds 8-byte class pointers (T_SIZE is 8), so these are
|
|
63
|
+
// behaviorally identical to their _int counterparts — they exist so
|
|
64
|
+
// List<T> can call a single uniform API whether T is a value struct
|
|
65
|
+
// (Buffer) or a class (ClassBuffer, after the Buffer<T> field is
|
|
66
|
+
// rewritten). Bodies use 8-byte slots, matching _int.
|
|
66
67
|
|
|
67
|
-
func
|
|
68
|
+
func grow = (ref self, int needed, out int: out >= needed) {
|
|
68
69
|
if self.cap >= needed {
|
|
69
70
|
return self.cap
|
|
70
71
|
}
|
|
@@ -79,7 +80,7 @@ pub struct ClassBuffer<T> {
|
|
|
79
80
|
return self.cap
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
inline func
|
|
83
|
+
inline func load = (self, int i: i >= 0 && i < self.cap, out T) {
|
|
83
84
|
```
|
|
84
85
|
#arch: c
|
|
85
86
|
return (void*)((long*)(unsigned long long)self->data)[i];
|
|
@@ -94,11 +95,15 @@ pub struct ClassBuffer<T> {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
// Store the owned class pointer `val` into slot `i`, which MUST be fresh
|
|
97
|
-
// (calloc'd, or zeroed by
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
98
|
+
// (calloc'd, or zeroed by move/move_int). Takes OWNERSHIP of `val`
|
|
99
|
+
// (`move`): the slot's #destroy reclaims the instance, so the caller must
|
|
100
|
+
// not free its copy — a fresh constructor result transfers here directly,
|
|
101
|
+
// and a local must be moved (its own cleanup is suppressed at the call
|
|
102
|
+
// site). This is a plain pointer store — it does NOT destroy/free the
|
|
103
|
+
// slot's previous occupant, so a second `store` at an occupied index
|
|
104
|
+
// leaks that instance. Use replace (or replace_int) to overwrite a live
|
|
105
|
+
// slot.
|
|
106
|
+
internal inline func store = (ref self, int i: i >= 0 && i < self.cap, move T val: val != 0) {
|
|
102
107
|
```
|
|
103
108
|
#arch: c
|
|
104
109
|
((long*)(unsigned long long)self->data)[i] = (long)val;
|
|
@@ -116,11 +121,11 @@ pub struct ClassBuffer<T> {
|
|
|
116
121
|
// buffer no longer references it. Declared `move out T` so the borrow
|
|
117
122
|
// checker treats the result as OWNED — the slot is relinquished, so the
|
|
118
123
|
// caller is the sole remaining owner. This is the owning-extract primitive
|
|
119
|
-
// (parallel to List.pop); use it (not
|
|
124
|
+
// (parallel to List.pop); use it (not load) when transferring ownership
|
|
120
125
|
// out of a ClassBuffer slot, or moving a value into a fresh owning
|
|
121
|
-
// container —
|
|
126
|
+
// container — load returns a BORROW (the slot is unchanged) and storing
|
|
122
127
|
// it elsewhere would create shared ownership (double-free at destroy).
|
|
123
|
-
inline func
|
|
128
|
+
inline func move = (ref self, int i: i >= 0 && i < self.cap, move out T) {
|
|
124
129
|
```
|
|
125
130
|
#arch: c
|
|
126
131
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -138,7 +143,57 @@ pub struct ClassBuffer<T> {
|
|
|
138
143
|
```
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
|
|
146
|
+
// Apply `f` to slot `i` in place — the owning-counterpart of Buffer's
|
|
147
|
+
// modify for class elements. `f` receives the current instance pointer
|
|
148
|
+
// as a borrow and returns the replacement instance (fresh, or the same
|
|
149
|
+
// pointer); the primitive frees the displaced instance (destroy + free)
|
|
150
|
+
// unless `f` returned it. Storing the returned instance raw is sound:
|
|
151
|
+
// the slot takes sole ownership and `f`'s return is not retained
|
|
152
|
+
// elsewhere. NOT `inline`: the body calls through the fn pointer.
|
|
153
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func (T, out T) f) {
|
|
154
|
+
```
|
|
155
|
+
#arch: c
|
|
156
|
+
long *slots = (long*)(unsigned long long)self->data;
|
|
157
|
+
void *old = (void*)slots[i];
|
|
158
|
+
void *new_value = (void*)((void *(*)(void *, void *))f->code)(f->env, old);
|
|
159
|
+
slots[i] = (long)new_value;
|
|
160
|
+
if (old != new_value) {
|
|
161
|
+
T_destroy(old);
|
|
162
|
+
free(old);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
```
|
|
166
|
+
#arch: aarch64
|
|
167
|
+
// x19 = self, x1 = i, x2 = f (a closure descriptor — env goes in x0,
|
|
168
|
+
// the old slot value shifts to x1). Class slots are one pointer wide.
|
|
169
|
+
stp x20, x21, [sp, #-16]!
|
|
170
|
+
stp x22, x23, [sp, #-16]!
|
|
171
|
+
stp x24, x25, [sp, #-16]!
|
|
172
|
+
mov x22, x2
|
|
173
|
+
ldr x9, [x19, #8]
|
|
174
|
+
lsl x3, x1, #3
|
|
175
|
+
add x20, x9, x3
|
|
176
|
+
ldr x24, [x22]
|
|
177
|
+
ldr x0, [x22, #8]
|
|
178
|
+
ldr x1, [x20]
|
|
179
|
+
blr x24
|
|
180
|
+
mov x23, x0
|
|
181
|
+
ldr x9, [x20]
|
|
182
|
+
str x23, [x20]
|
|
183
|
+
cmp x9, x23
|
|
184
|
+
b.eq .Lcb_modify_T_done
|
|
185
|
+
mov x0, x9
|
|
186
|
+
bl T_destroy
|
|
187
|
+
mov x0, x9
|
|
188
|
+
bl _free
|
|
189
|
+
.Lcb_modify_T_done:
|
|
190
|
+
ldp x24, x25, [sp], #16
|
|
191
|
+
ldp x22, x23, [sp], #16
|
|
192
|
+
ldp x20, x21, [sp], #16
|
|
193
|
+
```
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
internal inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
142
197
|
```
|
|
143
198
|
#arch: c
|
|
144
199
|
((long*)(unsigned long long)self->data)[i] = val;
|
|
@@ -152,7 +207,7 @@ pub struct ClassBuffer<T> {
|
|
|
152
207
|
```
|
|
153
208
|
}
|
|
154
209
|
|
|
155
|
-
inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
210
|
+
internal inline func move_int = (ref self, int i: i >= 0 && i < self.cap, out int) {
|
|
156
211
|
```
|
|
157
212
|
#arch: c
|
|
158
213
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -172,7 +227,7 @@ pub struct ClassBuffer<T> {
|
|
|
172
227
|
|
|
173
228
|
// Store into a slot, freeing the previous value (it is an owned class
|
|
174
229
|
// pointer): run its destroy then free it before overwriting.
|
|
175
|
-
func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
230
|
+
internal func replace_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
|
|
176
231
|
```
|
|
177
232
|
#arch: c
|
|
178
233
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -204,9 +259,11 @@ pub struct ClassBuffer<T> {
|
|
|
204
259
|
}
|
|
205
260
|
|
|
206
261
|
// T-typed alias of replace_int: free the outgoing class pointer (destroy +
|
|
207
|
-
// free) then store the new one. Used by List.set so the uniform
|
|
262
|
+
// free) then store the new one. Used by List.set so the uniform API
|
|
208
263
|
// preserves ownership for class elements.
|
|
209
|
-
|
|
264
|
+
// Overwrite a live slot: destroy + free the displaced instance, then take
|
|
265
|
+
// OWNERSHIP of `val` (`move`) — same transfer rule as store.
|
|
266
|
+
func replace = (ref self, int i: i >= 0 && i < self.cap, move T val: val != 0) {
|
|
210
267
|
```
|
|
211
268
|
#arch: c
|
|
212
269
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -239,10 +296,10 @@ pub struct ClassBuffer<T> {
|
|
|
239
296
|
|
|
240
297
|
// Move slot src into slot dst: reclaim dst's previous instance (destroy +
|
|
241
298
|
// free), take over src's pointer, and zero src — exactly one slot owns the
|
|
242
|
-
// instance afterwards. The class-buffer counterpart of Buffer.
|
|
299
|
+
// instance afterwards. The class-buffer counterpart of Buffer.shift,
|
|
243
300
|
// used by Map/Set.remove's backward-shift so shifting entries neither
|
|
244
301
|
// duplicates nor orphans owned instances.
|
|
245
|
-
func
|
|
302
|
+
func shift = (ref self, int dst: dst >= 0 && dst < self.cap, int src: src >= 0 && src < self.cap) {
|
|
246
303
|
```
|
|
247
304
|
#arch: c
|
|
248
305
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -286,7 +343,7 @@ pub struct ClassBuffer<T> {
|
|
|
286
343
|
// pointers, returned as a `view T` (T is a class, so each element is an
|
|
287
344
|
// 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
|
|
288
345
|
// Buffer.slice; ClassBuffer stores 8-byte slots, so the element width is 8.
|
|
289
|
-
func slice = (self, int start: start >= 0, int end: end >= start, out view T) {
|
|
346
|
+
func slice = (self, int start: start >= 0, int end: end >= start && end <= self.cap, out view T) {
|
|
290
347
|
```
|
|
291
348
|
#arch: c
|
|
292
349
|
nomen_view _r;
|
package/core/System/Console.nm
CHANGED
|
@@ -5,7 +5,9 @@ pub struct Console {
|
|
|
5
5
|
pub func write = (string line) {
|
|
6
6
|
```
|
|
7
7
|
#arch: c
|
|
8
|
-
printf
|
|
8
|
+
// Fat-string ABI: strings pass their ptr half. printf %s stops at
|
|
9
|
+
// the first embedded NUL on both backends (known display limit).
|
|
10
|
+
printf("%s", line.ptr);
|
|
9
11
|
```
|
|
10
12
|
```
|
|
11
13
|
#arch: aarch64
|
|
@@ -25,7 +27,7 @@ pub struct Console {
|
|
|
25
27
|
pub func write_line = (string line) {
|
|
26
28
|
```
|
|
27
29
|
#arch: c
|
|
28
|
-
printf("%s\n", line);
|
|
30
|
+
printf("%s\n", line.ptr);
|
|
29
31
|
```
|
|
30
32
|
```
|
|
31
33
|
#arch: aarch64
|
|
@@ -17,7 +17,7 @@ pub class Button : Control {
|
|
|
17
17
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
18
18
|
btn, sel_registerName("setTitle:"),
|
|
19
19
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
20
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title));
|
|
20
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
|
|
21
21
|
id contentView = ((id(*)(id, SEL))objc_msgSend)(
|
|
22
22
|
(id)window->handle, sel_registerName("contentView"));
|
|
23
23
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
@@ -53,7 +53,7 @@ pub class Button : Control {
|
|
|
53
53
|
((void(*)(id, SEL, unsigned long, id, id))objc_msgSend)(
|
|
54
54
|
btn, sel_registerName("setTitle:forState:"),
|
|
55
55
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
56
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title),
|
|
56
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr),
|
|
57
57
|
0); // UIControlStateNormal = 0
|
|
58
58
|
id parentView = ((id(*)(id, SEL))objc_msgSend)(
|
|
59
59
|
((id(*)(id, SEL))objc_msgSend)(
|
|
@@ -123,7 +123,7 @@ pub class Button : Control {
|
|
|
123
123
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
124
124
|
(id)self->handle, sel_registerName("setTitle:"),
|
|
125
125
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
126
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title));
|
|
126
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
|
|
127
127
|
```
|
|
128
128
|
```
|
|
129
129
|
#arch: aarch64_use_c
|
|
@@ -139,7 +139,7 @@ pub class Button : Control {
|
|
|
139
139
|
((void(*)(id, SEL, unsigned long, id, id))objc_msgSend)(
|
|
140
140
|
(id)self->handle, sel_registerName("setTitle:forState:"),
|
|
141
141
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
142
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title),
|
|
142
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr),
|
|
143
143
|
0); // UIControlStateNormal = 0
|
|
144
144
|
```
|
|
145
145
|
```
|
|
@@ -109,7 +109,7 @@ pub class CheckBox : Control {
|
|
|
109
109
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
110
110
|
(id)self->handle, sel_registerName("setTitle:"),
|
|
111
111
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
112
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title));
|
|
112
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
|
|
113
113
|
```
|
|
114
114
|
```
|
|
115
115
|
#arch: aarch64_use_c
|
|
@@ -100,7 +100,7 @@ pub class Text: Control {
|
|
|
100
100
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
101
101
|
(id)self->handle, sel_registerName("setStringValue:"),
|
|
102
102
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
103
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text));
|
|
103
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text.ptr));
|
|
104
104
|
```
|
|
105
105
|
```
|
|
106
106
|
#arch: aarch64_use_c
|
|
@@ -116,7 +116,7 @@ pub class Text: Control {
|
|
|
116
116
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
117
117
|
(id)self->handle, sel_registerName("setText:"),
|
|
118
118
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
119
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text));
|
|
119
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text.ptr));
|
|
120
120
|
```
|
|
121
121
|
```
|
|
122
122
|
#arch: aarch64_use_c
|
|
@@ -133,7 +133,7 @@ pub class TextBox : Control {
|
|
|
133
133
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
134
134
|
(id)self->handle, sel_registerName("setStringValue:"),
|
|
135
135
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
136
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text));
|
|
136
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text.ptr));
|
|
137
137
|
```
|
|
138
138
|
```
|
|
139
139
|
#arch: aarch64_use_c
|
|
@@ -149,7 +149,7 @@ pub class TextBox : Control {
|
|
|
149
149
|
((void(*)(id, SEL, id))objc_msgSend)(
|
|
150
150
|
(id)self->handle, sel_registerName("setText:"),
|
|
151
151
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
152
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text));
|
|
152
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text.ptr));
|
|
153
153
|
```
|
|
154
154
|
```
|
|
155
155
|
#arch: aarch64_use_c
|
|
@@ -169,7 +169,7 @@ pub class TextBox : Control {
|
|
|
169
169
|
(id)self->handle, sel_registerName("stringValue"));
|
|
170
170
|
const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
|
|
171
171
|
str, sel_registerName("UTF8String"));
|
|
172
|
-
return strdup(cstr);
|
|
172
|
+
return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
|
|
173
173
|
```
|
|
174
174
|
```
|
|
175
175
|
#arch: aarch64_use_c
|
|
@@ -178,7 +178,7 @@ pub class TextBox : Control {
|
|
|
178
178
|
(id)self->handle, sel_registerName("stringValue"));
|
|
179
179
|
const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
|
|
180
180
|
str, sel_registerName("UTF8String"));
|
|
181
|
-
return strdup(cstr);
|
|
181
|
+
return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
|
|
182
182
|
```
|
|
183
183
|
```
|
|
184
184
|
#arch: c
|
|
@@ -187,7 +187,7 @@ pub class TextBox : Control {
|
|
|
187
187
|
(id)self->handle, sel_registerName("text"));
|
|
188
188
|
const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
|
|
189
189
|
str, sel_registerName("UTF8String"));
|
|
190
|
-
return strdup(cstr);
|
|
190
|
+
return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
|
|
191
191
|
```
|
|
192
192
|
```
|
|
193
193
|
#arch: aarch64_use_c
|
|
@@ -196,7 +196,7 @@ pub class TextBox : Control {
|
|
|
196
196
|
(id)self->handle, sel_registerName("text.ptr"));
|
|
197
197
|
const char *cstr = ((const char*(*)(id, SEL))objc_msgSend)(
|
|
198
198
|
str, sel_registerName("UTF8String"));
|
|
199
|
-
return strdup(cstr);
|
|
199
|
+
return (nomen_string){ strdup(cstr), (long)strlen(cstr) };
|
|
200
200
|
```
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -22,7 +22,7 @@ pub class Window: Control {
|
|
|
22
22
|
((id(*)(id, SEL, id))objc_msgSend)(
|
|
23
23
|
win, sel_registerName("setTitle:"),
|
|
24
24
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
25
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title));
|
|
25
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
|
|
26
26
|
((void(*)(id, SEL, double, double))objc_msgSend)(
|
|
27
27
|
win, sel_registerName("setFrameTopLeftPoint:"), 100.0, 100.0);
|
|
28
28
|
((void(*)(id, SEL, id))objc_msgSend)(win, sel_registerName("makeKeyAndOrderFront:"), (id)0);
|
|
@@ -115,7 +115,7 @@ pub class Window: Control {
|
|
|
115
115
|
((id(*)(id, SEL, id))objc_msgSend)(
|
|
116
116
|
(id)self->handle, sel_registerName("setTitle:"),
|
|
117
117
|
((id(*)(id, SEL, const char*))objc_msgSend)(
|
|
118
|
-
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title));
|
|
118
|
+
(id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title.ptr));
|
|
119
119
|
```
|
|
120
120
|
```
|
|
121
121
|
#arch: aarch64_use_c
|