nomen-lang 0.0.17 → 0.0.18
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/core/System/Array.nm +20 -0
- package/core/System/Buffer.nm +72 -0
- package/core/System/ClassBuffer.nm +45 -0
- package/core/System/List.nm +1 -1
- package/core/System/Map.nm +7 -2
- package/core/System/Set.nm +6 -1
- package/core/docs/System/Array.md +2 -0
- package/dist/index.mjs +1295 -68
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1777,6 +1777,11 @@ var AccessFunctionCallNode = class extends BaseNode {
|
|
|
1777
1777
|
* FunctionCallNode.nullable_param_indices.
|
|
1778
1778
|
*/
|
|
1779
1779
|
nullable_param_indices;
|
|
1780
|
+
/**
|
|
1781
|
+
* Indices of arguments whose corresponding callee parameter is a `view T`
|
|
1782
|
+
* (notably `view string`). See FunctionCallNode.view_param_indices.
|
|
1783
|
+
*/
|
|
1784
|
+
view_param_indices;
|
|
1780
1785
|
swap_params;
|
|
1781
1786
|
variadic_param_name;
|
|
1782
1787
|
variadic_param_index;
|
|
@@ -1828,6 +1833,12 @@ var AccessFunctionCallNode = class extends BaseNode {
|
|
|
1828
1833
|
* the programmer is asserting the array is long enough positionally.
|
|
1829
1834
|
*/
|
|
1830
1835
|
skip_bounds_check;
|
|
1836
|
+
/**
|
|
1837
|
+
* The concrete FunctionNode the checker resolved this method call to.
|
|
1838
|
+
* See FunctionCallNode.resolved_function — used by build-time passes for
|
|
1839
|
+
* callee-signature facts (notably hidden string-length companions).
|
|
1840
|
+
*/
|
|
1841
|
+
resolved_function;
|
|
1831
1842
|
constructor(start, name, type, params, is_static) {
|
|
1832
1843
|
super("access_func", start);
|
|
1833
1844
|
this.name = name;
|
|
@@ -1901,6 +1912,14 @@ var FunctionCallNode = class extends BaseNode {
|
|
|
1901
1912
|
* struct pointer — see ROADBLOCKS "Nullable structs".
|
|
1902
1913
|
*/
|
|
1903
1914
|
nullable_param_indices;
|
|
1915
|
+
/**
|
|
1916
|
+
* Indices of arguments whose corresponding callee parameter is a `view T`
|
|
1917
|
+
* (notably `view string`). Populated during the check pass when the
|
|
1918
|
+
* callee's signature is resolved. The build uses this to pass the (ptr,
|
|
1919
|
+
* len) pair at the call boundary: a view-typed argument passes through,
|
|
1920
|
+
* an owned `string` argument is wrapped with its strlen.
|
|
1921
|
+
*/
|
|
1922
|
+
view_param_indices;
|
|
1904
1923
|
swap_params;
|
|
1905
1924
|
variadic_param_name;
|
|
1906
1925
|
variadic_param_index;
|
|
@@ -1934,6 +1953,14 @@ var FunctionCallNode = class extends BaseNode {
|
|
|
1934
1953
|
* tag+payload temp), mirroring `Enum.case(args)` access calls.
|
|
1935
1954
|
*/
|
|
1936
1955
|
is_enum_shorthand;
|
|
1956
|
+
/**
|
|
1957
|
+
* The concrete FunctionNode the checker resolved this call to (plain
|
|
1958
|
+
* function, struct constructor, or monomorphized/specialized copy).
|
|
1959
|
+
* Build-time passes consult it for callee-signature facts that only the
|
|
1960
|
+
* checker can resolve robustly (overloads, monos) — notably the hidden
|
|
1961
|
+
* string-length companion params (`ParameterNode.hidden_len`).
|
|
1962
|
+
*/
|
|
1963
|
+
resolved_function;
|
|
1937
1964
|
constructor(start, name, type, params, is_static) {
|
|
1938
1965
|
super("func_call", start);
|
|
1939
1966
|
this.name = name;
|
|
@@ -2055,6 +2082,29 @@ function type_from_value_node$1(node) {
|
|
|
2055
2082
|
return new Type("");
|
|
2056
2083
|
}
|
|
2057
2084
|
//#endregion
|
|
2085
|
+
//#region ../src/build_common/hidden_len.ts
|
|
2086
|
+
/**
|
|
2087
|
+
* Indices of a call's DECLARED arguments whose callee parameter carries the
|
|
2088
|
+
* hidden string-length companion (`ParameterNode.hidden_len`, stamped by the
|
|
2089
|
+
* post-check `stamp_hidden_string_lens` pass). The backends append one
|
|
2090
|
+
* companion argument right after each such argument — `long _<name>_len` on
|
|
2091
|
+
* C, the next AAPCS slot on aarch64 — and the hoist scan treats these args
|
|
2092
|
+
* like `.length` reads so a loop-invariant strlen temp covers them.
|
|
2093
|
+
*
|
|
2094
|
+
* Resolution comes from the checker's `resolved_function` stamp (exact for
|
|
2095
|
+
* overloads and monomorphized copies); an unstamped call site (synthesized
|
|
2096
|
+
* nodes, indirect func-value calls) yields no companions, which matches the
|
|
2097
|
+
* qualification pass — those callees are never stamped `hidden_len`.
|
|
2098
|
+
*/
|
|
2099
|
+
function callee_hidden_len_indices(call) {
|
|
2100
|
+
const func = call.resolved_function;
|
|
2101
|
+
if (!func) return [];
|
|
2102
|
+
const self_offset = func.params[0]?.is_self_param ? 1 : 0;
|
|
2103
|
+
const out = [];
|
|
2104
|
+
for (let i = 0; i < call.params.length; i++) if (func.params[i + self_offset]?.hidden_len) out.push(i);
|
|
2105
|
+
return out;
|
|
2106
|
+
}
|
|
2107
|
+
//#endregion
|
|
2058
2108
|
//#region ../src/build_common/mono_name.ts
|
|
2059
2109
|
function mono_type_name(type_or_name, type_args) {
|
|
2060
2110
|
const name = typeof type_or_name === "string" ? type_or_name : type_or_name.name;
|
|
@@ -2153,6 +2203,27 @@ var ParameterNode = class extends BaseNode {
|
|
|
2153
2203
|
is_variadic_tuple;
|
|
2154
2204
|
func_params;
|
|
2155
2205
|
func_return_type;
|
|
2206
|
+
/**
|
|
2207
|
+
* Parallel-length equalities (`a.length == b.length` conjuncts) stripped
|
|
2208
|
+
* from `constraint` at signature-registration time — before any call site
|
|
2209
|
+
* can observe this function, so declaration order never matters (a
|
|
2210
|
+
* forward-referenced callee must not ask its caller to prove a clause no
|
|
2211
|
+
* call site could). The callee's own param check seeds
|
|
2212
|
+
* `status.equal_lengths` from these so its body still gets the assumption.
|
|
2213
|
+
*/
|
|
2214
|
+
stripped_length_equalities;
|
|
2215
|
+
/**
|
|
2216
|
+
* True for a by-value `string` param whose function body reads
|
|
2217
|
+
* `<name>.length` (and never rebinds/mutates/escapes it) — stamped by the
|
|
2218
|
+
* post-check pass `stamp_hidden_string_lens`. The backends lower it as TWO
|
|
2219
|
+
* parameters: the `char*` itself plus a trailing companion length
|
|
2220
|
+
* (`long _<name>_len` on C; the next AAPCS slot on aarch64), so the body's
|
|
2221
|
+
* `.length` reads are register/stack loads instead of a per-call `strlen`.
|
|
2222
|
+
* Call sites append the companion argument (a hoisted loop-invariant
|
|
2223
|
+
* strlen temp when one exists — see scan_string_length_hoists). Mirrors
|
|
2224
|
+
* the nullable `_has` companion convention.
|
|
2225
|
+
*/
|
|
2226
|
+
hidden_len;
|
|
2156
2227
|
constructor(start, name, type, default_value, is_self_param, declaration) {
|
|
2157
2228
|
super("param", start);
|
|
2158
2229
|
this.name = name;
|
|
@@ -2217,6 +2288,12 @@ var FunctionNode = class extends BaseNode {
|
|
|
2217
2288
|
type_params = [];
|
|
2218
2289
|
scope;
|
|
2219
2290
|
/**
|
|
2291
|
+
* Set once the post-check `stamp_hidden_string_lens` pass has considered
|
|
2292
|
+
* this function — guards against double-stamping when a check pass runs
|
|
2293
|
+
* twice over a shared tree.
|
|
2294
|
+
*/
|
|
2295
|
+
hidden_len_stamped;
|
|
2296
|
+
/**
|
|
2220
2297
|
* Optional contract on the return value, parsed from `out TYPE: constraint`.
|
|
2221
2298
|
* The placeholder `out` refers to the return value; other identifiers refer
|
|
2222
2299
|
* to the function's parameters. Used by check_function_call to propagate
|
|
@@ -4396,6 +4473,11 @@ function build_function_node$1(node, status) {
|
|
|
4396
4473
|
first_pass_slot += 1;
|
|
4397
4474
|
continue;
|
|
4398
4475
|
}
|
|
4476
|
+
if (param.type.is_view) {
|
|
4477
|
+
first_pass_slot += 2;
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
if (param.hidden_len) first_pass_slot++;
|
|
4399
4481
|
const is_struct_type = !!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type);
|
|
4400
4482
|
const is_trait_type = !!status.traits.find((t) => t.name === param.type.name);
|
|
4401
4483
|
const is_enum_with_data = !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data);
|
|
@@ -4433,7 +4515,11 @@ function build_function_node$1(node, status) {
|
|
|
4433
4515
|
status.ref_class_slots = /* @__PURE__ */ new Map();
|
|
4434
4516
|
status.function_struct_param_slots = /* @__PURE__ */ new Set();
|
|
4435
4517
|
const old_variadic_params_aarch64 = status.function_variadic_params;
|
|
4518
|
+
const old_view_params = status.function_view_params;
|
|
4436
4519
|
status.function_variadic_params = /* @__PURE__ */ new Set();
|
|
4520
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
4521
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
4522
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
4437
4523
|
status.moved_class_params = /* @__PURE__ */ new Map();
|
|
4438
4524
|
let moved_param_save_slots = /* @__PURE__ */ new Map();
|
|
4439
4525
|
if (has_body) {
|
|
@@ -4454,6 +4540,22 @@ function build_function_node$1(node, status) {
|
|
|
4454
4540
|
}
|
|
4455
4541
|
param_idx++;
|
|
4456
4542
|
}
|
|
4543
|
+
if (param.type.is_view) {
|
|
4544
|
+
const offset = allocate_stack_space(status, 16, 16);
|
|
4545
|
+
status.stack_offsets.set(param.name, offset);
|
|
4546
|
+
status.function_view_params.add(param.name);
|
|
4547
|
+
for (const half of [0, 1]) {
|
|
4548
|
+
const p_slot = param_idx + half;
|
|
4549
|
+
if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
|
|
4550
|
+
else {
|
|
4551
|
+
const k = p_slot - 8;
|
|
4552
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
|
|
4553
|
+
status.code += `str x9, [x29, #${offset + half * 8}]\n`;
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
param_idx += 2;
|
|
4557
|
+
continue;
|
|
4558
|
+
}
|
|
4457
4559
|
if (callee_map.has(param.name)) {
|
|
4458
4560
|
const reg = callee_map.get(param.name);
|
|
4459
4561
|
status.function_param_regs.set(param.name, reg);
|
|
@@ -4487,6 +4589,19 @@ function build_function_node$1(node, status) {
|
|
|
4487
4589
|
}
|
|
4488
4590
|
}
|
|
4489
4591
|
param_idx++;
|
|
4592
|
+
if (param.hidden_len) {
|
|
4593
|
+
const len_offset = allocate_stack_space(status, 8, 8);
|
|
4594
|
+
status.stack_offsets.set(`_${param.name}_len`, len_offset);
|
|
4595
|
+
if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
4596
|
+
status.hidden_len_params.add(param.name);
|
|
4597
|
+
if (param_idx < 8) status.code += `str ${param_regs[param_idx]}, [x29, #${len_offset}]\n`;
|
|
4598
|
+
else {
|
|
4599
|
+
const k = param_idx - 8;
|
|
4600
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
|
|
4601
|
+
status.code += `str x9, [x29, #${len_offset}]\n`;
|
|
4602
|
+
}
|
|
4603
|
+
param_idx++;
|
|
4604
|
+
}
|
|
4490
4605
|
if (param.declaration === "var") status.function_param_vars.add(param.name);
|
|
4491
4606
|
if (param.type.is_array) {
|
|
4492
4607
|
if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
|
|
@@ -4502,6 +4617,7 @@ function build_function_node$1(node, status) {
|
|
|
4502
4617
|
for (let i = 0; i < node.params.length; i++) {
|
|
4503
4618
|
const param = node.params[i];
|
|
4504
4619
|
if (param.is_variadic) pidx++;
|
|
4620
|
+
if (param.hidden_len) pidx++;
|
|
4505
4621
|
if (param.is_moved) {
|
|
4506
4622
|
if (!!status.structs.find((s) => s.name === param.type.name && s.is_class)) {
|
|
4507
4623
|
const reg = callee_map.get(param.name) ?? param_regs[pidx];
|
|
@@ -4627,6 +4743,8 @@ function build_function_node$1(node, status) {
|
|
|
4627
4743
|
status.ref_class_slots = old_ref_class_slots;
|
|
4628
4744
|
status.function_struct_param_slots = old_struct_param_slots;
|
|
4629
4745
|
status.function_variadic_params = old_variadic_params_aarch64;
|
|
4746
|
+
status.function_view_params = old_view_params;
|
|
4747
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
4630
4748
|
status.function_return_label = old_return_label;
|
|
4631
4749
|
status.struct_return_buffer = void 0;
|
|
4632
4750
|
status.return_buffer_stack_offset = void 0;
|
|
@@ -6314,6 +6432,108 @@ function build_swap_params(node, status) {
|
|
|
6314
6432
|
}
|
|
6315
6433
|
}
|
|
6316
6434
|
//#endregion
|
|
6435
|
+
//#region ../src/build_aarch64/utils/view_value.ts
|
|
6436
|
+
/**
|
|
6437
|
+
* Resolve the struct name of an expression's value by walking the access
|
|
6438
|
+
* chain through the (monomorphized) struct table — bare names via
|
|
6439
|
+
* variable_types / scoped declarations / `self`, field accesses via the
|
|
6440
|
+
* base struct's field type, method calls via the method's return type.
|
|
6441
|
+
* Fallback for cached node types that lost their modifiers (generic bodies
|
|
6442
|
+
* re-checked after monomorphization can carry a stale plain `string`).
|
|
6443
|
+
*/
|
|
6444
|
+
function value_struct_name$1(node, status) {
|
|
6445
|
+
if (node.node_type === "value") {
|
|
6446
|
+
const name = node.value;
|
|
6447
|
+
if (name === "self" && status.current_struct) return status.current_struct.name;
|
|
6448
|
+
const vt = status.variable_types?.get(name);
|
|
6449
|
+
if (vt?.name) return vt.name;
|
|
6450
|
+
const decl = status.scoped_declarations.findLast((d) => d.name === name);
|
|
6451
|
+
if (decl?.type?.name) return decl.type.name;
|
|
6452
|
+
return;
|
|
6453
|
+
}
|
|
6454
|
+
if (node.node_type === "access") {
|
|
6455
|
+
const inner = node.access;
|
|
6456
|
+
const base = value_struct_name$1(node.target, status);
|
|
6457
|
+
if (!base) return void 0;
|
|
6458
|
+
const struct = status.structs.find((s) => s.name === base && !s.is_generic);
|
|
6459
|
+
if (!struct) return void 0;
|
|
6460
|
+
if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
|
|
6461
|
+
if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
/**
|
|
6465
|
+
* Whether an expression is a `view T` VALUE — a (ptr, len) pair that can be
|
|
6466
|
+
* passed to a `view T` parameter unchanged (loaded from the value's two
|
|
6467
|
+
* stack slots, or produced directly by a view-returning call). Recovers
|
|
6468
|
+
* `is_view` from the cached node type, the declared variable type, the
|
|
6469
|
+
* current function's view params, or (for method calls whose cached type
|
|
6470
|
+
* lost the modifier) the callee's declared return type.
|
|
6471
|
+
*/
|
|
6472
|
+
function is_view_value$1(node, status) {
|
|
6473
|
+
if (type_from_value_node$1(node)?.is_view) return true;
|
|
6474
|
+
if (node.node_type === "value") {
|
|
6475
|
+
const name = node.value;
|
|
6476
|
+
if (status.variable_types?.get(name)?.is_view) return true;
|
|
6477
|
+
if (status.function_view_params?.has(name)) return true;
|
|
6478
|
+
}
|
|
6479
|
+
if (node.node_type === "access" && node.access.node_type === "access_func") {
|
|
6480
|
+
const access = node.access;
|
|
6481
|
+
const recv_struct = value_struct_name$1(node.target, status);
|
|
6482
|
+
if ((status.structs.find((s) => s.name === recv_struct && !s.is_generic)?.functions.find((f) => f.name === access.name || f.name === `#${access.name}`))?.return_type?.is_view) return true;
|
|
6483
|
+
}
|
|
6484
|
+
return false;
|
|
6485
|
+
}
|
|
6486
|
+
/**
|
|
6487
|
+
* Emit the (ptr, len) pair for a `view string` argument into x0/x1.
|
|
6488
|
+
* - A view VALUE (a view local/param's two stack slots, or a view-returning
|
|
6489
|
+
* call that leaves the pair in x0/x1) passes through unchanged.
|
|
6490
|
+
* - An owned string expression builds its pointer into x0, then measures it
|
|
6491
|
+
* with strlen — an implicit owned→view borrow; ownership stays with the
|
|
6492
|
+
* caller.
|
|
6493
|
+
*/
|
|
6494
|
+
function emit_view_string_arg(arg, status) {
|
|
6495
|
+
if (is_view_value$1(arg, status)) {
|
|
6496
|
+
if (arg.node_type === "value") {
|
|
6497
|
+
const base = status.stack_offsets?.get(arg.value);
|
|
6498
|
+
if (base !== void 0) {
|
|
6499
|
+
status.code += `ldr x0, [x29, #${base}]\n`;
|
|
6500
|
+
status.code += `ldr x1, [x29, #${base + 8}]\n`;
|
|
6501
|
+
return;
|
|
6502
|
+
}
|
|
6503
|
+
}
|
|
6504
|
+
build_node$1(arg, status);
|
|
6505
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6506
|
+
return;
|
|
6507
|
+
}
|
|
6508
|
+
build_node$1(arg, status);
|
|
6509
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6510
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
6511
|
+
status.code += `bl _strlen\n`;
|
|
6512
|
+
status.code += `mov x1, x0\n`;
|
|
6513
|
+
status.code += `ldr x0, [sp], #16\n`;
|
|
6514
|
+
}
|
|
6515
|
+
/**
|
|
6516
|
+
* Materialize a `view string` into an OWNED heap string. The (ptr, len)
|
|
6517
|
+
* pair must be in x0/x1 on entry; on exit x0 holds a malloc'd, null-
|
|
6518
|
+
* terminated copy of exactly len bytes (and the caller must reclaim it —
|
|
6519
|
+
* set last_result_is_heap / mark the target as a heap string). Mirrors the
|
|
6520
|
+
* view `.to_string` builtin sequence.
|
|
6521
|
+
*/
|
|
6522
|
+
function emit_view_materialize_owned(status) {
|
|
6523
|
+
status.code += `stp x0, x1, [sp, #-16]!\n`;
|
|
6524
|
+
status.code += `add x0, x1, #1\n`;
|
|
6525
|
+
emit_malloc(status);
|
|
6526
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
6527
|
+
status.code += `ldr x1, [sp, #16]\n`;
|
|
6528
|
+
status.code += `ldr x2, [sp, #24]\n`;
|
|
6529
|
+
status.code += `ldr x0, [sp]\n`;
|
|
6530
|
+
status.code += `bl _memcpy\n`;
|
|
6531
|
+
status.code += `ldr x0, [sp]\n`;
|
|
6532
|
+
status.code += `ldr x1, [sp, #24]\n`;
|
|
6533
|
+
status.code += `strb wzr, [x0, x1]\n`;
|
|
6534
|
+
status.code += `add sp, sp, #32\n`;
|
|
6535
|
+
}
|
|
6536
|
+
//#endregion
|
|
6317
6537
|
//#region ../src/build_aarch64/build_declaration_node.ts
|
|
6318
6538
|
/**
|
|
6319
6539
|
* Pass a struct-typed param by address. For ValueNode (variable references)
|
|
@@ -6476,10 +6696,13 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
6476
6696
|
const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
|
|
6477
6697
|
const elem_size = elem_struct ? get_struct_size(elem_type_name, status) : 8;
|
|
6478
6698
|
const count = arr.values.length;
|
|
6479
|
-
const arr_offset = allocate_stack_space(status, Math.max(count, 1) * elem_size, 16);
|
|
6699
|
+
const arr_offset = allocate_stack_space(status, 8 + Math.max(count, 1) * elem_size, 16);
|
|
6700
|
+
const data_base = arr_offset + 8;
|
|
6701
|
+
status.code += `mov x0, #${count}\n`;
|
|
6702
|
+
status.code += `str x0, [x29, #${arr_offset}]\n`;
|
|
6480
6703
|
for (let j = count - 1; j >= 0; j--) {
|
|
6481
6704
|
const arg = arr.values[j];
|
|
6482
|
-
const slot_offset =
|
|
6705
|
+
const slot_offset = data_base + j * elem_size;
|
|
6483
6706
|
if (elem_struct && arg.node_type === "func_call") {
|
|
6484
6707
|
const tfc = arg;
|
|
6485
6708
|
const fc_param_regs = [
|
|
@@ -6539,7 +6762,7 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
6539
6762
|
emit_int_immediate(status, String(count));
|
|
6540
6763
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6541
6764
|
status.code += `str x0, [x29, #${count_slot}]\n`;
|
|
6542
|
-
status.code += `add x0, x29, #${
|
|
6765
|
+
status.code += `add x0, x29, #${data_base}\n`;
|
|
6543
6766
|
status.code += `str x0, [x29, #${ptr_slot}]\n`;
|
|
6544
6767
|
const reg_slots = Math.min(total_slots, 7);
|
|
6545
6768
|
for (let i = 0; i < reg_slots; i++) status.code += `ldr ${param_regs[i]}, [x29, #${slots_base + i * 8}]\n`;
|
|
@@ -7414,10 +7637,18 @@ function build_declaration_node$1(node, status) {
|
|
|
7414
7637
|
const value_node = node.value;
|
|
7415
7638
|
const raw = get_raw_value$1(value_node);
|
|
7416
7639
|
const is_literal = /^(\+|-)?\d+(\.\d+)?$/.test(raw) || raw.startsWith("\"") || raw === "true" || raw === "false";
|
|
7417
|
-
|
|
7640
|
+
const use_stack = !!status.function_return_label;
|
|
7641
|
+
const is_heap_alias = node.type.name === "string" && !is_literal && status.heap_strings?.has(raw);
|
|
7642
|
+
if (use_stack) {
|
|
7418
7643
|
const offset = allocate_stack_space(status, size, size);
|
|
7419
7644
|
status.stack_offsets.set(node.name, offset);
|
|
7420
|
-
if (node.type.name === "string" && !
|
|
7645
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
|
|
7646
|
+
emit_view_string_arg(node.value, status);
|
|
7647
|
+
emit_view_materialize_owned(status);
|
|
7648
|
+
status.code += `str x0, [x29, #${offset}]\n`;
|
|
7649
|
+
mark_heap_string(status, node.name);
|
|
7650
|
+
status.last_result_is_heap = false;
|
|
7651
|
+
} else if (is_heap_alias) {
|
|
7421
7652
|
emit_var_load(status, "x0", raw, 8);
|
|
7422
7653
|
emit_strdup(status);
|
|
7423
7654
|
status.code += `str x0, [x29, #${offset}]\n`;
|
|
@@ -7570,6 +7801,14 @@ function build_declaration_node$1(node, status) {
|
|
|
7570
7801
|
const offset = allocate_stack_space(status, size, size);
|
|
7571
7802
|
status.stack_offsets.set(node.name, offset);
|
|
7572
7803
|
} else emit_data(status, `${node.name}: .space ${size}\n`);
|
|
7804
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
|
|
7805
|
+
emit_view_string_arg(node.value, status);
|
|
7806
|
+
emit_view_materialize_owned(status);
|
|
7807
|
+
emit_var_store(status, "x0", node.name, size);
|
|
7808
|
+
mark_heap_string(status, node.name);
|
|
7809
|
+
status.last_result_is_heap = false;
|
|
7810
|
+
return;
|
|
7811
|
+
}
|
|
7573
7812
|
build_node$1(node.value, status);
|
|
7574
7813
|
emit_var_store(status, "x0", node.name, size);
|
|
7575
7814
|
check_heap();
|
|
@@ -8292,10 +8531,13 @@ function build_function_call_node$1(node, status) {
|
|
|
8292
8531
|
const elem_type_name = arr.type.name || "int";
|
|
8293
8532
|
const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
|
|
8294
8533
|
const elem_size = elem_struct ? get_struct_size(elem_type_name, status) : 8;
|
|
8295
|
-
const arr_offset = allocate_stack_space(status, arr.values.length * elem_size, 16);
|
|
8534
|
+
const arr_offset = allocate_stack_space(status, 8 + Math.max(arr.values.length, 1) * elem_size, 16);
|
|
8535
|
+
const data_base = arr_offset + 8;
|
|
8536
|
+
status.code += `mov x0, #${arr.values.length}\n`;
|
|
8537
|
+
status.code += `str x0, [x29, #${arr_offset}]\n`;
|
|
8296
8538
|
for (let j = arr.values.length - 1; j >= 0; j--) {
|
|
8297
8539
|
const arg = arr.values[j];
|
|
8298
|
-
const slot_offset =
|
|
8540
|
+
const slot_offset = data_base + j * elem_size;
|
|
8299
8541
|
if (elem_struct && arg.node_type === "func_call") {
|
|
8300
8542
|
const fc = arg;
|
|
8301
8543
|
const fc_param_regs = [
|
|
@@ -8338,16 +8580,29 @@ function build_function_call_node$1(node, status) {
|
|
|
8338
8580
|
non_variadic.push({
|
|
8339
8581
|
node: param,
|
|
8340
8582
|
is_struct: is_struct_type$2(param_type, status) || is_enum_with_data_type$1(param_type, status),
|
|
8341
|
-
is_ref: !!is_ref_param
|
|
8583
|
+
is_ref: !!is_ref_param,
|
|
8584
|
+
is_view: !!node.view_param_indices?.includes(i)
|
|
8342
8585
|
});
|
|
8343
8586
|
}
|
|
8344
8587
|
const nv = non_variadic.length;
|
|
8345
|
-
const
|
|
8588
|
+
const nv_slot = [];
|
|
8589
|
+
let nv_total = 0;
|
|
8590
|
+
for (let i = 0; i < nv; i++) {
|
|
8591
|
+
nv_slot.push(nv_total);
|
|
8592
|
+
nv_total += non_variadic[i].is_view ? 2 : 1;
|
|
8593
|
+
}
|
|
8594
|
+
const total_slots = nv_total + 2;
|
|
8346
8595
|
const slots_base = allocate_stack_space(status, total_slots * 8, 16);
|
|
8347
|
-
const count_slot = slots_base +
|
|
8596
|
+
const count_slot = slots_base + nv_total * 8;
|
|
8348
8597
|
const ptr_slot = count_slot + 8;
|
|
8349
8598
|
for (let i = nv - 1; i >= 0; i--) {
|
|
8350
8599
|
const ep = non_variadic[i];
|
|
8600
|
+
if (ep.is_view) {
|
|
8601
|
+
emit_view_string_arg(ep.node, status);
|
|
8602
|
+
status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
|
|
8603
|
+
status.code += `str x1, [x29, #${slots_base + (nv_slot[i] + 1) * 8}]\n`;
|
|
8604
|
+
continue;
|
|
8605
|
+
}
|
|
8351
8606
|
if (ep.is_struct) emit_struct_address(ep.node, status);
|
|
8352
8607
|
else if (ep.is_ref) {
|
|
8353
8608
|
const ep_name = ep.node.node_type === "value" ? ep.node.value : void 0;
|
|
@@ -8356,11 +8611,11 @@ function build_function_call_node$1(node, status) {
|
|
|
8356
8611
|
else emit_address_of(ep.node, status);
|
|
8357
8612
|
} else build_node$1(ep.node, status);
|
|
8358
8613
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
8359
|
-
status.code += `str x0, [x29, #${slots_base + i * 8}]\n`;
|
|
8614
|
+
status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
|
|
8360
8615
|
}
|
|
8361
8616
|
status.code += `mov x0, #${arr.values.length}\n`;
|
|
8362
8617
|
status.code += `str x0, [x29, #${count_slot}]\n`;
|
|
8363
|
-
status.code += `add x0, x29, #${
|
|
8618
|
+
status.code += `add x0, x29, #${data_base}\n`;
|
|
8364
8619
|
status.code += `str x0, [x29, #${ptr_slot}]\n`;
|
|
8365
8620
|
for (let s = 0; s < total_slots; s++) {
|
|
8366
8621
|
const slot = start_reg + s;
|
|
@@ -8381,13 +8636,33 @@ function build_function_call_node$1(node, status) {
|
|
|
8381
8636
|
}
|
|
8382
8637
|
}
|
|
8383
8638
|
} else {
|
|
8384
|
-
const
|
|
8639
|
+
const view_arg_set = new Set(node.view_param_indices ?? []);
|
|
8640
|
+
const hidden_len_indices = callee_hidden_len_indices(node);
|
|
8641
|
+
const hidden_len_set = new Set(hidden_len_indices);
|
|
8642
|
+
const hidden_len_slots = /* @__PURE__ */ new Map();
|
|
8643
|
+
const arg_slot = [];
|
|
8644
|
+
let total_slots = 0;
|
|
8645
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
8646
|
+
arg_slot.push(total_slots);
|
|
8647
|
+
total_slots += view_arg_set.has(i) ? 2 : 1;
|
|
8648
|
+
if (hidden_len_set.has(i)) {
|
|
8649
|
+
hidden_len_slots.set(i, total_slots);
|
|
8650
|
+
total_slots += 1;
|
|
8651
|
+
}
|
|
8652
|
+
}
|
|
8653
|
+
const has_args = total_slots > 0;
|
|
8385
8654
|
let args_base = 0;
|
|
8386
|
-
if (has_args) args_base = allocate_stack_space(status,
|
|
8655
|
+
if (has_args) args_base = allocate_stack_space(status, total_slots * 8, 16);
|
|
8387
8656
|
for (let i = node.params.length - 1; i >= 0; i--) {
|
|
8388
8657
|
const param = node.params[i];
|
|
8389
8658
|
const param_type = param.type?.name || "";
|
|
8390
8659
|
const is_ref_param = node.ref_param_indices?.includes(i);
|
|
8660
|
+
if (view_arg_set.has(i)) {
|
|
8661
|
+
emit_view_string_arg(param, status);
|
|
8662
|
+
status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8663
|
+
status.code += `str x1, [x29, #${args_base + (arg_slot[i] + 1) * 8}]\n`;
|
|
8664
|
+
continue;
|
|
8665
|
+
}
|
|
8391
8666
|
const arr_arg_name = param.node_type === "value" ? param.value : void 0;
|
|
8392
8667
|
if (!!arr_arg_name && !!status.heap_array_vars?.has(arr_arg_name)) build_node$1(param, status);
|
|
8393
8668
|
else if (param.node_type === "array" && param_type) {
|
|
@@ -8445,19 +8720,35 @@ function build_function_call_node$1(node, status) {
|
|
|
8445
8720
|
} else if (is_struct_type$2(param_type, status) || is_enum_with_data_type$1(param_type, status)) emit_struct_address(node.params[i], status);
|
|
8446
8721
|
else build_node$1(node.params[i], status);
|
|
8447
8722
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
8448
|
-
status.code += `str x0, [x29, #${args_base + i * 8}]\n`;
|
|
8723
|
+
status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8724
|
+
}
|
|
8725
|
+
for (const i of hidden_len_indices) {
|
|
8726
|
+
const arg = node.params[i];
|
|
8727
|
+
let computed = false;
|
|
8728
|
+
if (arg.node_type === "value") {
|
|
8729
|
+
const hoisted = status.string_length_slots?.get(arg.value);
|
|
8730
|
+
if (hoisted !== void 0) {
|
|
8731
|
+
status.code += `ldr x0, [x29, #${hoisted}]\n`;
|
|
8732
|
+
computed = true;
|
|
8733
|
+
}
|
|
8734
|
+
}
|
|
8735
|
+
if (!computed) {
|
|
8736
|
+
status.code += `ldr x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8737
|
+
status.code += `bl _strlen\n`;
|
|
8738
|
+
}
|
|
8739
|
+
status.code += `str x0, [x29, #${args_base + hidden_len_slots.get(i) * 8}]\n`;
|
|
8449
8740
|
}
|
|
8450
8741
|
if (has_args) {
|
|
8451
|
-
for (let
|
|
8452
|
-
const slot = start_reg +
|
|
8742
|
+
for (let s = 0; s < total_slots; s++) {
|
|
8743
|
+
const slot = start_reg + s;
|
|
8453
8744
|
if (slot >= 8) continue;
|
|
8454
8745
|
const reg = param_regs[slot];
|
|
8455
8746
|
if (reg === "x0") continue;
|
|
8456
|
-
status.code += `ldr ${reg}, [x29, #${args_base +
|
|
8747
|
+
status.code += `ldr ${reg}, [x29, #${args_base + s * 8}]\n`;
|
|
8457
8748
|
}
|
|
8458
8749
|
if (!is_struct) status.code += `ldr x0, [x29, #${args_base}]\n`;
|
|
8459
8750
|
}
|
|
8460
|
-
overflow_count = Math.max(0,
|
|
8751
|
+
overflow_count = Math.max(0, total_slots - (8 - start_reg));
|
|
8461
8752
|
if (overflow_count > 0) {
|
|
8462
8753
|
outgoing_size = Math.ceil(overflow_count * 8 / 16) * 16;
|
|
8463
8754
|
status.code += `sub sp, sp, #${outgoing_size}\n`;
|
|
@@ -8769,9 +9060,13 @@ function static_enum_case(operand, enum_node, status) {
|
|
|
8769
9060
|
//#region ../src/build_aarch64/build_operation_node.ts
|
|
8770
9061
|
let string_counter$1 = 0;
|
|
8771
9062
|
let coalesce_counter = 0;
|
|
9063
|
+
let view_cmp_counter = 0;
|
|
9064
|
+
let sc_counter = 0;
|
|
8772
9065
|
function reset_string_counter$1() {
|
|
8773
9066
|
string_counter$1 = 0;
|
|
8774
9067
|
coalesce_counter = 0;
|
|
9068
|
+
view_cmp_counter = 0;
|
|
9069
|
+
sc_counter = 0;
|
|
8775
9070
|
}
|
|
8776
9071
|
function is_comparison(op) {
|
|
8777
9072
|
return [
|
|
@@ -9052,6 +9347,29 @@ function build_operation_node$1(node, status) {
|
|
|
9052
9347
|
status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
|
|
9053
9348
|
return;
|
|
9054
9349
|
}
|
|
9350
|
+
if ((node.op === "==" || node.op === "!=") && node.operator_func?.struct_name === "string" && (is_view_value$1(node.left_value, status) || is_view_value$1(node.right_value, status))) {
|
|
9351
|
+
const id = view_cmp_counter++;
|
|
9352
|
+
const len_ne_label = `.Lview_cmp_len_ne_${id}`;
|
|
9353
|
+
const done_label = `.Lview_cmp_done_${id}`;
|
|
9354
|
+
emit_view_string_arg(node.right_value, status);
|
|
9355
|
+
status.code += `stp x0, x1, [sp, #-16]!\n`;
|
|
9356
|
+
emit_view_string_arg(node.left_value, status);
|
|
9357
|
+
status.code += `ldr x2, [sp, #8]\n`;
|
|
9358
|
+
status.code += `cmp x1, x2\n`;
|
|
9359
|
+
status.code += `b.ne ${len_ne_label}\n`;
|
|
9360
|
+
status.code += `mov x2, x1\n`;
|
|
9361
|
+
status.code += `ldr x1, [sp]\n`;
|
|
9362
|
+
status.code += `bl _memcmp\n`;
|
|
9363
|
+
status.code += `cmp x0, #0\n`;
|
|
9364
|
+
status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
|
|
9365
|
+
status.code += `b ${done_label}\n`;
|
|
9366
|
+
status.code += `${len_ne_label}:\n`;
|
|
9367
|
+
status.code += `mov x0, #0\n`;
|
|
9368
|
+
if (node.op === "!=") status.code += `mov x0, #1\n`;
|
|
9369
|
+
status.code += `${done_label}:\n`;
|
|
9370
|
+
status.code += `add sp, sp, #16\n`;
|
|
9371
|
+
return;
|
|
9372
|
+
}
|
|
9055
9373
|
if (node.operator_func) {
|
|
9056
9374
|
const left_type = type_from_value_node$1(node.left_value);
|
|
9057
9375
|
const is_array_op = node.operator_func.struct_name.startsWith("Array") && left_type.is_array;
|
|
@@ -9185,6 +9503,25 @@ function build_operation_node$1(node, status) {
|
|
|
9185
9503
|
else status.code += `fmov x0, d0\n`;
|
|
9186
9504
|
return;
|
|
9187
9505
|
}
|
|
9506
|
+
if (node.op === "&&" || node.op === "||") {
|
|
9507
|
+
const id = sc_counter++;
|
|
9508
|
+
const skip_right = `.Lsc_skip_${id}`;
|
|
9509
|
+
const done = `.Lsc_done_${id}`;
|
|
9510
|
+
build_operand$1(node.left_value, "x0", status);
|
|
9511
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9512
|
+
status.code += `cmp x0, #0\n`;
|
|
9513
|
+
status.code += node.op === "&&" ? `b.eq ${skip_right}\n` : `b.ne ${skip_right}\n`;
|
|
9514
|
+
build_operand$1(node.right_value, "x0", status);
|
|
9515
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9516
|
+
status.code += `cmp x0, #0\n`;
|
|
9517
|
+
status.code += `cset x0, ne\n`;
|
|
9518
|
+
status.code += `b ${done}\n`;
|
|
9519
|
+
status.code += `${skip_right}:\n`;
|
|
9520
|
+
if (node.op === "&&") status.code += `mov x0, #0\n`;
|
|
9521
|
+
else status.code += `mov x0, #1\n`;
|
|
9522
|
+
status.code += `${done}:\n`;
|
|
9523
|
+
return;
|
|
9524
|
+
}
|
|
9188
9525
|
const need_spill = !is_simple(node.left_value);
|
|
9189
9526
|
build_operand$1(node.right_value, "x2", status);
|
|
9190
9527
|
if (need_spill) {
|
|
@@ -9195,19 +9532,7 @@ function build_operation_node$1(node, status) {
|
|
|
9195
9532
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9196
9533
|
if (need_spill) status.code += `ldr x2, [sp], #16\n`;
|
|
9197
9534
|
const unsigned = is_unsigned_type(node.left_value) || is_unsigned_type(node.right_value);
|
|
9198
|
-
if (node.op
|
|
9199
|
-
status.code += `cmp x1, #0\n`;
|
|
9200
|
-
status.code += `cset x1, ne\n`;
|
|
9201
|
-
status.code += `cmp x2, #0\n`;
|
|
9202
|
-
status.code += `cset x2, ne\n`;
|
|
9203
|
-
status.code += `and x0, x1, x2\n`;
|
|
9204
|
-
} else if (node.op === "||") {
|
|
9205
|
-
status.code += `cmp x1, #0\n`;
|
|
9206
|
-
status.code += `cset x1, ne\n`;
|
|
9207
|
-
status.code += `cmp x2, #0\n`;
|
|
9208
|
-
status.code += `cset x2, ne\n`;
|
|
9209
|
-
status.code += `orr x0, x1, x2\n`;
|
|
9210
|
-
} else if (is_comparison(node.op)) {
|
|
9535
|
+
if (is_comparison(node.op)) {
|
|
9211
9536
|
status.code += `cmp x1, x2\n`;
|
|
9212
9537
|
status.code += `cset x0, ${map_cmp(node.op, unsigned)}\n`;
|
|
9213
9538
|
} else {
|
|
@@ -9447,6 +9772,12 @@ function build_return_node$1(node, status) {
|
|
|
9447
9772
|
if (override_ctor) status.struct_return_buffer = saved_buffer;
|
|
9448
9773
|
}
|
|
9449
9774
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9775
|
+
if (status.function_return_type?.is_view && status.function_return_type.name === "string" && node.value && !is_view_value$1(node.value, status)) {
|
|
9776
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
9777
|
+
status.code += `bl _strlen\n`;
|
|
9778
|
+
status.code += `mov x1, x0\n`;
|
|
9779
|
+
status.code += `ldr x0, [sp], #16\n`;
|
|
9780
|
+
}
|
|
9450
9781
|
if (status.function_return_type?.name === "string" && !status.function_return_type?.is_view && node.value.node_type === "value" && node.value.value.length >= 2 && node.value.value.startsWith("\"") && node.value.value.endsWith("\"")) {
|
|
9451
9782
|
const fn_name = status.current_function_name;
|
|
9452
9783
|
const mangled = status.current_struct ? `${status.current_struct.name}_${fn_name}` : fn_name;
|
|
@@ -9767,7 +10098,7 @@ function build_value_node$1(node, status) {
|
|
|
9767
10098
|
}
|
|
9768
10099
|
//#endregion
|
|
9769
10100
|
//#region ../src/build_common/scan_string_length_hoists.ts
|
|
9770
|
-
const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
10101
|
+
const NON_IDENTIFIERS$1 = /* @__PURE__ */ new Set([
|
|
9771
10102
|
"true",
|
|
9772
10103
|
"false",
|
|
9773
10104
|
"null",
|
|
@@ -9776,12 +10107,12 @@ const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
|
9776
10107
|
"default"
|
|
9777
10108
|
]);
|
|
9778
10109
|
function is_identifier(value) {
|
|
9779
|
-
return !!value && !NON_IDENTIFIERS.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
|
|
10110
|
+
return !!value && !NON_IDENTIFIERS$1.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
|
|
9780
10111
|
}
|
|
9781
|
-
function root_name(node) {
|
|
10112
|
+
function root_name$1(node) {
|
|
9782
10113
|
if (!node) return void 0;
|
|
9783
10114
|
if (node.node_type === "value") return node.value;
|
|
9784
|
-
if (node.node_type === "access") return root_name(node.target);
|
|
10115
|
+
if (node.node_type === "access") return root_name$1(node.target);
|
|
9785
10116
|
}
|
|
9786
10117
|
/**
|
|
9787
10118
|
* Find the bare `string` variables whose `.length` is read inside a while
|
|
@@ -9868,9 +10199,9 @@ function scan_string_length_hoists(condition, statements, update, status) {
|
|
|
9868
10199
|
}
|
|
9869
10200
|
case "assign": {
|
|
9870
10201
|
const assign = n;
|
|
9871
|
-
const lhs = root_name(assign.left_value);
|
|
10202
|
+
const lhs = root_name$1(assign.left_value);
|
|
9872
10203
|
if (lhs) invalidated.add(lhs);
|
|
9873
|
-
const swap = root_name(assign.swap);
|
|
10204
|
+
const swap = root_name$1(assign.swap);
|
|
9874
10205
|
if (swap) invalidated.add(swap);
|
|
9875
10206
|
walk_children(n, in_boundary);
|
|
9876
10207
|
return;
|
|
@@ -9892,11 +10223,20 @@ function scan_string_length_hoists(condition, statements, update, status) {
|
|
|
9892
10223
|
case "access_func": {
|
|
9893
10224
|
const call = n;
|
|
9894
10225
|
for (const indices of [call.ref_param_indices, call.mov_param_indices]) for (const i of indices ?? []) {
|
|
9895
|
-
const arg = root_name(call.params?.[i]);
|
|
10226
|
+
const arg = root_name$1(call.params?.[i]);
|
|
9896
10227
|
if (arg) invalidated.add(arg);
|
|
9897
10228
|
}
|
|
9898
10229
|
if (call.swap_params) {
|
|
9899
|
-
for (const [i, swap] of call.swap_params) for (const arg of [root_name(call.params?.[i]), root_name(swap)]) if (arg) invalidated.add(arg);
|
|
10230
|
+
for (const [i, swap] of call.swap_params) for (const arg of [root_name$1(call.params?.[i]), root_name$1(swap)]) if (arg) invalidated.add(arg);
|
|
10231
|
+
}
|
|
10232
|
+
if (!in_boundary && call.resolved_function) {
|
|
10233
|
+
const self_offset = call.resolved_function.params[0]?.is_self_param ? 1 : 0;
|
|
10234
|
+
const params = call.params ?? [];
|
|
10235
|
+
for (let i = 0; i < params.length; i++) {
|
|
10236
|
+
if (!call.resolved_function.params[i + self_offset]?.hidden_len) continue;
|
|
10237
|
+
const arg = params[i];
|
|
10238
|
+
if (arg.node_type === "value" && is_identifier(arg.value) && is_string_target(arg)) candidates.set(arg.value, arg);
|
|
10239
|
+
}
|
|
9900
10240
|
}
|
|
9901
10241
|
walk_children(n, in_boundary);
|
|
9902
10242
|
return;
|
|
@@ -10073,6 +10413,7 @@ function build_while_loop_node$1(node, status) {
|
|
|
10073
10413
|
if (!status.string_length_slots) status.string_length_slots = /* @__PURE__ */ new Map();
|
|
10074
10414
|
for (const [name, target] of hoists) {
|
|
10075
10415
|
if (status.string_length_slots.has(name)) continue;
|
|
10416
|
+
if (status.hidden_len_params?.has(name)) continue;
|
|
10076
10417
|
const offset = allocate_stack_space(status, 8);
|
|
10077
10418
|
status.string_length_slots.set(name, offset);
|
|
10078
10419
|
build_node$1(target, status);
|
|
@@ -10317,6 +10658,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10317
10658
|
emit_owning_replace_T(elem, status);
|
|
10318
10659
|
return true;
|
|
10319
10660
|
}
|
|
10661
|
+
if (func_name === "shift_T") {
|
|
10662
|
+
emit_owning_shift_T(elem, status, "x0");
|
|
10663
|
+
return true;
|
|
10664
|
+
}
|
|
10320
10665
|
return false;
|
|
10321
10666
|
}
|
|
10322
10667
|
if (owning_buffer_is_string_elem_aarch64(struct_node)) {
|
|
@@ -10328,6 +10673,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10328
10673
|
emit_string_replace_T(status, "x0");
|
|
10329
10674
|
return true;
|
|
10330
10675
|
}
|
|
10676
|
+
if (func_name === "shift_T") {
|
|
10677
|
+
emit_string_shift_T(status, "x0");
|
|
10678
|
+
return true;
|
|
10679
|
+
}
|
|
10331
10680
|
}
|
|
10332
10681
|
return false;
|
|
10333
10682
|
}
|
|
@@ -10341,7 +10690,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10341
10690
|
function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
10342
10691
|
const elem = owning_buffer_element_aarch64(node, status);
|
|
10343
10692
|
if (elem) {
|
|
10344
|
-
if (func_name !== "store_T" && func_name !== "replace_T") return false;
|
|
10693
|
+
if (func_name !== "store_T" && func_name !== "replace_T" && func_name !== "shift_T") return false;
|
|
10345
10694
|
emit_owning_standalone_struct(elem, func_name, status);
|
|
10346
10695
|
return true;
|
|
10347
10696
|
}
|
|
@@ -10354,12 +10703,20 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
|
10354
10703
|
emit_string_replace_T(status, "x19");
|
|
10355
10704
|
return true;
|
|
10356
10705
|
}
|
|
10706
|
+
if (func_name === "shift_T") {
|
|
10707
|
+
emit_string_shift_T(status, "x19");
|
|
10708
|
+
return true;
|
|
10709
|
+
}
|
|
10357
10710
|
}
|
|
10358
10711
|
return false;
|
|
10359
10712
|
}
|
|
10360
10713
|
function emit_owning_standalone_struct(elem, func_name, status) {
|
|
10361
10714
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
10362
10715
|
const string_fields = collect_string_fields(elem, status);
|
|
10716
|
+
if (func_name === "shift_T") {
|
|
10717
|
+
emit_owning_shift_T(elem, status, "x19");
|
|
10718
|
+
return true;
|
|
10719
|
+
}
|
|
10363
10720
|
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10364
10721
|
status.code += `str x22, [sp, #-16]!\n`;
|
|
10365
10722
|
status.code += `ldr x20, [x19, #8]\n`;
|
|
@@ -10459,6 +10816,59 @@ function emit_string_replace_T(status, self_reg) {
|
|
|
10459
10816
|
status.code += `${done}:\n`;
|
|
10460
10817
|
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10461
10818
|
}
|
|
10819
|
+
/**
|
|
10820
|
+
* Specialized `Buffer<string>` shift_T. `self_reg` is the register holding
|
|
10821
|
+
* the Buffer pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src.
|
|
10822
|
+
* Move slot src into slot dst — free dst's heap copy, take over src's
|
|
10823
|
+
* pointer, zero src. Exactly one slot owns the string afterwards.
|
|
10824
|
+
*/
|
|
10825
|
+
function emit_string_shift_T(status, self_reg) {
|
|
10826
|
+
const done = `.Lstr_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
10827
|
+
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10828
|
+
status.code += `ldr x9, [${self_reg}, #8]\n`;
|
|
10829
|
+
status.code += `add x20, x9, x1, lsl #3\n`;
|
|
10830
|
+
status.code += `add x21, x9, x2, lsl #3\n`;
|
|
10831
|
+
status.code += `cmp x20, x21\n`;
|
|
10832
|
+
status.code += `b.eq ${done}\n`;
|
|
10833
|
+
status.code += `ldr x0, [x20]\n`;
|
|
10834
|
+
emit_free(status);
|
|
10835
|
+
status.code += `ldr x9, [x21]\n`;
|
|
10836
|
+
status.code += `str x9, [x20]\n`;
|
|
10837
|
+
status.code += `str xzr, [x21]\n`;
|
|
10838
|
+
status.code += `${done}:\n`;
|
|
10839
|
+
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10840
|
+
}
|
|
10841
|
+
/**
|
|
10842
|
+
* Specialized owning-value-struct shift_T. `self_reg` holds the Buffer
|
|
10843
|
+
* pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src (slot indices).
|
|
10844
|
+
* Destroy dst's old value, copy src's bytes over it wholesale (the moved
|
|
10845
|
+
* struct's owned pointers transfer — no deep copy), zero src.
|
|
10846
|
+
*/
|
|
10847
|
+
function emit_owning_shift_T(elem, status, self_reg) {
|
|
10848
|
+
const T_SIZE = get_struct_size(elem.name, status);
|
|
10849
|
+
const done = `.Lown_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
10850
|
+
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10851
|
+
status.code += `str x22, [sp, #-16]!\n`;
|
|
10852
|
+
status.code += `ldr x9, [${self_reg}, #8]\n`;
|
|
10853
|
+
status.code += `mov x22, #${T_SIZE}\n`;
|
|
10854
|
+
status.code += `madd x20, x1, x22, x9\n`;
|
|
10855
|
+
status.code += `madd x21, x2, x22, x9\n`;
|
|
10856
|
+
status.code += `cmp x20, x21\n`;
|
|
10857
|
+
status.code += `b.eq ${done}\n`;
|
|
10858
|
+
status.code += `mov x0, x20\n`;
|
|
10859
|
+
status.code += `bl ${elem.name}_destroy\n`;
|
|
10860
|
+
status.code += `mov x0, x20\n`;
|
|
10861
|
+
status.code += `mov x1, x21\n`;
|
|
10862
|
+
status.code += `mov x2, x22\n`;
|
|
10863
|
+
status.code += `bl _memcpy\n`;
|
|
10864
|
+
status.code += `mov x0, x21\n`;
|
|
10865
|
+
status.code += `mov x1, #0\n`;
|
|
10866
|
+
status.code += `mov x2, x22\n`;
|
|
10867
|
+
status.code += `bl _memset\n`;
|
|
10868
|
+
status.code += `${done}:\n`;
|
|
10869
|
+
status.code += `ldr x22, [sp], #16\n`;
|
|
10870
|
+
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10871
|
+
}
|
|
10462
10872
|
function emit_owning_store_T(elem, status) {
|
|
10463
10873
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
10464
10874
|
const string_fields = collect_string_fields(elem, status);
|
|
@@ -11192,10 +11602,14 @@ function build_struct_functions$1(node, status) {
|
|
|
11192
11602
|
if (needs_x19) callee_idx = 1;
|
|
11193
11603
|
const old_ref_params = status.function_ref_params;
|
|
11194
11604
|
const old_ref_class_slots = status.ref_class_slots;
|
|
11605
|
+
const old_view_params = status.function_view_params;
|
|
11606
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
11195
11607
|
status.function_param_regs = /* @__PURE__ */ new Map();
|
|
11196
11608
|
status.function_param_vars = /* @__PURE__ */ new Set();
|
|
11197
11609
|
status.function_ref_params = /* @__PURE__ */ new Set();
|
|
11198
11610
|
status.ref_class_slots = /* @__PURE__ */ new Map();
|
|
11611
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
11612
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
11199
11613
|
status.struct_return_buffer = void 0;
|
|
11200
11614
|
const return_struct = !func.return_type?.is_array && !!status.structs.find((s) => s.name === func.return_type?.name && !s.is_simple_type && !s.is_class);
|
|
11201
11615
|
let return_buffer_stack_offset;
|
|
@@ -11218,6 +11632,10 @@ function build_struct_functions$1(node, status) {
|
|
|
11218
11632
|
if (param.type.is_ref) status.function_ref_params.add(param.name);
|
|
11219
11633
|
continue;
|
|
11220
11634
|
}
|
|
11635
|
+
if (param.type.is_view) {
|
|
11636
|
+
slot_idx += 2;
|
|
11637
|
+
continue;
|
|
11638
|
+
}
|
|
11221
11639
|
if ((!!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type) || !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data)) && callee_idx < callee_saved.length) {
|
|
11222
11640
|
const saved_reg = callee_saved[callee_idx++];
|
|
11223
11641
|
if (saved_reg !== "x19" || !needs_x19) status.code += `str ${saved_reg}, [sp, #-16]!\n`;
|
|
@@ -11235,6 +11653,7 @@ function build_struct_functions$1(node, status) {
|
|
|
11235
11653
|
if (!status.class_vars) status.class_vars = /* @__PURE__ */ new Set();
|
|
11236
11654
|
status.class_vars.add(param.name);
|
|
11237
11655
|
}
|
|
11656
|
+
if (param.hidden_len) slot_idx++;
|
|
11238
11657
|
slot_idx++;
|
|
11239
11658
|
}
|
|
11240
11659
|
status.code += `sub sp, sp, #${stack_placeholder}\n`;
|
|
@@ -11255,6 +11674,22 @@ function build_struct_functions$1(node, status) {
|
|
|
11255
11674
|
second_slot_idx += 2;
|
|
11256
11675
|
continue;
|
|
11257
11676
|
}
|
|
11677
|
+
if (param.type.is_view) {
|
|
11678
|
+
const offset = allocate_stack_space(status, 16, 16);
|
|
11679
|
+
status.stack_offsets.set(param.name, offset);
|
|
11680
|
+
status.function_view_params.add(param.name);
|
|
11681
|
+
for (const half of [0, 1]) {
|
|
11682
|
+
const p_slot = second_slot_idx + half;
|
|
11683
|
+
if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
|
|
11684
|
+
else {
|
|
11685
|
+
const k = p_slot - 8;
|
|
11686
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
|
|
11687
|
+
status.code += `str x9, [x29, #${offset + half * 8}]\n`;
|
|
11688
|
+
}
|
|
11689
|
+
}
|
|
11690
|
+
second_slot_idx += 2;
|
|
11691
|
+
continue;
|
|
11692
|
+
}
|
|
11258
11693
|
if (!(!!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type) || !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data))) {
|
|
11259
11694
|
const size = aarch64_size(param.type.name);
|
|
11260
11695
|
const offset = allocate_stack_space(status, size, size);
|
|
@@ -11285,6 +11720,20 @@ function build_struct_functions$1(node, status) {
|
|
|
11285
11720
|
}
|
|
11286
11721
|
}
|
|
11287
11722
|
}
|
|
11723
|
+
if (param.hidden_len) {
|
|
11724
|
+
const len_offset = allocate_stack_space(status, 8, 8);
|
|
11725
|
+
status.stack_offsets.set(`_${param.name}_len`, len_offset);
|
|
11726
|
+
if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
11727
|
+
status.hidden_len_params.add(param.name);
|
|
11728
|
+
const len_slot = second_slot_idx + 1;
|
|
11729
|
+
if (len_slot < 8) status.code += `str ${param_regs[len_slot]}, [x29, #${len_offset}]\n`;
|
|
11730
|
+
else {
|
|
11731
|
+
const k = len_slot - 8;
|
|
11732
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
|
|
11733
|
+
status.code += `str x9, [x29, #${len_offset}]\n`;
|
|
11734
|
+
}
|
|
11735
|
+
second_slot_idx++;
|
|
11736
|
+
}
|
|
11288
11737
|
second_slot_idx++;
|
|
11289
11738
|
}
|
|
11290
11739
|
const moved_param_save_slots = /* @__PURE__ */ new Map();
|
|
@@ -11374,6 +11823,8 @@ function build_struct_functions$1(node, status) {
|
|
|
11374
11823
|
status.function_param_vars = old_param_vars;
|
|
11375
11824
|
status.function_ref_params = old_ref_params;
|
|
11376
11825
|
status.ref_class_slots = old_ref_class_slots;
|
|
11826
|
+
status.function_view_params = old_view_params;
|
|
11827
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
11377
11828
|
status.function_return_label = old_return_label;
|
|
11378
11829
|
status.force_heap_strings = old_force_heap;
|
|
11379
11830
|
status.struct_return_buffer = void 0;
|
|
@@ -11814,6 +12265,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11814
12265
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
11815
12266
|
const old_moved = status.moved;
|
|
11816
12267
|
const old_outer_scope_declarations = status.outer_scope_declarations;
|
|
12268
|
+
const old_current_struct = status.current_struct;
|
|
12269
|
+
const old_current_function_name = status.current_function_name;
|
|
12270
|
+
const old_return_assign = status.return_assign;
|
|
12271
|
+
const old_join_needs_owned_string = status.join_needs_owned_string;
|
|
12272
|
+
const old_emitted_allocations = status.emitted_allocations;
|
|
11817
12273
|
const return_label = `.inline_ret_${inline_counter++}`;
|
|
11818
12274
|
status.function_return_label = return_label;
|
|
11819
12275
|
status.scoped_declarations = [];
|
|
@@ -11824,6 +12280,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11824
12280
|
status.buffer_data_cache = void 0;
|
|
11825
12281
|
status.heap_cleanup_stack = [];
|
|
11826
12282
|
status.moved = /* @__PURE__ */ new Set();
|
|
12283
|
+
status.current_struct = struct_node;
|
|
12284
|
+
status.current_function_name = func.name;
|
|
12285
|
+
status.return_assign = void 0;
|
|
12286
|
+
status.join_needs_owned_string = void 0;
|
|
12287
|
+
status.emitted_allocations = /* @__PURE__ */ new Set();
|
|
11827
12288
|
if (needs_x19) {
|
|
11828
12289
|
status.code += `str x19, [sp, #-16]!\n`;
|
|
11829
12290
|
status.code += `mov x19, x0\n`;
|
|
@@ -11881,9 +12342,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11881
12342
|
status.code += `str x8, [x29, #${return_buffer_stack_offset}]\n`;
|
|
11882
12343
|
status.return_buffer_stack_offset = return_buffer_stack_offset;
|
|
11883
12344
|
}
|
|
12345
|
+
const code_length_before_body = status.code.length;
|
|
11884
12346
|
build_block_node$1(func, status);
|
|
11885
12347
|
const standalone_return_label = `.return_${struct_node.name}_${func.name.replace(/#/g, "")}`;
|
|
11886
|
-
|
|
12348
|
+
const body = status.code.slice(code_length_before_body);
|
|
12349
|
+
status.code = status.code.slice(0, code_length_before_body) + body.replaceAll(`b ${standalone_return_label}\n`, `b ${return_label}\n`);
|
|
11887
12350
|
status.code += `${return_label}:\n`;
|
|
11888
12351
|
for (let i = saved_stack_slots.length - 1; i >= 0; i--) status.code += `add sp, sp, #16\n`;
|
|
11889
12352
|
for (let ci = callee_idx - 1; ci >= 0; ci--) {
|
|
@@ -11905,6 +12368,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11905
12368
|
status.heap_cleanup_stack = old_heap_cleanup_stack;
|
|
11906
12369
|
status.moved = old_moved;
|
|
11907
12370
|
status.outer_scope_declarations = old_outer_scope_declarations;
|
|
12371
|
+
status.current_struct = old_current_struct;
|
|
12372
|
+
status.current_function_name = old_current_function_name;
|
|
12373
|
+
status.return_assign = old_return_assign;
|
|
12374
|
+
status.join_needs_owned_string = old_join_needs_owned_string;
|
|
12375
|
+
status.emitted_allocations = old_emitted_allocations;
|
|
11908
12376
|
}
|
|
11909
12377
|
let inline_fn_depth = 0;
|
|
11910
12378
|
const MAX_INLINE_DEPTH = 2;
|
|
@@ -11925,6 +12393,11 @@ function build_inline_function(func, status) {
|
|
|
11925
12393
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
11926
12394
|
const old_moved = status.moved;
|
|
11927
12395
|
const old_outer_scope_declarations = status.outer_scope_declarations;
|
|
12396
|
+
const old_current_struct = status.current_struct;
|
|
12397
|
+
const old_current_function_name = status.current_function_name;
|
|
12398
|
+
const old_return_assign = status.return_assign;
|
|
12399
|
+
const old_join_needs_owned_string = status.join_needs_owned_string;
|
|
12400
|
+
const old_emitted_allocations = status.emitted_allocations;
|
|
11928
12401
|
const return_label = `.inline_fn_ret_${inline_counter++}`;
|
|
11929
12402
|
status.function_return_label = return_label;
|
|
11930
12403
|
status.scoped_declarations = [];
|
|
@@ -11935,6 +12408,11 @@ function build_inline_function(func, status) {
|
|
|
11935
12408
|
status.buffer_data_cache = void 0;
|
|
11936
12409
|
status.heap_cleanup_stack = [];
|
|
11937
12410
|
status.moved = /* @__PURE__ */ new Set();
|
|
12411
|
+
status.current_struct = void 0;
|
|
12412
|
+
status.current_function_name = func.name;
|
|
12413
|
+
status.return_assign = void 0;
|
|
12414
|
+
status.join_needs_owned_string = void 0;
|
|
12415
|
+
status.emitted_allocations = /* @__PURE__ */ new Set();
|
|
11938
12416
|
const param_regs = [
|
|
11939
12417
|
"x0",
|
|
11940
12418
|
"x1",
|
|
@@ -12004,6 +12482,11 @@ function build_inline_function(func, status) {
|
|
|
12004
12482
|
status.heap_cleanup_stack = old_heap_cleanup_stack;
|
|
12005
12483
|
status.moved = old_moved;
|
|
12006
12484
|
status.outer_scope_declarations = old_outer_scope_declarations;
|
|
12485
|
+
status.current_struct = old_current_struct;
|
|
12486
|
+
status.current_function_name = old_current_function_name;
|
|
12487
|
+
status.return_assign = old_return_assign;
|
|
12488
|
+
status.join_needs_owned_string = old_join_needs_owned_string;
|
|
12489
|
+
status.emitted_allocations = old_emitted_allocations;
|
|
12007
12490
|
inline_fn_depth--;
|
|
12008
12491
|
return true;
|
|
12009
12492
|
}
|
|
@@ -12158,8 +12641,25 @@ function load_nursery_struct_address(target, status) {
|
|
|
12158
12641
|
}
|
|
12159
12642
|
//#endregion
|
|
12160
12643
|
//#region ../src/build_aarch64/build_access_node.ts
|
|
12644
|
+
/**
|
|
12645
|
+
* Whether `struct_node` is a monomorphized `Array<T>` (`Array_<T>`). The mono
|
|
12646
|
+
* name is unambiguous — the compiler derives it itself (`"Array_" + elem`)
|
|
12647
|
+
* for every `Array<T>` instantiation, so a name match is always an Array mono.
|
|
12648
|
+
*/
|
|
12649
|
+
function is_array_mono_struct(struct_node, status) {
|
|
12650
|
+
return !!struct_node && !struct_node.is_generic && struct_node.name.startsWith("Array_") && !!status.structs.find((s) => s.name === struct_node.name);
|
|
12651
|
+
}
|
|
12161
12652
|
function emit_string_length$1(target, status) {
|
|
12162
12653
|
if (target.node_type === "value") {
|
|
12654
|
+
const name = target.value;
|
|
12655
|
+
if (name !== "self" && status.hidden_len_params?.has(name)) {
|
|
12656
|
+
const len_slot = status.stack_offsets?.get(`_${name}_len`);
|
|
12657
|
+
if (len_slot !== void 0) {
|
|
12658
|
+
status.last_result_is_heap = false;
|
|
12659
|
+
status.code += `ldr x0, [x29, #${len_slot}]\n`;
|
|
12660
|
+
return;
|
|
12661
|
+
}
|
|
12662
|
+
}
|
|
12163
12663
|
const slot = status.string_length_slots?.get(target.value);
|
|
12164
12664
|
if (slot !== void 0) {
|
|
12165
12665
|
status.last_result_is_heap = false;
|
|
@@ -12229,18 +12729,7 @@ function build_view_op(node, access_func, status) {
|
|
|
12229
12729
|
if (access_func.name === "to_string" && t.name === "string") {
|
|
12230
12730
|
status.code += `ldr x0, [x29, #${base}]\n`;
|
|
12231
12731
|
status.code += `ldr x1, [x29, #${base + 8}]\n`;
|
|
12232
|
-
status
|
|
12233
|
-
status.code += `add x0, x1, #1\n`;
|
|
12234
|
-
emit_malloc(status);
|
|
12235
|
-
status.code += `str x0, [sp, #-16]!\n`;
|
|
12236
|
-
status.code += `ldr x1, [sp, #16]\n`;
|
|
12237
|
-
status.code += `ldr x2, [sp, #24]\n`;
|
|
12238
|
-
status.code += `ldr x0, [sp]\n`;
|
|
12239
|
-
status.code += `bl _memcpy\n`;
|
|
12240
|
-
status.code += `ldr x0, [sp]\n`;
|
|
12241
|
-
status.code += `ldr x1, [sp, #24]\n`;
|
|
12242
|
-
status.code += `strb wzr, [x0, x1]\n`;
|
|
12243
|
-
status.code += `add sp, sp, #32\n`;
|
|
12732
|
+
emit_view_materialize_owned(status);
|
|
12244
12733
|
status.last_result_is_heap = true;
|
|
12245
12734
|
return true;
|
|
12246
12735
|
}
|
|
@@ -12448,6 +12937,14 @@ function build_access_field(node, status) {
|
|
|
12448
12937
|
}
|
|
12449
12938
|
const target_name = node.target.node_type === "value" ? node.target.value : target_type?.name;
|
|
12450
12939
|
const access_field = node.access;
|
|
12940
|
+
if (access_field.name === "length" && node.target.node_type === "value" && node.target.value === "self" && is_array_mono_struct(status.current_struct, status)) {
|
|
12941
|
+
const self_reg = get_param_reg("self", status);
|
|
12942
|
+
if (self_reg) {
|
|
12943
|
+
if (self_reg !== "x0") status.code += `mov x0, ${self_reg}\n`;
|
|
12944
|
+
status.code += `ldr x0, [x0, #-8]\n`;
|
|
12945
|
+
return;
|
|
12946
|
+
}
|
|
12947
|
+
}
|
|
12451
12948
|
if (access_field.type?.name === "func") {
|
|
12452
12949
|
status.code += `adr x0, ${target_type.name}_${access_field.name}\n`;
|
|
12453
12950
|
return;
|
|
@@ -13086,14 +13583,41 @@ function build_access_method(node, access_func, status) {
|
|
|
13086
13583
|
"x6",
|
|
13087
13584
|
"x7"
|
|
13088
13585
|
];
|
|
13089
|
-
const
|
|
13586
|
+
const view_arg_set = new Set(access_func.view_param_indices ?? []);
|
|
13587
|
+
const hidden_len_indices = callee_hidden_len_indices(access_func);
|
|
13588
|
+
const hidden_len_set = new Set(hidden_len_indices);
|
|
13589
|
+
const hidden_len_slots = /* @__PURE__ */ new Map();
|
|
13590
|
+
const arg_slot = [];
|
|
13591
|
+
let total_arg_slots = 0;
|
|
13592
|
+
for (let i = 0; i < access_func.params.length; i++) {
|
|
13593
|
+
arg_slot.push(total_arg_slots);
|
|
13594
|
+
total_arg_slots += view_arg_set.has(i) ? 2 : 1;
|
|
13595
|
+
if (hidden_len_set.has(i)) {
|
|
13596
|
+
hidden_len_slots.set(i, total_arg_slots);
|
|
13597
|
+
total_arg_slots += 1;
|
|
13598
|
+
}
|
|
13599
|
+
}
|
|
13600
|
+
const overflow_count = Math.max(0, total_arg_slots - (8 - start_reg));
|
|
13090
13601
|
let overflow_base = 0;
|
|
13091
13602
|
if (overflow_count > 0) overflow_base = allocate_stack_space(status, overflow_count * 8, 16);
|
|
13603
|
+
const has_view_args = view_arg_set.size > 0;
|
|
13604
|
+
let view_spill_base = 0;
|
|
13605
|
+
if (has_view_args) view_spill_base = allocate_stack_space(status, total_arg_slots * 8, 16);
|
|
13606
|
+
const view_half_store = (j, half) => {
|
|
13607
|
+
const half_slot = start_reg + arg_slot[j] + half;
|
|
13608
|
+
return half_slot >= 8 ? overflow_base + (half_slot - 8) * 8 : view_spill_base + (arg_slot[j] + half) * 8;
|
|
13609
|
+
};
|
|
13092
13610
|
const ref_class_param_reload = [];
|
|
13093
13611
|
for (let i = access_func.params.length - 1; i >= 0; i--) {
|
|
13094
13612
|
const param = access_func.params[i];
|
|
13095
13613
|
const is_ref_param = access_func.ref_param_indices?.includes(i);
|
|
13096
13614
|
const param_type = param.type?.name || "";
|
|
13615
|
+
if (view_arg_set.has(i)) {
|
|
13616
|
+
emit_view_string_arg(param, status);
|
|
13617
|
+
status.code += `str x0, [x29, #${view_half_store(i, 0)}]\n`;
|
|
13618
|
+
status.code += `str x1, [x29, #${view_half_store(i, 1)}]\n`;
|
|
13619
|
+
continue;
|
|
13620
|
+
}
|
|
13097
13621
|
const is_struct = is_struct_type(param_type, status) || is_enum_with_data_type(param_type, status);
|
|
13098
13622
|
if (is_ref_param) {
|
|
13099
13623
|
const rp_name = param.node_type === "value" ? param.value : void 0;
|
|
@@ -13115,14 +13639,66 @@ function build_access_method(node, access_func, status) {
|
|
|
13115
13639
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13116
13640
|
}
|
|
13117
13641
|
} else build_node$1(param, status);
|
|
13118
|
-
const slot = start_reg + i;
|
|
13642
|
+
const slot = start_reg + arg_slot[i];
|
|
13119
13643
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13120
13644
|
if (slot >= 8) status.code += `str x0, [x29, #${overflow_base + (slot - 8) * 8}]\n`;
|
|
13121
13645
|
else {
|
|
13122
|
-
const reg = param_regs[i];
|
|
13646
|
+
const reg = param_regs[arg_slot[i]];
|
|
13123
13647
|
if (reg && reg !== "x0") status.code += `mov ${reg}, x0\n`;
|
|
13124
13648
|
}
|
|
13125
13649
|
}
|
|
13650
|
+
if (has_view_args) for (let j = 0; j < access_func.params.length; j++) {
|
|
13651
|
+
if (!view_arg_set.has(j)) continue;
|
|
13652
|
+
for (const half of [0, 1]) {
|
|
13653
|
+
const half_slot = start_reg + arg_slot[j] + half;
|
|
13654
|
+
if (half_slot >= 8) continue;
|
|
13655
|
+
status.code += `ldr x${half_slot}, [x29, #${view_spill_base + (arg_slot[j] + half) * 8}]\n`;
|
|
13656
|
+
}
|
|
13657
|
+
}
|
|
13658
|
+
if (hidden_len_indices.length > 0) {
|
|
13659
|
+
const needs_strlen = hidden_len_indices.some((i) => {
|
|
13660
|
+
const arg = access_func.params[i];
|
|
13661
|
+
if (arg.node_type !== "value") return true;
|
|
13662
|
+
return !status.string_length_slots?.has(arg.value);
|
|
13663
|
+
});
|
|
13664
|
+
const live_regs = [];
|
|
13665
|
+
if (needs_strlen) {
|
|
13666
|
+
const max_live = Math.min(7, start_reg + total_arg_slots - 1);
|
|
13667
|
+
for (let s = start_reg; s <= max_live; s++) live_regs.push(`x${s}`);
|
|
13668
|
+
}
|
|
13669
|
+
const reg_save_base = needs_strlen ? allocate_stack_space(status, Math.max(live_regs.length, 1) * 8, 16) : 0;
|
|
13670
|
+
const len_save_base = allocate_stack_space(status, hidden_len_indices.length * 8, 16);
|
|
13671
|
+
for (let k = 0; k < live_regs.length; k++) status.code += `str ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
|
|
13672
|
+
for (let h = 0; h < hidden_len_indices.length; h++) {
|
|
13673
|
+
const i = hidden_len_indices[h];
|
|
13674
|
+
const arg = access_func.params[i];
|
|
13675
|
+
let computed = false;
|
|
13676
|
+
if (arg.node_type === "value") {
|
|
13677
|
+
const hoisted = status.string_length_slots?.get(arg.value);
|
|
13678
|
+
if (hoisted !== void 0) {
|
|
13679
|
+
status.code += `ldr x9, [x29, #${hoisted}]\n`;
|
|
13680
|
+
status.code += `str x9, [x29, #${len_save_base + h * 8}]\n`;
|
|
13681
|
+
computed = true;
|
|
13682
|
+
}
|
|
13683
|
+
}
|
|
13684
|
+
if (!computed) {
|
|
13685
|
+
const src_slot = start_reg + arg_slot[i];
|
|
13686
|
+
if (src_slot < 8) {
|
|
13687
|
+
const save_index = src_slot - start_reg;
|
|
13688
|
+
status.code += `ldr x0, [x29, #${reg_save_base + save_index * 8}]\n`;
|
|
13689
|
+
} else status.code += `ldr x0, [x29, #${overflow_base + (src_slot - 8) * 8}]\n`;
|
|
13690
|
+
status.code += `bl _strlen\n`;
|
|
13691
|
+
status.code += `str x0, [x29, #${len_save_base + h * 8}]\n`;
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13694
|
+
for (let k = live_regs.length - 1; k >= 0; k--) status.code += `ldr ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
|
|
13695
|
+
for (let h = 0; h < hidden_len_indices.length; h++) {
|
|
13696
|
+
const dest_slot = start_reg + hidden_len_slots.get(hidden_len_indices[h]);
|
|
13697
|
+
status.code += `ldr x9, [x29, #${len_save_base + h * 8}]\n`;
|
|
13698
|
+
if (dest_slot < 8) status.code += `mov x${dest_slot}, x9\n`;
|
|
13699
|
+
else status.code += `str x9, [x29, #${overflow_base + (dest_slot - 8) * 8}]\n`;
|
|
13700
|
+
}
|
|
13701
|
+
}
|
|
13126
13702
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13127
13703
|
if (needs_self_save) status.code += `ldr x0, [sp], #16\n`;
|
|
13128
13704
|
let outgoing_size = 0;
|
|
@@ -14226,6 +14802,84 @@ function has_flag_name(name) {
|
|
|
14226
14802
|
return `${name}_has`;
|
|
14227
14803
|
}
|
|
14228
14804
|
//#endregion
|
|
14805
|
+
//#region ../src/build_c/utils/view_value.ts
|
|
14806
|
+
/**
|
|
14807
|
+
* Resolve the struct name of an expression's value by walking the access
|
|
14808
|
+
* chain through the (monomorphized) struct table — bare names via
|
|
14809
|
+
* variable_types / scoped declarations / `self`, field accesses via the
|
|
14810
|
+
* base struct's field type, method calls via the method's return type.
|
|
14811
|
+
* Fallback for cached node types that lost their modifiers (generic bodies
|
|
14812
|
+
* re-checked after monomorphization can carry a stale plain `string`).
|
|
14813
|
+
*/
|
|
14814
|
+
function value_struct_name(node, status) {
|
|
14815
|
+
if (node.node_type === "value") {
|
|
14816
|
+
const name = node.value;
|
|
14817
|
+
if (name === "self" && status.current_struct) return status.current_struct.name;
|
|
14818
|
+
const vt = status.variable_types?.get(name);
|
|
14819
|
+
if (vt?.name) return vt.name;
|
|
14820
|
+
const decl = status.scoped_declarations.findLast((d) => d.name === name);
|
|
14821
|
+
if (decl?.type?.name) return decl.type.name;
|
|
14822
|
+
return;
|
|
14823
|
+
}
|
|
14824
|
+
if (node.node_type === "access") {
|
|
14825
|
+
const inner = node.access;
|
|
14826
|
+
const base = value_struct_name(node.target, status);
|
|
14827
|
+
if (!base) return void 0;
|
|
14828
|
+
const struct = status.structs.find((s) => s.name === base && !s.is_generic);
|
|
14829
|
+
if (!struct) return void 0;
|
|
14830
|
+
if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
|
|
14831
|
+
if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
|
|
14832
|
+
}
|
|
14833
|
+
}
|
|
14834
|
+
/**
|
|
14835
|
+
* Whether an expression is a `view T` VALUE at the C level — a nomen_view
|
|
14836
|
+
* (ptr, len) struct expression that can pass through a `view T` parameter
|
|
14837
|
+
* unchanged. Recovers `is_view` from the cached node type, the declared
|
|
14838
|
+
* variable type, the current function's view params, or (for method calls
|
|
14839
|
+
* whose cached type lost the modifier) the callee's declared return type.
|
|
14840
|
+
*/
|
|
14841
|
+
function is_view_value(node, status) {
|
|
14842
|
+
if (type_from_value_node$1(node)?.is_view) return true;
|
|
14843
|
+
if (node.node_type === "value") {
|
|
14844
|
+
const name = node.value;
|
|
14845
|
+
if (status.variable_types?.get(name)?.is_view) return true;
|
|
14846
|
+
if (status.function_view_params?.has(name)) return true;
|
|
14847
|
+
}
|
|
14848
|
+
if (node.node_type === "access" && node.access.node_type === "access_func") {
|
|
14849
|
+
const access = node.access;
|
|
14850
|
+
const recv_struct = value_struct_name(node.target, status);
|
|
14851
|
+
if ((status.structs.find((s) => s.name === recv_struct && !s.is_generic)?.functions.find((f) => f.name === access.name || f.name === `#${access.name}`))?.return_type?.is_view) return true;
|
|
14852
|
+
}
|
|
14853
|
+
return false;
|
|
14854
|
+
}
|
|
14855
|
+
/**
|
|
14856
|
+
* Emit a C expression for an argument passed to a `view string` parameter.
|
|
14857
|
+
* A view-typed argument is already a nomen_view — pass it through. An owned
|
|
14858
|
+
* `string` expression is wrapped into a nomen_view carrying its strlen (an
|
|
14859
|
+
* implicit owned→view borrow; the caller keeps ownership of the string).
|
|
14860
|
+
* Statement-expression form evaluates the operand exactly once.
|
|
14861
|
+
*/
|
|
14862
|
+
function c_view_string_arg(arg, status) {
|
|
14863
|
+
if (is_view_value(arg, status)) {
|
|
14864
|
+
build_node(arg, status);
|
|
14865
|
+
return;
|
|
14866
|
+
}
|
|
14867
|
+
status.code += `({ char* _p = `;
|
|
14868
|
+
build_node(arg, status);
|
|
14869
|
+
status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
|
|
14870
|
+
}
|
|
14871
|
+
/**
|
|
14872
|
+
* Emit a C expression materializing a `view string` into an OWNED heap
|
|
14873
|
+
* string (malloc len+1 / memcpy / null-terminate). Used when a view value
|
|
14874
|
+
* initializes an owned `string` declaration (`const string s = v`) — the
|
|
14875
|
+
* resulting pointer is heap-owned and must be freed at scope exit.
|
|
14876
|
+
*/
|
|
14877
|
+
function c_materialize_view_string(arg, status) {
|
|
14878
|
+
status.code += `({ nomen_view _t = `;
|
|
14879
|
+
build_node(arg, status);
|
|
14880
|
+
status.code += `; char* _r = malloc(_t.len + 1); memcpy(_r, _t.ptr, _t.len); _r[_t.len] = 0; _r; })`;
|
|
14881
|
+
}
|
|
14882
|
+
//#endregion
|
|
14229
14883
|
//#region ../src/build_c/build_operation_node.ts
|
|
14230
14884
|
function build_operation_node(node, status) {
|
|
14231
14885
|
if (node.operator_func?.deferred) {
|
|
@@ -14332,8 +14986,16 @@ function build_operation_node(node, status) {
|
|
|
14332
14986
|
status.code += `)`;
|
|
14333
14987
|
} else if (node.operator_func) {
|
|
14334
14988
|
const label = node.operator_func.mangled_name || `${node.operator_func.struct_name}_${node.operator_func.func_name}`;
|
|
14335
|
-
const is_string_op = node.type?.name === "string";
|
|
14336
14989
|
const is_string_cmp = node.op === "==" || node.op === "!=";
|
|
14990
|
+
if (is_string_cmp && node.operator_func.struct_name === "string" && (is_view_value(node.left_value, status) || is_view_value(node.right_value, status))) {
|
|
14991
|
+
status.code += `({ nomen_view _a = `;
|
|
14992
|
+
c_view_string_arg(node.left_value, status);
|
|
14993
|
+
status.code += `, _b = `;
|
|
14994
|
+
c_view_string_arg(node.right_value, status);
|
|
14995
|
+
status.code += `; ((_a.len == _b.len) && (memcmp(_a.ptr, _b.ptr, _a.len) == 0))${node.op === "!=" ? " == 0" : ""}; })`;
|
|
14996
|
+
return;
|
|
14997
|
+
}
|
|
14998
|
+
const is_string_op = node.type?.name === "string";
|
|
14337
14999
|
const left_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.left_value, status);
|
|
14338
15000
|
const right_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.right_value, status);
|
|
14339
15001
|
if (left_temp || right_temp) {
|
|
@@ -14587,7 +15249,7 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
14587
15249
|
}
|
|
14588
15250
|
const is_destructured_field_access = dec.value?.node_type === "access" && dec.value.access.node_type === "access_field" && !dec.value.is_moved;
|
|
14589
15251
|
const is_borrowed_string = is_string_borrow(dec.value) || !!status.string_borrow_vars?.has(dec.name);
|
|
14590
|
-
const value_is_heap_string = dec.type.name === "string" && (dec.value?.node_type === "access" && dec.value.access.node_type === "access_func" || dec.value?.node_type === "func_call");
|
|
15252
|
+
const value_is_heap_string = dec.type.name === "string" && (dec.value?.node_type === "access" && dec.value.access.node_type === "access_func" || dec.value?.node_type === "func_call" || !!dec.value && !dec.type.is_view && is_view_value(dec.value, status));
|
|
14591
15253
|
const dec_value = dec.value;
|
|
14592
15254
|
const dec_val_is_string_literal = dec.value?.node_type === "value" && dec_value.value.length >= 2 && dec_value.value.startsWith("\"") && dec_value.value.endsWith("\"");
|
|
14593
15255
|
const dec_val_is_heap_string_var = dec.value?.node_type === "value" && !dec_val_is_string_literal && !!status.scoped_declarations.find((d) => d.name === dec_value.value);
|
|
@@ -14865,6 +15527,70 @@ function reclaim_to_loop_body(status) {
|
|
|
14865
15527
|
return loopBodyIdx;
|
|
14866
15528
|
}
|
|
14867
15529
|
//#endregion
|
|
15530
|
+
//#region ../src/build_c/utils/hidden_len_call.ts
|
|
15531
|
+
function needs_len_materialize(arg) {
|
|
15532
|
+
if (arg.node_type === "value") return false;
|
|
15533
|
+
if (arg.node_type === "access" && arg.access.node_type === "access_field") return false;
|
|
15534
|
+
return true;
|
|
15535
|
+
}
|
|
15536
|
+
/**
|
|
15537
|
+
* Emit the statement-expression prologue materialising every non-lvalue
|
|
15538
|
+
* hidden-len argument once. Must be called immediately BEFORE the call's
|
|
15539
|
+
* label text is emitted; the args loop then substitutes the temps, and
|
|
15540
|
+
* close_hidden_len_wrap() terminates the expression after the call.
|
|
15541
|
+
*/
|
|
15542
|
+
function open_hidden_len_wrap(indices, params, status) {
|
|
15543
|
+
const temps = /* @__PURE__ */ new Map();
|
|
15544
|
+
const to_materialize = indices.filter((i) => params[i] && needs_len_materialize(params[i]));
|
|
15545
|
+
if (to_materialize.length === 0) return {
|
|
15546
|
+
temps,
|
|
15547
|
+
open: false
|
|
15548
|
+
};
|
|
15549
|
+
status.code += `({ `;
|
|
15550
|
+
for (const i of to_materialize) {
|
|
15551
|
+
const tmp = `_hlm_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
15552
|
+
status.code += `char* ${tmp} = `;
|
|
15553
|
+
build_node(params[i], status);
|
|
15554
|
+
status.code += `; `;
|
|
15555
|
+
temps.set(i, tmp);
|
|
15556
|
+
}
|
|
15557
|
+
return {
|
|
15558
|
+
temps,
|
|
15559
|
+
open: true
|
|
15560
|
+
};
|
|
15561
|
+
}
|
|
15562
|
+
function close_hidden_len_wrap(wrap, status) {
|
|
15563
|
+
if (wrap.open) status.code += `; })`;
|
|
15564
|
+
}
|
|
15565
|
+
/**
|
|
15566
|
+
* Emit the companion length EXPRESSION for the argument at a hidden-len
|
|
15567
|
+
* index (the caller emits the separating `, `). `temp` is the materialised
|
|
15568
|
+
* pointer temp when one exists (open_hidden_len_wrap).
|
|
15569
|
+
*/
|
|
15570
|
+
function emit_hidden_len_expr(arg, temp, status) {
|
|
15571
|
+
if (temp) {
|
|
15572
|
+
status.code += `strlen(${temp})`;
|
|
15573
|
+
return;
|
|
15574
|
+
}
|
|
15575
|
+
if (arg.node_type === "value") {
|
|
15576
|
+
const value = arg.value;
|
|
15577
|
+
if (value.startsWith("\"")) {
|
|
15578
|
+
status.code += `sizeof(${value}) - 1`;
|
|
15579
|
+
return;
|
|
15580
|
+
}
|
|
15581
|
+
const hoisted = status.string_length_temps?.get(value);
|
|
15582
|
+
if (hoisted) {
|
|
15583
|
+
status.code += hoisted;
|
|
15584
|
+
return;
|
|
15585
|
+
}
|
|
15586
|
+
status.code += `strlen(${value})`;
|
|
15587
|
+
return;
|
|
15588
|
+
}
|
|
15589
|
+
status.code += `strlen(`;
|
|
15590
|
+
build_node(arg, status);
|
|
15591
|
+
status.code += `)`;
|
|
15592
|
+
}
|
|
15593
|
+
//#endregion
|
|
14868
15594
|
//#region ../src/build_c/build_access_node.ts
|
|
14869
15595
|
/**
|
|
14870
15596
|
* The C type of a single element of a `view T` slice, used to cast the
|
|
@@ -14926,6 +15652,66 @@ function build_vtable_target(node, status) {
|
|
|
14926
15652
|
status.code = status.code.substring(0, before);
|
|
14927
15653
|
status.code += "&" + expr;
|
|
14928
15654
|
}
|
|
15655
|
+
/**
|
|
15656
|
+
* An Array method's `self` is a `struct Array_<T>*` — header `[0]=_vt`,
|
|
15657
|
+
* `[8]=length`, elements at `+16` — which is exactly what a heap receiver
|
|
15658
|
+
* (`struct Array_<T>*`) already is. A NON-heap receiver (a plain stack C
|
|
15659
|
+
* array `T w[N]`, or a variadic `...T` param lowered to `T *w`) would pass
|
|
15660
|
+
* the bare element pointer, so the callee's `self->length` and the raw
|
|
15661
|
+
* bodies' `(T*)((char*)self + sizeof(*self))` reads land on garbage
|
|
15662
|
+
* (`w[1]`, `w + 2`). When such a receiver with a usable length calls a
|
|
15663
|
+
* method on the mono `Array_<T>` struct, build a statement-expression
|
|
15664
|
+
* temporary carrying a proper header plus a copy of the elements — the
|
|
15665
|
+
* same wrap the binary-operator operand path uses
|
|
15666
|
+
* (build_array_operand_for_call). A `ref self` callee additionally gets
|
|
15667
|
+
* its element copy written back after the call, so mutations through
|
|
15668
|
+
* `self` reach the caller's array.
|
|
15669
|
+
*/
|
|
15670
|
+
function array_receiver_wrap(node, target_type, target_method, mono_struct_name, status) {
|
|
15671
|
+
if (!target_type?.is_array || !mono_struct_name.startsWith("Array_")) return void 0;
|
|
15672
|
+
if (node.target.node_type !== "value") return void 0;
|
|
15673
|
+
const target_value = node.target.value;
|
|
15674
|
+
if (target_type.is_array_heap || status.heap_array_vars?.has(target_value)) return void 0;
|
|
15675
|
+
let length;
|
|
15676
|
+
if (status.stack_array_lengths?.has(target_value)) length = status.stack_array_lengths.get(target_value);
|
|
15677
|
+
else if (target_type.length) {
|
|
15678
|
+
const before = status.code.length;
|
|
15679
|
+
build_node(target_type.length, status);
|
|
15680
|
+
length = status.code.substring(before);
|
|
15681
|
+
status.code = status.code.substring(0, before);
|
|
15682
|
+
} else if (status.function_variadic_params?.has(target_value)) length = `_${target_value}_len`;
|
|
15683
|
+
if (!length) return void 0;
|
|
15684
|
+
const before = status.code.length;
|
|
15685
|
+
status.suppress_dereference = true;
|
|
15686
|
+
build_node(node.target, status);
|
|
15687
|
+
status.suppress_dereference = false;
|
|
15688
|
+
const recv = status.code.substring(before);
|
|
15689
|
+
status.code = status.code.substring(0, before);
|
|
15690
|
+
const elem_c = c_type(target_type.name);
|
|
15691
|
+
const id = status.label_counter = (status.label_counter ?? 0) + 1;
|
|
15692
|
+
const wrap = `_arrm_${id}`;
|
|
15693
|
+
const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
|
|
15694
|
+
const ret = target_method?.return_type;
|
|
15695
|
+
const ret_c = !ret?.name ? "void" : ret.is_array ? "void*" : c_type(ret.name);
|
|
15696
|
+
const bytes = `${length} * sizeof(${elem_c})`;
|
|
15697
|
+
const length_is_literal = /^(\d+L?|0x[0-9a-fA-F]+)$/.test(length.trim());
|
|
15698
|
+
let prefix;
|
|
15699
|
+
let self_expr;
|
|
15700
|
+
if (length_is_literal) {
|
|
15701
|
+
prefix = `({ struct { struct ${mono_struct_name} _h; ${elem_c} _d[${length}]; } ${wrap}; ${wrap}._h._vt = 0; ${wrap}._h.length = ${length}; memcpy(${wrap}._d, ${recv}, ${bytes}); ` + (method_self_is_ref && ret_c !== "void" ? `${ret_c} _arrm_r_${id} = ` : "");
|
|
15702
|
+
self_expr = `(struct ${mono_struct_name}*)&${wrap}`;
|
|
15703
|
+
} else {
|
|
15704
|
+
prefix = `({ char ${wrap}[sizeof(struct ${mono_struct_name}) + ${bytes}]; struct ${mono_struct_name}* _arrm_p_${id} = (struct ${mono_struct_name}*)${wrap}; _arrm_p_${id}->_vt = 0; _arrm_p_${id}->length = ${length}; memcpy(${wrap} + sizeof(struct ${mono_struct_name}), ${recv}, ${bytes}); ` + (method_self_is_ref && ret_c !== "void" ? `${ret_c} _arrm_r_${id} = ` : "");
|
|
15705
|
+
self_expr = `_arrm_p_${id}`;
|
|
15706
|
+
}
|
|
15707
|
+
const copy_back = `memcpy(${recv}, ${length_is_literal ? `${wrap}._d` : `${wrap} + sizeof(struct ${mono_struct_name})`}, ${bytes}); `;
|
|
15708
|
+
const suffix = (method_self_is_ref ? `; ${copy_back}` + (ret_c !== "void" ? `_arrm_r_${id}; ` : `(void)0; `) : "") + `; })`;
|
|
15709
|
+
return {
|
|
15710
|
+
prefix,
|
|
15711
|
+
self_expr,
|
|
15712
|
+
suffix
|
|
15713
|
+
};
|
|
15714
|
+
}
|
|
14929
15715
|
function build_access_node(node, status) {
|
|
14930
15716
|
let target_type = type_from_value_node$1(node.target);
|
|
14931
15717
|
if (!target_type?.name && node.target.node_type === "value") {
|
|
@@ -15233,20 +16019,36 @@ function build_access_node(node, status) {
|
|
|
15233
16019
|
}
|
|
15234
16020
|
}
|
|
15235
16021
|
const label = access_func.mangled_name || trait_default_label || `${mono_struct_name}_${access_func.name.replace(/#/g, "")}`;
|
|
16022
|
+
const array_wrap = array_receiver_wrap(node, method_type, target_method, mono_struct_name, status);
|
|
16023
|
+
if (array_wrap) status.code += array_wrap.prefix;
|
|
16024
|
+
const hidden_len = callee_hidden_len_indices(access_func);
|
|
16025
|
+
const hidden_wrap = open_hidden_len_wrap(hidden_len, access_func.params, status);
|
|
15236
16026
|
status.code += `${label}(`;
|
|
15237
16027
|
if (!access_func.is_static) {
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
const
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
16028
|
+
if (array_wrap) status.code += array_wrap.self_expr;
|
|
16029
|
+
else {
|
|
16030
|
+
const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
|
|
16031
|
+
if (!built_in_types.includes(method_type?.name || "") || method_self_is_ref) {
|
|
16032
|
+
const target_value = node.target.node_type === "value" ? node.target.value : "";
|
|
16033
|
+
const target_is_ref_class_param = !!status.ref_class_params?.has(target_value);
|
|
16034
|
+
if (!(!!status.function_ref_params?.has(target_value) || !!status.class_vars?.has(target_value) || !!status.heap_array_vars?.has(target_value))) status.code += "&";
|
|
16035
|
+
else if (target_is_ref_class_param) {} else status.suppress_dereference = true;
|
|
16036
|
+
}
|
|
16037
|
+
build_node(node.target, status);
|
|
16038
|
+
status.suppress_dereference = false;
|
|
15244
16039
|
}
|
|
15245
|
-
build_node(node.target, status);
|
|
15246
|
-
status.suppress_dereference = false;
|
|
15247
16040
|
}
|
|
15248
16041
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
15249
16042
|
if (!access_func.is_static || i > 0) status.code += ", ";
|
|
16043
|
+
const hidden_temp = hidden_wrap.temps.get(i);
|
|
16044
|
+
if (hidden_temp) {
|
|
16045
|
+
status.code += `${hidden_temp}, strlen(${hidden_temp})`;
|
|
16046
|
+
continue;
|
|
16047
|
+
}
|
|
16048
|
+
if (access_func.view_param_indices?.includes(i)) {
|
|
16049
|
+
c_view_string_arg(access_func.params[i], status);
|
|
16050
|
+
continue;
|
|
16051
|
+
}
|
|
15250
16052
|
const param_type = type_from_value_node$1(access_func.params[i]);
|
|
15251
16053
|
const param_value = access_func.params[i].node_type === "value" ? access_func.params[i].value : "";
|
|
15252
16054
|
const arg_is_struct_or_trait = !!status.structs.find((s) => s.name === param_type.name && !s.is_simple_type) || !!status.traits.find((t) => t.name === param_type.name) || !!status.class_vars?.has(param_value);
|
|
@@ -15267,8 +16069,14 @@ function build_access_node(node, status) {
|
|
|
15267
16069
|
build_node(access_func.params[i], status);
|
|
15268
16070
|
status.suppress_dereference = false;
|
|
15269
16071
|
} else build_node(access_func.params[i], status);
|
|
16072
|
+
if (hidden_len.includes(i)) {
|
|
16073
|
+
status.code += `, `;
|
|
16074
|
+
emit_hidden_len_expr(access_func.params[i], void 0, status);
|
|
16075
|
+
}
|
|
15270
16076
|
}
|
|
15271
16077
|
status.code += ")";
|
|
16078
|
+
close_hidden_len_wrap(hidden_wrap, status);
|
|
16079
|
+
if (array_wrap) status.code += array_wrap.suffix;
|
|
15272
16080
|
}
|
|
15273
16081
|
if (access_func.mov_param_indices) for (const idx of access_func.mov_param_indices) {
|
|
15274
16082
|
const param = access_func.params[idx];
|
|
@@ -15310,6 +16118,11 @@ function resolve_access_field_type(node, status) {
|
|
|
15310
16118
|
}
|
|
15311
16119
|
function emit_string_length(target, status) {
|
|
15312
16120
|
if (target.node_type === "value") {
|
|
16121
|
+
const name = target.value;
|
|
16122
|
+
if (name !== "self" && status.hidden_len_params?.has(c_function_name(name))) {
|
|
16123
|
+
status.code += `_${c_function_name(name)}_len`;
|
|
16124
|
+
return;
|
|
16125
|
+
}
|
|
15313
16126
|
const temp = status.string_length_temps?.get(target.value);
|
|
15314
16127
|
if (temp) {
|
|
15315
16128
|
status.code += temp;
|
|
@@ -15834,6 +16647,10 @@ function build_parameter_node(node, status) {
|
|
|
15834
16647
|
status.code += `)`;
|
|
15835
16648
|
return;
|
|
15836
16649
|
}
|
|
16650
|
+
if (!node.is_self_param && node.type.is_view) {
|
|
16651
|
+
status.code += `nomen_view ${c_function_name(node.name)}`;
|
|
16652
|
+
return;
|
|
16653
|
+
}
|
|
15837
16654
|
const cls = classify_param(node.type, type_name, {
|
|
15838
16655
|
is_ref: node.is_ref || node.type.is_ref,
|
|
15839
16656
|
is_self: node.is_self_param,
|
|
@@ -15961,6 +16778,7 @@ function has_string_fields(node, status) {
|
|
|
15961
16778
|
const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
|
|
15962
16779
|
"store_T",
|
|
15963
16780
|
"replace_T",
|
|
16781
|
+
"shift_T",
|
|
15964
16782
|
"destroy",
|
|
15965
16783
|
"#destroy"
|
|
15966
16784
|
]);
|
|
@@ -15995,6 +16813,15 @@ function emit_owning_buffer_string_body(func_name, status) {
|
|
|
15995
16813
|
status.code += `if (val != _old) { free(_old); _slots[i] = val ? strdup(val) : 0; }\n`;
|
|
15996
16814
|
return true;
|
|
15997
16815
|
}
|
|
16816
|
+
if (func_name === "shift_T") {
|
|
16817
|
+
status.code += `char** _slots = (char**)self->data;\n`;
|
|
16818
|
+
status.code += `if (dst != src) {\n`;
|
|
16819
|
+
status.code += `free(_slots[dst]);\n`;
|
|
16820
|
+
status.code += `_slots[dst] = _slots[src];\n`;
|
|
16821
|
+
status.code += `_slots[src] = 0;\n`;
|
|
16822
|
+
status.code += `}\n`;
|
|
16823
|
+
return true;
|
|
16824
|
+
}
|
|
15998
16825
|
if (func_name === "destroy") {
|
|
15999
16826
|
status.code += `if (self->data) {\n`;
|
|
16000
16827
|
status.code += `char** _slots = (char**)self->data;\n`;
|
|
@@ -16034,6 +16861,15 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
16034
16861
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status);
|
|
16035
16862
|
return true;
|
|
16036
16863
|
}
|
|
16864
|
+
if (func_name === "shift_T") {
|
|
16865
|
+
status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
|
|
16866
|
+
status.code += `if (dst != src) {\n`;
|
|
16867
|
+
status.code += `${elem.name}_destroy(&_slots[dst]);\n`;
|
|
16868
|
+
status.code += `_slots[dst] = _slots[src];\n`;
|
|
16869
|
+
status.code += `memset((void*)&_slots[src], 0, sizeof(struct ${elem.name}));\n`;
|
|
16870
|
+
status.code += `}\n`;
|
|
16871
|
+
return true;
|
|
16872
|
+
}
|
|
16037
16873
|
if (func_name === "destroy") {
|
|
16038
16874
|
status.code += `if (self->data) {\n`;
|
|
16039
16875
|
status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
|
|
@@ -16356,6 +17192,10 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16356
17192
|
const old_borrow_only = status.c_borrow_only_strings;
|
|
16357
17193
|
const old_return_type = status.function_return_type;
|
|
16358
17194
|
const old_function_name = status.current_function_name;
|
|
17195
|
+
const old_view_params = status.function_view_params;
|
|
17196
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
17197
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
17198
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
16359
17199
|
status.current_function_name = func.name;
|
|
16360
17200
|
status.function_ref_params = /* @__PURE__ */ new Set();
|
|
16361
17201
|
status.class_vars = /* @__PURE__ */ new Set();
|
|
@@ -16367,6 +17207,8 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16367
17207
|
const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
|
|
16368
17208
|
status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
|
|
16369
17209
|
for (let param of func.params) {
|
|
17210
|
+
if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
|
|
17211
|
+
if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
|
|
16370
17212
|
const param_struct = status.structs.find((s) => s.name === param.type.name);
|
|
16371
17213
|
const param_trait = status.traits.find((t) => t.name === param.type.name);
|
|
16372
17214
|
if (param.is_self_param || param_struct && !param_struct.is_simple_type || param_trait || param.is_ref || param.type.is_ref || param.declaration === "var" && param_struct && !param_struct.is_simple_type) {
|
|
@@ -16417,6 +17259,7 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16417
17259
|
for (let i = 0; i < func.params.length; i++) {
|
|
16418
17260
|
if (i > 0) status.code += ", ";
|
|
16419
17261
|
build_parameter_node(func.params[i], status);
|
|
17262
|
+
if (func.params[i].hidden_len) status.code += `, long _${c_function_name(func.params[i].name)}_len`;
|
|
16420
17263
|
}
|
|
16421
17264
|
status.code += `)`;
|
|
16422
17265
|
forward_decl_referenced_types(func, status);
|
|
@@ -16446,6 +17289,8 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16446
17289
|
status.c_borrow_only_strings = old_borrow_only;
|
|
16447
17290
|
status.function_return_type = old_return_type;
|
|
16448
17291
|
status.current_function_name = old_function_name;
|
|
17292
|
+
status.function_view_params = old_view_params;
|
|
17293
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
16449
17294
|
}
|
|
16450
17295
|
status.current_struct = old_current_struct;
|
|
16451
17296
|
for (let traitName of node.traits) {
|
|
@@ -16576,6 +17421,7 @@ function build_function_node(node, status) {
|
|
|
16576
17421
|
else {
|
|
16577
17422
|
if (node.return_type.name) {
|
|
16578
17423
|
if (node.return_type.is_array) status.code += `struct Array_${node.return_type.name}* `;
|
|
17424
|
+
else if (node.return_type.is_view) status.code += `nomen_view `;
|
|
16579
17425
|
else {
|
|
16580
17426
|
const mono_return_name = mono_type_name(node.return_type);
|
|
16581
17427
|
const return_is_class = !!status.structs.find((s) => s.name === mono_return_name && s.is_class);
|
|
@@ -16596,6 +17442,7 @@ function build_function_node(node, status) {
|
|
|
16596
17442
|
if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
|
|
16597
17443
|
build_parameter_node(node.params[i], status);
|
|
16598
17444
|
if (is_nullable_struct_type(node.params[i].type, status) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
|
|
17445
|
+
if (node.params[i].hidden_len) status.code += `, long _${c_function_name(node.params[i].name)}_len`;
|
|
16599
17446
|
}
|
|
16600
17447
|
if (is_nullable_struct_type(node.return_type, status)) {
|
|
16601
17448
|
if (!first_param) status.code += ", ";
|
|
@@ -16624,6 +17471,10 @@ function build_function_node(node, status) {
|
|
|
16624
17471
|
status.ref_class_param_types = /* @__PURE__ */ new Map();
|
|
16625
17472
|
const old_variadic_params = status.function_variadic_params;
|
|
16626
17473
|
status.function_variadic_params = /* @__PURE__ */ new Set();
|
|
17474
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
17475
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
17476
|
+
const old_view_params = status.function_view_params;
|
|
17477
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
16627
17478
|
const old_return_type = status.function_return_type;
|
|
16628
17479
|
status.function_return_type = node.return_type;
|
|
16629
17480
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
@@ -16633,6 +17484,8 @@ function build_function_node(node, status) {
|
|
|
16633
17484
|
status.current_function_name = node.name;
|
|
16634
17485
|
for (let param of node.params) {
|
|
16635
17486
|
if (param.is_variadic) status.function_variadic_params.add(c_function_name(param.name));
|
|
17487
|
+
if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
|
|
17488
|
+
if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
|
|
16636
17489
|
if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
|
|
16637
17490
|
const pname = c_function_name(param.name);
|
|
16638
17491
|
if (!status.heap_array_vars) status.heap_array_vars = /* @__PURE__ */ new Set();
|
|
@@ -16665,6 +17518,8 @@ function build_function_node(node, status) {
|
|
|
16665
17518
|
status.ref_class_params = old_ref_class_params;
|
|
16666
17519
|
status.ref_class_param_types = old_ref_class_param_types;
|
|
16667
17520
|
status.function_variadic_params = old_variadic_params;
|
|
17521
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
17522
|
+
status.function_view_params = old_view_params;
|
|
16668
17523
|
status.function_return_type = old_return_type;
|
|
16669
17524
|
status.nullable_ret_has_param = old_nullable_ret_has;
|
|
16670
17525
|
status.current_function_name = old_function_name;
|
|
@@ -17378,7 +18233,8 @@ function build_declaration_node(node, status) {
|
|
|
17378
18233
|
const val_node = node.value;
|
|
17379
18234
|
const val_is_string_literal = node.value.node_type === "value" && val_node.value.length >= 2 && val_node.value.startsWith("\"") && val_node.value.endsWith("\"");
|
|
17380
18235
|
const val_is_heap_string_var = node.value.node_type === "value" && !val_is_string_literal && !!status.scoped_declarations.find((d) => d.name === val_node.value);
|
|
17381
|
-
if (node.
|
|
18236
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value(node.value, status)) c_materialize_view_string(node.value, status);
|
|
18237
|
+
else if (node.declaration === "var" && node.type.name === "string" && !node.type.is_view && (val_is_string_literal || val_is_heap_string_var) && !is_borrow_only_string) {
|
|
17382
18238
|
status.code += `strdup(`;
|
|
17383
18239
|
build_node(node.value, status);
|
|
17384
18240
|
status.code += `)`;
|
|
@@ -17595,10 +18451,17 @@ function build_function_call_node(node, status) {
|
|
|
17595
18451
|
}
|
|
17596
18452
|
const is_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type);
|
|
17597
18453
|
const func_name = is_struct ? `${node.name}_init` : c_function_name(node.name);
|
|
18454
|
+
const hidden_len = callee_hidden_len_indices(node);
|
|
18455
|
+
const hidden_wrap = open_hidden_len_wrap(hidden_len, node.params, status);
|
|
17598
18456
|
status.code += `${func_name}(`;
|
|
17599
18457
|
const variadic_idx = node.variadic_param_name ? node.params.findIndex((p) => p.node_type === "array") : -1;
|
|
17600
18458
|
for (let i = 0; i < node.params.length; i++) {
|
|
17601
18459
|
if (i > 0) status.code += ", ";
|
|
18460
|
+
const hidden_temp = hidden_wrap.temps.get(i);
|
|
18461
|
+
if (hidden_temp) {
|
|
18462
|
+
status.code += `${hidden_temp}, strlen(${hidden_temp})`;
|
|
18463
|
+
continue;
|
|
18464
|
+
}
|
|
17602
18465
|
if (i === variadic_idx && node.params[i].node_type === "array") {
|
|
17603
18466
|
const arr = node.params[i];
|
|
17604
18467
|
status.code += `${arr.values.length}, (${c_type(arr.type.name)}[]){`;
|
|
@@ -17620,6 +18483,10 @@ function build_function_call_node(node, status) {
|
|
|
17620
18483
|
status.suppress_dereference = false;
|
|
17621
18484
|
continue;
|
|
17622
18485
|
}
|
|
18486
|
+
if (node.view_param_indices?.includes(i)) {
|
|
18487
|
+
c_view_string_arg(node.params[i], status);
|
|
18488
|
+
continue;
|
|
18489
|
+
}
|
|
17623
18490
|
const is_ref_param = node.ref_param_indices?.includes(i);
|
|
17624
18491
|
const param_is_class = !!status.structs.find((s) => s.name === param_type.name && s.is_class);
|
|
17625
18492
|
if (is_ref_param && param_is_class) {
|
|
@@ -17649,6 +18516,10 @@ function build_function_call_node(node, status) {
|
|
|
17649
18516
|
status.code += `, `;
|
|
17650
18517
|
emit_nullable_arg_flag(node.params[i], status);
|
|
17651
18518
|
}
|
|
18519
|
+
if (hidden_len.includes(i)) {
|
|
18520
|
+
status.code += `, `;
|
|
18521
|
+
emit_hidden_len_expr(node.params[i], void 0, status);
|
|
18522
|
+
}
|
|
17652
18523
|
}
|
|
17653
18524
|
if (is_nullable_struct_type(node.type, status)) {
|
|
17654
18525
|
const flag_name = status.current_nullable_call_flag;
|
|
@@ -17664,6 +18535,7 @@ function build_function_call_node(node, status) {
|
|
|
17664
18535
|
}
|
|
17665
18536
|
}
|
|
17666
18537
|
status.code += ")";
|
|
18538
|
+
close_hidden_len_wrap(hidden_wrap, status);
|
|
17667
18539
|
if (node.name.startsWith("_string_interpolate_")) status.interpolate_string_counts.add(node.params.length - 1);
|
|
17668
18540
|
if (node.swap_params?.size) for (const [idx, swap_expr] of node.swap_params) {
|
|
17669
18541
|
const source = node.params[idx];
|
|
@@ -18072,10 +18944,13 @@ function build_return_node(node, status) {
|
|
|
18072
18944
|
}
|
|
18073
18945
|
}
|
|
18074
18946
|
if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `strdup(`;
|
|
18947
|
+
const wrap_view_string_return = !!ret_type.is_view && ret_type.name === "string" && !is_view_value(node.value, status);
|
|
18075
18948
|
const expr_type_name = node.type?.name || "";
|
|
18076
18949
|
const expr_is_simple = !status.structs.find((s) => s.name === expr_type_name && !s.is_simple_type);
|
|
18077
18950
|
if (is_struct && expr_is_simple && !returns_borrowed_string && !ret_type.is_view) status.code += return_is_class ? `(struct ${mono_ret_name}*)` : `(struct ${mono_ret_name})`;
|
|
18951
|
+
if (wrap_view_string_return) status.code += `({ char* _p = `;
|
|
18078
18952
|
build_node(node.value, status);
|
|
18953
|
+
if (wrap_view_string_return) status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
|
|
18079
18954
|
if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `)`;
|
|
18080
18955
|
status.code += `;\n`;
|
|
18081
18956
|
if (node.value.node_type === "func_call" && node.value.field_overrides?.length) emit_field_overrides("_return_val", node.value, build_node, status, "", ";\n");
|
|
@@ -18289,6 +19164,7 @@ function build_while_loop_node(node, status) {
|
|
|
18289
19164
|
if (!status.string_length_temps) status.string_length_temps = /* @__PURE__ */ new Map();
|
|
18290
19165
|
for (const [name, target] of hoists) {
|
|
18291
19166
|
if (status.string_length_temps.has(name)) continue;
|
|
19167
|
+
if (status.hidden_len_params?.has(c_function_name(name))) continue;
|
|
18292
19168
|
const temp = `_slh_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
18293
19169
|
status.string_length_temps.set(name, temp);
|
|
18294
19170
|
status.code += `const long ${temp} = (long)strlen(`;
|
|
@@ -20131,6 +21007,16 @@ function add_error(status, message, start) {
|
|
|
20131
21007
|
});
|
|
20132
21008
|
}
|
|
20133
21009
|
//#endregion
|
|
21010
|
+
//#region ../src/nodes/set_resolved_function.ts
|
|
21011
|
+
function set_resolved_function(node, func) {
|
|
21012
|
+
Object.defineProperty(node, "resolved_function", {
|
|
21013
|
+
value: func,
|
|
21014
|
+
enumerable: false,
|
|
21015
|
+
writable: true,
|
|
21016
|
+
configurable: true
|
|
21017
|
+
});
|
|
21018
|
+
}
|
|
21019
|
+
//#endregion
|
|
20134
21020
|
//#region ../src/nodes/clone_node.ts
|
|
20135
21021
|
function clone_type$2(type) {
|
|
20136
21022
|
const t = new Type(type.name, type.is_static, type.is_array, type.length ? clone_node(type.length) : void 0);
|
|
@@ -20165,10 +21051,13 @@ function clone_node(node) {
|
|
|
20165
21051
|
c.type_args = n.type_args?.map(clone_type$2);
|
|
20166
21052
|
c.ref_param_indices = n.ref_param_indices?.slice();
|
|
20167
21053
|
c.mov_param_indices = n.mov_param_indices?.slice();
|
|
21054
|
+
c.nullable_param_indices = n.nullable_param_indices?.slice();
|
|
21055
|
+
c.view_param_indices = n.view_param_indices?.slice();
|
|
20168
21056
|
c.swap_params = n.swap_params ? new Map([...n.swap_params].map(([k, v]) => [k, clone_node(v)])) : void 0;
|
|
20169
21057
|
c.variadic_param_name = n.variadic_param_name;
|
|
20170
21058
|
c.variadic_param_index = n.variadic_param_index;
|
|
20171
21059
|
c.is_enum_shorthand = n.is_enum_shorthand;
|
|
21060
|
+
set_resolved_function(c, n.resolved_function);
|
|
20172
21061
|
c.allocations = n.allocations?.map(clone_node);
|
|
20173
21062
|
return c;
|
|
20174
21063
|
}
|
|
@@ -20196,6 +21085,7 @@ function clone_node(node) {
|
|
|
20196
21085
|
c.mangled_name = n.mangled_name;
|
|
20197
21086
|
c.owned_return = n.owned_return;
|
|
20198
21087
|
c.nullable_param_indices = n.nullable_param_indices?.slice();
|
|
21088
|
+
c.view_param_indices = n.view_param_indices?.slice();
|
|
20199
21089
|
c.return_bounds = n.return_bounds ? {
|
|
20200
21090
|
upper: n.return_bounds.upper.slice(),
|
|
20201
21091
|
lower: n.return_bounds.lower.slice(),
|
|
@@ -20207,6 +21097,7 @@ function clone_node(node) {
|
|
|
20207
21097
|
c.function_return_type = n.function_return_type ? clone_type$2(n.function_return_type) : void 0;
|
|
20208
21098
|
c.is_statement = n.is_statement;
|
|
20209
21099
|
c.skip_bounds_check = n.skip_bounds_check;
|
|
21100
|
+
set_resolved_function(c, n.resolved_function);
|
|
20210
21101
|
c.allocations = n.allocations?.map(clone_node);
|
|
20211
21102
|
return c;
|
|
20212
21103
|
}
|
|
@@ -20405,7 +21296,10 @@ function clone_node(node) {
|
|
|
20405
21296
|
c.default_value_start = n.default_value_start;
|
|
20406
21297
|
c.is_variadic = n.is_variadic;
|
|
20407
21298
|
c.is_ref = n.is_ref;
|
|
21299
|
+
c.is_variadic_tuple = n.is_variadic_tuple;
|
|
21300
|
+
c.hidden_len = n.hidden_len;
|
|
20408
21301
|
c.constraint = n.constraint ? clone_node(n.constraint) : void 0;
|
|
21302
|
+
c.stripped_length_equalities = n.stripped_length_equalities?.map((e) => ({ ...e }));
|
|
20409
21303
|
c.func_params = n.func_params?.map((p) => clone_node(p));
|
|
20410
21304
|
c.func_return_type = n.func_return_type ? clone_type$2(n.func_return_type) : void 0;
|
|
20411
21305
|
c.allocations = n.allocations?.map(clone_node);
|
|
@@ -21111,6 +22005,66 @@ function strip_length_equalities(constraint, param_name, status) {
|
|
|
21111
22005
|
});
|
|
21112
22006
|
}
|
|
21113
22007
|
/**
|
|
22008
|
+
* Registration-time variant of strip_length_equalities: removes
|
|
22009
|
+
* `<container>.<size> == <container>.<size>` conjuncts from a parameter
|
|
22010
|
+
* constraint and returns them alongside the remaining constraint. Used when a
|
|
22011
|
+
* function signature is REGISTERED — before any call site can observe it — so
|
|
22012
|
+
* a forward-referenced callee never hands its caller a clause no call site
|
|
22013
|
+
* could prove (declaration order must not matter). Bases are validated
|
|
22014
|
+
* against `known_names` (the function's own parameters) instead of
|
|
22015
|
+
* status.values, because at registration time nothing has been pushed into
|
|
22016
|
+
* any scope yet. Does NOT touch status.equal_lengths: the extracted
|
|
22017
|
+
* equalities are returned for the callee's own param check to seed.
|
|
22018
|
+
*/
|
|
22019
|
+
function extract_length_equalities_at_registration(constraint, param_name, known_names) {
|
|
22020
|
+
const equalities = [];
|
|
22021
|
+
if (!constraint) return {
|
|
22022
|
+
constraint,
|
|
22023
|
+
equalities
|
|
22024
|
+
};
|
|
22025
|
+
const walk = (node) => {
|
|
22026
|
+
if (node.node_type !== "op") return node;
|
|
22027
|
+
const op = node;
|
|
22028
|
+
if (op.op === "&&") {
|
|
22029
|
+
const left = walk(op.left_value);
|
|
22030
|
+
const right = walk(op.right_value);
|
|
22031
|
+
if (left && right) {
|
|
22032
|
+
if (left === op.left_value && right === op.right_value) return node;
|
|
22033
|
+
return new OperationNode(op.start, "&&", left, right, op.type);
|
|
22034
|
+
}
|
|
22035
|
+
return left ?? right;
|
|
22036
|
+
}
|
|
22037
|
+
if (op.op !== "==") return node;
|
|
22038
|
+
const extract = (side) => {
|
|
22039
|
+
if (side.node_type !== "access") return void 0;
|
|
22040
|
+
const access = side;
|
|
22041
|
+
let field;
|
|
22042
|
+
if (access.access.node_type === "access_field") field = access.access.name;
|
|
22043
|
+
else if (access.access.node_type === "access_func") field = access.access.name;
|
|
22044
|
+
if (!field || !NON_NEGATIVE_FIELDS$1.has(field)) return void 0;
|
|
22045
|
+
if (access.target.node_type !== "value") return void 0;
|
|
22046
|
+
return {
|
|
22047
|
+
base: access.target.value,
|
|
22048
|
+
field
|
|
22049
|
+
};
|
|
22050
|
+
};
|
|
22051
|
+
const lhs = extract(op.left_value);
|
|
22052
|
+
const rhs = extract(op.right_value);
|
|
22053
|
+
if (!lhs || !rhs || lhs.field !== rhs.field) return node;
|
|
22054
|
+
if (lhs.base === rhs.base) return node;
|
|
22055
|
+
for (const base of [lhs.base, rhs.base]) if (base !== param_name && !known_names.has(base)) return node;
|
|
22056
|
+
equalities.push({
|
|
22057
|
+
a: lhs.base,
|
|
22058
|
+
b: rhs.base,
|
|
22059
|
+
field: lhs.field
|
|
22060
|
+
});
|
|
22061
|
+
};
|
|
22062
|
+
return {
|
|
22063
|
+
constraint: walk(constraint),
|
|
22064
|
+
equalities
|
|
22065
|
+
};
|
|
22066
|
+
}
|
|
22067
|
+
/**
|
|
21114
22068
|
* Extract flow-sensitive bounds from a comparison condition.
|
|
21115
22069
|
* E.g. `j < list.length` → { var: "j", op: "<", expr: "list.length" }
|
|
21116
22070
|
* Returns undefined if the condition isn't a simple var-vs-expr comparison.
|
|
@@ -22433,6 +23387,7 @@ function is_inside_core_method(status) {
|
|
|
22433
23387
|
return false;
|
|
22434
23388
|
}
|
|
22435
23389
|
function check_function_call(node, status, func, target_type, self_value, self_path) {
|
|
23390
|
+
set_resolved_function(node, func);
|
|
22436
23391
|
const access_scope = status.stack.at(-1);
|
|
22437
23392
|
if (func.name === "#init") {
|
|
22438
23393
|
const struct_name = target_type?.name || func.return_type.name;
|
|
@@ -22498,6 +23453,10 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
22498
23453
|
if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
|
|
22499
23454
|
}
|
|
22500
23455
|
}
|
|
23456
|
+
if (func_param && func_param.type.is_view && !func_param.is_self_param) {
|
|
23457
|
+
if (!node.view_param_indices) node.view_param_indices = [];
|
|
23458
|
+
if (!node.view_param_indices.includes(i)) node.view_param_indices.push(i);
|
|
23459
|
+
}
|
|
22501
23460
|
const old_expected_type = status.expected_type;
|
|
22502
23461
|
const old_allow_null = status.allow_null_value;
|
|
22503
23462
|
if (func_param) {
|
|
@@ -24114,6 +25073,15 @@ function gather_structs(block, status) {
|
|
|
24114
25073
|
func.scope = status.stack.at(-1) || block;
|
|
24115
25074
|
func.return_type = materialize_type(func.return_type, status);
|
|
24116
25075
|
instantiate_generic_type(func.return_type, status);
|
|
25076
|
+
const param_names = new Set(func.params.map((p) => p.name));
|
|
25077
|
+
for (const param of func.params) {
|
|
25078
|
+
if (!param.constraint) continue;
|
|
25079
|
+
const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
|
|
25080
|
+
if (equalities.length) {
|
|
25081
|
+
param.constraint = constraint;
|
|
25082
|
+
param.stripped_length_equalities = equalities;
|
|
25083
|
+
}
|
|
25084
|
+
}
|
|
24117
25085
|
status.functions.push(func);
|
|
24118
25086
|
}
|
|
24119
25087
|
break;
|
|
@@ -24227,6 +25195,10 @@ function check_function_parameter_node(param, status) {
|
|
|
24227
25195
|
is_set: true,
|
|
24228
25196
|
is_null: param.type.is_nullable ? true : void 0
|
|
24229
25197
|
});
|
|
25198
|
+
if (param.stripped_length_equalities?.length) {
|
|
25199
|
+
if (!status.equal_lengths) status.equal_lengths = [];
|
|
25200
|
+
for (const eq of param.stripped_length_equalities) if (!status.equal_lengths.some((e) => e.field === eq.field && (e.a === eq.a && e.b === eq.b || e.a === eq.b && e.b === eq.a))) status.equal_lengths.push(eq);
|
|
25201
|
+
}
|
|
24230
25202
|
if (param.constraint) {
|
|
24231
25203
|
const stripped = strip_length_equalities(param.constraint, param.name, status);
|
|
24232
25204
|
if (stripped !== param.constraint) {
|
|
@@ -24289,6 +25261,15 @@ function check_function_node(func, status) {
|
|
|
24289
25261
|
if (func.checked) return;
|
|
24290
25262
|
func.checked = true;
|
|
24291
25263
|
status.functions.push(func);
|
|
25264
|
+
const param_names = new Set(func.params.map((p) => p.name));
|
|
25265
|
+
for (const param of func.params) {
|
|
25266
|
+
if (!param.constraint) continue;
|
|
25267
|
+
const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
|
|
25268
|
+
if (equalities.length) {
|
|
25269
|
+
param.constraint = constraint;
|
|
25270
|
+
param.stripped_length_equalities = equalities;
|
|
25271
|
+
}
|
|
25272
|
+
}
|
|
24292
25273
|
if (is_generic_func(func)) {
|
|
24293
25274
|
func.is_generic = true;
|
|
24294
25275
|
return;
|
|
@@ -27711,6 +28692,251 @@ function promote_allocations(node, status) {
|
|
|
27711
28692
|
}
|
|
27712
28693
|
}
|
|
27713
28694
|
//#endregion
|
|
28695
|
+
//#region ../src/check/stamp_hidden_string_lens.ts
|
|
28696
|
+
/**
|
|
28697
|
+
* Post-check pass implementing the PERF gap 2.4 fix: thread a string's
|
|
28698
|
+
* length through the call boundary so a per-character helper doesn't pay one
|
|
28699
|
+
* `strlen` per CALL.
|
|
28700
|
+
*
|
|
28701
|
+
* For every function whose Nomen-level body reads `<param>.length` on a
|
|
28702
|
+
* by-value `string` param (and never rebinds, mutates, or escapes it), the
|
|
28703
|
+
* param is stamped `hidden_len`. The backends then lower it as TWO
|
|
28704
|
+
* parameters — the `char*` plus a trailing `long _<name>_len` companion
|
|
28705
|
+
* (mirroring the nullable `_has` companion) — and the body's `.length` reads
|
|
28706
|
+
* become loads of that companion instead of `strlen`. Call sites append the
|
|
28707
|
+
* companion argument, preferring a loop-invariant hoisted strlen temp
|
|
28708
|
+
* (scan_string_length_hoists treats such args like `.length` reads), so a
|
|
28709
|
+
* helper called once per line over a large document costs ONE strlen, not
|
|
28710
|
+
* O(lines).
|
|
28711
|
+
*
|
|
28712
|
+
* Conservative exclusions (any of these keeps the plain one-pointer ABI):
|
|
28713
|
+
* - raw blocks anywhere in the body (opaque code may touch the param), or no
|
|
28714
|
+
* body at all
|
|
28715
|
+
* - `#init`/`#destroy`/`init`/`destroy`/`main` and any non-identifier or
|
|
28716
|
+
* `_`/`#`-prefixed name (operators, compiler-generated helpers) — their
|
|
28717
|
+
* call sites use bespoke emission paths
|
|
28718
|
+
* - variadic params anywhere (their call sites pack hidden count/ptr pairs)
|
|
28719
|
+
* - `is_inline` functions (the aarch64 inline splicer)
|
|
28720
|
+
* - trait method declarations/default bodies, and struct methods that
|
|
28721
|
+
* implement a trait method (vtable dispatch uses the trait's declared
|
|
28722
|
+
* signature)
|
|
28723
|
+
* - functions referenced as VALUES anywhere in the program (`var func f =
|
|
28724
|
+
* helper`, `obj.method` as a value, nursery.spawn targets) — indirect
|
|
28725
|
+
* calls use the declared ABI
|
|
28726
|
+
* - params that are ref/mov/view/nullable/variadic/self, or that the body
|
|
28727
|
+
* assigns to, passes ref/mov/swap, mutates through a `var self`/`ref self`
|
|
28728
|
+
* string method, or references across a nested func/async boundary, or
|
|
28729
|
+
* whose companion name `_<name>_len` collides with any declared name in
|
|
28730
|
+
* the body
|
|
28731
|
+
*/
|
|
28732
|
+
const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
28733
|
+
"true",
|
|
28734
|
+
"false",
|
|
28735
|
+
"null",
|
|
28736
|
+
"self",
|
|
28737
|
+
"as",
|
|
28738
|
+
"default"
|
|
28739
|
+
]);
|
|
28740
|
+
function is_plain_identifier(value) {
|
|
28741
|
+
if (!value) return false;
|
|
28742
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) return false;
|
|
28743
|
+
if (value.startsWith("_")) return false;
|
|
28744
|
+
return !NON_IDENTIFIERS.has(value);
|
|
28745
|
+
}
|
|
28746
|
+
function root_name(node) {
|
|
28747
|
+
if (!node) return void 0;
|
|
28748
|
+
if (node.node_type === "value") {
|
|
28749
|
+
const v = node.value;
|
|
28750
|
+
return is_plain_identifier(v) ? v : void 0;
|
|
28751
|
+
}
|
|
28752
|
+
if (node.node_type === "access") return root_name(node.target);
|
|
28753
|
+
}
|
|
28754
|
+
function collect_functions(root, status) {
|
|
28755
|
+
const out = [];
|
|
28756
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28757
|
+
const add = (func, struct, is_trait_func) => {
|
|
28758
|
+
if (seen.has(func)) return;
|
|
28759
|
+
seen.add(func);
|
|
28760
|
+
out.push({
|
|
28761
|
+
func,
|
|
28762
|
+
struct,
|
|
28763
|
+
is_trait_func
|
|
28764
|
+
});
|
|
28765
|
+
};
|
|
28766
|
+
for (const struct of status.structs) for (const func of struct.functions) add(func, struct);
|
|
28767
|
+
for (const trait of status.traits) for (const func of trait.functions) add(func, void 0, true);
|
|
28768
|
+
for (const func of status.functions) add(func);
|
|
28769
|
+
walk_tree(root, (n) => {
|
|
28770
|
+
if (n.node_type === "struct") {
|
|
28771
|
+
const s = n;
|
|
28772
|
+
for (const func of s.functions) add(func, s);
|
|
28773
|
+
} else if (n.node_type === "trait") {
|
|
28774
|
+
const t = n;
|
|
28775
|
+
for (const func of t.functions) add(func, void 0, true);
|
|
28776
|
+
} else if (n.node_type === "func") add(n);
|
|
28777
|
+
});
|
|
28778
|
+
return out;
|
|
28779
|
+
}
|
|
28780
|
+
/** Names of functions referenced as VALUES (func-typed, non-call nodes). */
|
|
28781
|
+
function collect_value_referenced_names(root, status) {
|
|
28782
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
28783
|
+
const visit = (n) => {
|
|
28784
|
+
if (n.type?.name !== "func") return;
|
|
28785
|
+
if (n.node_type === "value") {
|
|
28786
|
+
const v = n.value;
|
|
28787
|
+
if (is_plain_identifier(v)) excluded.add(v);
|
|
28788
|
+
} else if (n.node_type === "access_field") {
|
|
28789
|
+
const name = n.name;
|
|
28790
|
+
if (is_plain_identifier(name)) excluded.add(name);
|
|
28791
|
+
}
|
|
28792
|
+
};
|
|
28793
|
+
walk_tree(root, visit);
|
|
28794
|
+
for (const struct of status.structs) walk_tree(struct, visit);
|
|
28795
|
+
for (const trait of status.traits) walk_tree(trait, visit);
|
|
28796
|
+
for (const func of status.functions) walk_tree(func, visit);
|
|
28797
|
+
return excluded;
|
|
28798
|
+
}
|
|
28799
|
+
function walk_tree(node, visit) {
|
|
28800
|
+
if (!node || typeof node !== "object") return;
|
|
28801
|
+
if (Array.isArray(node)) {
|
|
28802
|
+
for (const item of node) walk_tree(item, visit);
|
|
28803
|
+
return;
|
|
28804
|
+
}
|
|
28805
|
+
const n = node;
|
|
28806
|
+
if (typeof n.node_type !== "string") return;
|
|
28807
|
+
visit(n);
|
|
28808
|
+
const record = n;
|
|
28809
|
+
for (const key of Object.keys(record)) {
|
|
28810
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
28811
|
+
walk_tree(record[key], visit);
|
|
28812
|
+
}
|
|
28813
|
+
}
|
|
28814
|
+
function stamp_hidden_string_lens(root, status) {
|
|
28815
|
+
const value_referenced = collect_value_referenced_names(root, status);
|
|
28816
|
+
const owners = collect_functions(root, status);
|
|
28817
|
+
const string_struct = status.structs.find((s) => s.name === "string");
|
|
28818
|
+
for (const { func, struct, is_trait_func } of owners) {
|
|
28819
|
+
if (func.hidden_len_stamped) continue;
|
|
28820
|
+
func.hidden_len_stamped = true;
|
|
28821
|
+
if (!func.has_body || func.statements.length === 0) continue;
|
|
28822
|
+
if (func.is_inline || is_trait_func) continue;
|
|
28823
|
+
const bare_name = func.name.replace(/^#/, "");
|
|
28824
|
+
if (!is_plain_identifier(func.name)) continue;
|
|
28825
|
+
if ([
|
|
28826
|
+
"main",
|
|
28827
|
+
"init",
|
|
28828
|
+
"destroy"
|
|
28829
|
+
].includes(bare_name)) continue;
|
|
28830
|
+
if (value_referenced.has(func.name)) continue;
|
|
28831
|
+
if (func.params.some((p) => p.is_variadic || p.is_variadic_tuple)) continue;
|
|
28832
|
+
if (struct && trait_declares_method(struct, func.name, status)) continue;
|
|
28833
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
28834
|
+
for (const param of func.params) if (param_is_hidden_len_candidate(param)) candidates.add(param.name);
|
|
28835
|
+
if (candidates.size === 0) continue;
|
|
28836
|
+
const reads = /* @__PURE__ */ new Set();
|
|
28837
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
28838
|
+
const declared_names = /* @__PURE__ */ new Set();
|
|
28839
|
+
let has_raw = false;
|
|
28840
|
+
const visit_children = (n, in_boundary) => {
|
|
28841
|
+
const record = n;
|
|
28842
|
+
for (const key of Object.keys(record)) {
|
|
28843
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
28844
|
+
const child = record[key];
|
|
28845
|
+
if (Array.isArray(child)) {
|
|
28846
|
+
for (const item of child) if (item && typeof item === "object" && item.node_type) visit(item, in_boundary);
|
|
28847
|
+
} else if (child && typeof child === "object" && child.node_type) visit(child, in_boundary);
|
|
28848
|
+
}
|
|
28849
|
+
};
|
|
28850
|
+
const visit = (n, in_boundary) => {
|
|
28851
|
+
switch (n.node_type) {
|
|
28852
|
+
case "raw":
|
|
28853
|
+
has_raw = true;
|
|
28854
|
+
return;
|
|
28855
|
+
case "func":
|
|
28856
|
+
case "async_block":
|
|
28857
|
+
visit_children(n, true);
|
|
28858
|
+
return;
|
|
28859
|
+
case "value": {
|
|
28860
|
+
const v = n.value;
|
|
28861
|
+
if (in_boundary && is_plain_identifier(v)) invalidated.add(v);
|
|
28862
|
+
return;
|
|
28863
|
+
}
|
|
28864
|
+
case "access": {
|
|
28865
|
+
const access = n;
|
|
28866
|
+
if (!in_boundary && access.access.node_type === "access_field" && access.access.name === "length" && access.target.node_type === "value") {
|
|
28867
|
+
const name = access.target.value;
|
|
28868
|
+
if (name !== void 0 && candidates.has(name)) reads.add(name);
|
|
28869
|
+
}
|
|
28870
|
+
if (!in_boundary && access.access.node_type === "access_func" && access.target.node_type === "value") {
|
|
28871
|
+
const name = access.target.value;
|
|
28872
|
+
const call = access.access;
|
|
28873
|
+
if (name !== void 0 && candidates.has(name)) {
|
|
28874
|
+
const self_param = (string_struct?.functions.find((f) => f.name === call.name || f.name === `#${call.name}`))?.params?.[0];
|
|
28875
|
+
if (self_param?.is_self_param && (self_param.declaration === "var" || self_param.is_ref)) invalidated.add(name);
|
|
28876
|
+
}
|
|
28877
|
+
}
|
|
28878
|
+
visit_children(n, in_boundary);
|
|
28879
|
+
return;
|
|
28880
|
+
}
|
|
28881
|
+
case "assign": {
|
|
28882
|
+
const assign = n;
|
|
28883
|
+
const lhs = root_name(assign.left_value);
|
|
28884
|
+
if (lhs) invalidated.add(lhs);
|
|
28885
|
+
const swap = root_name(assign.swap);
|
|
28886
|
+
if (swap) invalidated.add(swap);
|
|
28887
|
+
visit_children(n, in_boundary);
|
|
28888
|
+
return;
|
|
28889
|
+
}
|
|
28890
|
+
case "declare":
|
|
28891
|
+
case "param": {
|
|
28892
|
+
const name = n.name;
|
|
28893
|
+
if (name) declared_names.add(name);
|
|
28894
|
+
visit_children(n, in_boundary);
|
|
28895
|
+
return;
|
|
28896
|
+
}
|
|
28897
|
+
case "for": {
|
|
28898
|
+
const item = n.item;
|
|
28899
|
+
if (item?.value) declared_names.add(item.value);
|
|
28900
|
+
visit_children(n, in_boundary);
|
|
28901
|
+
return;
|
|
28902
|
+
}
|
|
28903
|
+
case "func_call":
|
|
28904
|
+
case "access_func": {
|
|
28905
|
+
const call = n;
|
|
28906
|
+
for (const indices of [call.ref_param_indices, call.mov_param_indices]) for (const i of indices ?? []) {
|
|
28907
|
+
const arg = root_name(call.params?.[i]);
|
|
28908
|
+
if (arg) invalidated.add(arg);
|
|
28909
|
+
}
|
|
28910
|
+
if (call.swap_params) {
|
|
28911
|
+
for (const [i, swap] of call.swap_params) for (const arg of [root_name(call.params?.[i]), root_name(swap)]) if (arg) invalidated.add(arg);
|
|
28912
|
+
}
|
|
28913
|
+
visit_children(n, in_boundary);
|
|
28914
|
+
return;
|
|
28915
|
+
}
|
|
28916
|
+
default:
|
|
28917
|
+
visit_children(n, in_boundary);
|
|
28918
|
+
return;
|
|
28919
|
+
}
|
|
28920
|
+
};
|
|
28921
|
+
for (const stmt of func.statements) visit(stmt, false);
|
|
28922
|
+
if (has_raw) continue;
|
|
28923
|
+
for (const name of candidates) {
|
|
28924
|
+
if (!reads.has(name)) continue;
|
|
28925
|
+
if (invalidated.has(name)) continue;
|
|
28926
|
+
if (declared_names.has(`_${name}_len`)) continue;
|
|
28927
|
+
const param = func.params.find((p) => p.name === name);
|
|
28928
|
+
param.hidden_len = true;
|
|
28929
|
+
}
|
|
28930
|
+
}
|
|
28931
|
+
}
|
|
28932
|
+
function trait_declares_method(struct, method_name, status) {
|
|
28933
|
+
for (const trait_name of struct.traits) if (status.traits.find((t) => t.name === trait_name)?.functions.some((f) => f.name === method_name)) return true;
|
|
28934
|
+
return false;
|
|
28935
|
+
}
|
|
28936
|
+
function param_is_hidden_len_candidate(param) {
|
|
28937
|
+
return !param.is_self_param && !param.is_variadic && !param.is_variadic_tuple && !param.is_ref && !param.is_moved && param.type.name === "string" && !param.type.is_array && !param.type.is_ref && !param.type.is_view && !param.type.is_nullable;
|
|
28938
|
+
}
|
|
28939
|
+
//#endregion
|
|
27714
28940
|
//#region ../src/check/warnings.ts
|
|
27715
28941
|
/**
|
|
27716
28942
|
* Lint pass run after a successful check. It walks the AST to surface three
|
|
@@ -27913,6 +29139,7 @@ function check(root) {
|
|
|
27913
29139
|
mutated_local_names: /* @__PURE__ */ new Set()
|
|
27914
29140
|
};
|
|
27915
29141
|
check_node(root, status);
|
|
29142
|
+
stamp_hidden_string_lens(root, status);
|
|
27916
29143
|
if (!status.errors.length) emit_warnings(root, status);
|
|
27917
29144
|
return {
|
|
27918
29145
|
ok: !status.errors.length,
|