nomen-lang 0.0.17 → 0.0.19
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 +21 -1
- 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 +1366 -79
- 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
|
|
@@ -2673,6 +2750,20 @@ function is_container_borrow_access(node) {
|
|
|
2673
2750
|
return !!node && node.node_type === "access" && node.access?.node_type === "access_func" && !node.access.owned_return && is_container_borrow_accessor_name(node.access.name);
|
|
2674
2751
|
}
|
|
2675
2752
|
/**
|
|
2753
|
+
* Whether `target` (the receiver of a `.to_string()` call) is a `view string`
|
|
2754
|
+
* VALUE — a named view local/param (the checker stamps variable references
|
|
2755
|
+
* with the declared type, and substitute_type preserves `is_view` across
|
|
2756
|
+
* monomorphization) or an inline `.slice(...)` chain (a slice call always
|
|
2757
|
+
* produces a view, even when its cached type lost the modifier). Such a call
|
|
2758
|
+
* MATERIALIZES a fresh owned heap copy (malloc + memcpy), unlike
|
|
2759
|
+
* `to_string` on an owned string (the `string_to_string` identity).
|
|
2760
|
+
*/
|
|
2761
|
+
function is_view_materialization_target(target) {
|
|
2762
|
+
if (!target || typeof target !== "object") return false;
|
|
2763
|
+
if (target.type?.is_view) return true;
|
|
2764
|
+
return target.node_type === "access" && target.access?.node_type === "access_func" && !target.access.owned_return && target.access.name === "slice";
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2676
2767
|
* Whether a function has any `return <expr>` statement in its body (used by
|
|
2677
2768
|
* the C backend's gather, which registers every string-returning function
|
|
2678
2769
|
* that actually returns — the backend strdup's borrows at the return site,
|
|
@@ -2781,6 +2872,7 @@ function value_is_owned_string(v, table, visiting, borrow_names, enclosing_fn) {
|
|
|
2781
2872
|
if (mangled.startsWith("_string_interpolate_")) return true;
|
|
2782
2873
|
if (mangled.endsWith("_to_string") && mangled !== "string_to_string") return true;
|
|
2783
2874
|
if (raw === "to_string" && v.target?.type?.name && v.target.type.name !== "string") return true;
|
|
2875
|
+
if (raw === "to_string" && is_view_materialization_target(v.target)) return true;
|
|
2784
2876
|
if (!v.access.owned_return && is_container_borrow_accessor_name(raw)) return !is_call_site_borrow_accessor(enclosing_fn);
|
|
2785
2877
|
const resolved = method_call_returns_owned(v, table, visiting);
|
|
2786
2878
|
if (resolved !== void 0) return resolved;
|
|
@@ -4396,6 +4488,11 @@ function build_function_node$1(node, status) {
|
|
|
4396
4488
|
first_pass_slot += 1;
|
|
4397
4489
|
continue;
|
|
4398
4490
|
}
|
|
4491
|
+
if (param.type.is_view) {
|
|
4492
|
+
first_pass_slot += 2;
|
|
4493
|
+
continue;
|
|
4494
|
+
}
|
|
4495
|
+
if (param.hidden_len) first_pass_slot++;
|
|
4399
4496
|
const is_struct_type = !!status.structs.find((s) => s.name === param.type.name && !s.is_simple_type);
|
|
4400
4497
|
const is_trait_type = !!status.traits.find((t) => t.name === param.type.name);
|
|
4401
4498
|
const is_enum_with_data = !!status.enums.find((e) => e.name === param.type.name && e.has_associated_data);
|
|
@@ -4433,7 +4530,11 @@ function build_function_node$1(node, status) {
|
|
|
4433
4530
|
status.ref_class_slots = /* @__PURE__ */ new Map();
|
|
4434
4531
|
status.function_struct_param_slots = /* @__PURE__ */ new Set();
|
|
4435
4532
|
const old_variadic_params_aarch64 = status.function_variadic_params;
|
|
4533
|
+
const old_view_params = status.function_view_params;
|
|
4436
4534
|
status.function_variadic_params = /* @__PURE__ */ new Set();
|
|
4535
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
4536
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
4537
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
4437
4538
|
status.moved_class_params = /* @__PURE__ */ new Map();
|
|
4438
4539
|
let moved_param_save_slots = /* @__PURE__ */ new Map();
|
|
4439
4540
|
if (has_body) {
|
|
@@ -4454,6 +4555,22 @@ function build_function_node$1(node, status) {
|
|
|
4454
4555
|
}
|
|
4455
4556
|
param_idx++;
|
|
4456
4557
|
}
|
|
4558
|
+
if (param.type.is_view) {
|
|
4559
|
+
const offset = allocate_stack_space(status, 16, 16);
|
|
4560
|
+
status.stack_offsets.set(param.name, offset);
|
|
4561
|
+
status.function_view_params.add(param.name);
|
|
4562
|
+
for (const half of [0, 1]) {
|
|
4563
|
+
const p_slot = param_idx + half;
|
|
4564
|
+
if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
|
|
4565
|
+
else {
|
|
4566
|
+
const k = p_slot - 8;
|
|
4567
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
|
|
4568
|
+
status.code += `str x9, [x29, #${offset + half * 8}]\n`;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
param_idx += 2;
|
|
4572
|
+
continue;
|
|
4573
|
+
}
|
|
4457
4574
|
if (callee_map.has(param.name)) {
|
|
4458
4575
|
const reg = callee_map.get(param.name);
|
|
4459
4576
|
status.function_param_regs.set(param.name, reg);
|
|
@@ -4487,6 +4604,19 @@ function build_function_node$1(node, status) {
|
|
|
4487
4604
|
}
|
|
4488
4605
|
}
|
|
4489
4606
|
param_idx++;
|
|
4607
|
+
if (param.hidden_len) {
|
|
4608
|
+
const len_offset = allocate_stack_space(status, 8, 8);
|
|
4609
|
+
status.stack_offsets.set(`_${param.name}_len`, len_offset);
|
|
4610
|
+
if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
4611
|
+
status.hidden_len_params.add(param.name);
|
|
4612
|
+
if (param_idx < 8) status.code += `str ${param_regs[param_idx]}, [x29, #${len_offset}]\n`;
|
|
4613
|
+
else {
|
|
4614
|
+
const k = param_idx - 8;
|
|
4615
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(node.name, k)}]\n`;
|
|
4616
|
+
status.code += `str x9, [x29, #${len_offset}]\n`;
|
|
4617
|
+
}
|
|
4618
|
+
param_idx++;
|
|
4619
|
+
}
|
|
4490
4620
|
if (param.declaration === "var") status.function_param_vars.add(param.name);
|
|
4491
4621
|
if (param.type.is_array) {
|
|
4492
4622
|
if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
|
|
@@ -4502,6 +4632,7 @@ function build_function_node$1(node, status) {
|
|
|
4502
4632
|
for (let i = 0; i < node.params.length; i++) {
|
|
4503
4633
|
const param = node.params[i];
|
|
4504
4634
|
if (param.is_variadic) pidx++;
|
|
4635
|
+
if (param.hidden_len) pidx++;
|
|
4505
4636
|
if (param.is_moved) {
|
|
4506
4637
|
if (!!status.structs.find((s) => s.name === param.type.name && s.is_class)) {
|
|
4507
4638
|
const reg = callee_map.get(param.name) ?? param_regs[pidx];
|
|
@@ -4627,6 +4758,8 @@ function build_function_node$1(node, status) {
|
|
|
4627
4758
|
status.ref_class_slots = old_ref_class_slots;
|
|
4628
4759
|
status.function_struct_param_slots = old_struct_param_slots;
|
|
4629
4760
|
status.function_variadic_params = old_variadic_params_aarch64;
|
|
4761
|
+
status.function_view_params = old_view_params;
|
|
4762
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
4630
4763
|
status.function_return_label = old_return_label;
|
|
4631
4764
|
status.struct_return_buffer = void 0;
|
|
4632
4765
|
status.return_buffer_stack_offset = void 0;
|
|
@@ -6314,6 +6447,108 @@ function build_swap_params(node, status) {
|
|
|
6314
6447
|
}
|
|
6315
6448
|
}
|
|
6316
6449
|
//#endregion
|
|
6450
|
+
//#region ../src/build_aarch64/utils/view_value.ts
|
|
6451
|
+
/**
|
|
6452
|
+
* Resolve the struct name of an expression's value by walking the access
|
|
6453
|
+
* chain through the (monomorphized) struct table — bare names via
|
|
6454
|
+
* variable_types / scoped declarations / `self`, field accesses via the
|
|
6455
|
+
* base struct's field type, method calls via the method's return type.
|
|
6456
|
+
* Fallback for cached node types that lost their modifiers (generic bodies
|
|
6457
|
+
* re-checked after monomorphization can carry a stale plain `string`).
|
|
6458
|
+
*/
|
|
6459
|
+
function value_struct_name$1(node, status) {
|
|
6460
|
+
if (node.node_type === "value") {
|
|
6461
|
+
const name = node.value;
|
|
6462
|
+
if (name === "self" && status.current_struct) return status.current_struct.name;
|
|
6463
|
+
const vt = status.variable_types?.get(name);
|
|
6464
|
+
if (vt?.name) return vt.name;
|
|
6465
|
+
const decl = status.scoped_declarations.findLast((d) => d.name === name);
|
|
6466
|
+
if (decl?.type?.name) return decl.type.name;
|
|
6467
|
+
return;
|
|
6468
|
+
}
|
|
6469
|
+
if (node.node_type === "access") {
|
|
6470
|
+
const inner = node.access;
|
|
6471
|
+
const base = value_struct_name$1(node.target, status);
|
|
6472
|
+
if (!base) return void 0;
|
|
6473
|
+
const struct = status.structs.find((s) => s.name === base && !s.is_generic);
|
|
6474
|
+
if (!struct) return void 0;
|
|
6475
|
+
if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
|
|
6476
|
+
if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
/**
|
|
6480
|
+
* Whether an expression is a `view T` VALUE — a (ptr, len) pair that can be
|
|
6481
|
+
* passed to a `view T` parameter unchanged (loaded from the value's two
|
|
6482
|
+
* stack slots, or produced directly by a view-returning call). Recovers
|
|
6483
|
+
* `is_view` from the cached node type, the declared variable type, the
|
|
6484
|
+
* current function's view params, or (for method calls whose cached type
|
|
6485
|
+
* lost the modifier) the callee's declared return type.
|
|
6486
|
+
*/
|
|
6487
|
+
function is_view_value$1(node, status) {
|
|
6488
|
+
if (type_from_value_node$1(node)?.is_view) return true;
|
|
6489
|
+
if (node.node_type === "value") {
|
|
6490
|
+
const name = node.value;
|
|
6491
|
+
if (status.variable_types?.get(name)?.is_view) return true;
|
|
6492
|
+
if (status.function_view_params?.has(name)) return true;
|
|
6493
|
+
}
|
|
6494
|
+
if (node.node_type === "access" && node.access.node_type === "access_func") {
|
|
6495
|
+
const access = node.access;
|
|
6496
|
+
const recv_struct = value_struct_name$1(node.target, status);
|
|
6497
|
+
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;
|
|
6498
|
+
}
|
|
6499
|
+
return false;
|
|
6500
|
+
}
|
|
6501
|
+
/**
|
|
6502
|
+
* Emit the (ptr, len) pair for a `view string` argument into x0/x1.
|
|
6503
|
+
* - A view VALUE (a view local/param's two stack slots, or a view-returning
|
|
6504
|
+
* call that leaves the pair in x0/x1) passes through unchanged.
|
|
6505
|
+
* - An owned string expression builds its pointer into x0, then measures it
|
|
6506
|
+
* with strlen — an implicit owned→view borrow; ownership stays with the
|
|
6507
|
+
* caller.
|
|
6508
|
+
*/
|
|
6509
|
+
function emit_view_string_arg(arg, status) {
|
|
6510
|
+
if (is_view_value$1(arg, status)) {
|
|
6511
|
+
if (arg.node_type === "value") {
|
|
6512
|
+
const base = status.stack_offsets?.get(arg.value);
|
|
6513
|
+
if (base !== void 0) {
|
|
6514
|
+
status.code += `ldr x0, [x29, #${base}]\n`;
|
|
6515
|
+
status.code += `ldr x1, [x29, #${base + 8}]\n`;
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6519
|
+
build_node$1(arg, status);
|
|
6520
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6521
|
+
return;
|
|
6522
|
+
}
|
|
6523
|
+
build_node$1(arg, status);
|
|
6524
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6525
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
6526
|
+
status.code += `bl _strlen\n`;
|
|
6527
|
+
status.code += `mov x1, x0\n`;
|
|
6528
|
+
status.code += `ldr x0, [sp], #16\n`;
|
|
6529
|
+
}
|
|
6530
|
+
/**
|
|
6531
|
+
* Materialize a `view string` into an OWNED heap string. The (ptr, len)
|
|
6532
|
+
* pair must be in x0/x1 on entry; on exit x0 holds a malloc'd, null-
|
|
6533
|
+
* terminated copy of exactly len bytes (and the caller must reclaim it —
|
|
6534
|
+
* set last_result_is_heap / mark the target as a heap string). Mirrors the
|
|
6535
|
+
* view `.to_string` builtin sequence.
|
|
6536
|
+
*/
|
|
6537
|
+
function emit_view_materialize_owned(status) {
|
|
6538
|
+
status.code += `stp x0, x1, [sp, #-16]!\n`;
|
|
6539
|
+
status.code += `add x0, x1, #1\n`;
|
|
6540
|
+
emit_malloc(status);
|
|
6541
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
6542
|
+
status.code += `ldr x1, [sp, #16]\n`;
|
|
6543
|
+
status.code += `ldr x2, [sp, #24]\n`;
|
|
6544
|
+
status.code += `ldr x0, [sp]\n`;
|
|
6545
|
+
status.code += `bl _memcpy\n`;
|
|
6546
|
+
status.code += `ldr x0, [sp]\n`;
|
|
6547
|
+
status.code += `ldr x1, [sp, #24]\n`;
|
|
6548
|
+
status.code += `strb wzr, [x0, x1]\n`;
|
|
6549
|
+
status.code += `add sp, sp, #32\n`;
|
|
6550
|
+
}
|
|
6551
|
+
//#endregion
|
|
6317
6552
|
//#region ../src/build_aarch64/build_declaration_node.ts
|
|
6318
6553
|
/**
|
|
6319
6554
|
* Pass a struct-typed param by address. For ValueNode (variable references)
|
|
@@ -6476,10 +6711,13 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
6476
6711
|
const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
|
|
6477
6712
|
const elem_size = elem_struct ? get_struct_size(elem_type_name, status) : 8;
|
|
6478
6713
|
const count = arr.values.length;
|
|
6479
|
-
const arr_offset = allocate_stack_space(status, Math.max(count, 1) * elem_size, 16);
|
|
6714
|
+
const arr_offset = allocate_stack_space(status, 8 + Math.max(count, 1) * elem_size, 16);
|
|
6715
|
+
const data_base = arr_offset + 8;
|
|
6716
|
+
status.code += `mov x0, #${count}\n`;
|
|
6717
|
+
status.code += `str x0, [x29, #${arr_offset}]\n`;
|
|
6480
6718
|
for (let j = count - 1; j >= 0; j--) {
|
|
6481
6719
|
const arg = arr.values[j];
|
|
6482
|
-
const slot_offset =
|
|
6720
|
+
const slot_offset = data_base + j * elem_size;
|
|
6483
6721
|
if (elem_struct && arg.node_type === "func_call") {
|
|
6484
6722
|
const tfc = arg;
|
|
6485
6723
|
const fc_param_regs = [
|
|
@@ -6539,7 +6777,7 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
6539
6777
|
emit_int_immediate(status, String(count));
|
|
6540
6778
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
6541
6779
|
status.code += `str x0, [x29, #${count_slot}]\n`;
|
|
6542
|
-
status.code += `add x0, x29, #${
|
|
6780
|
+
status.code += `add x0, x29, #${data_base}\n`;
|
|
6543
6781
|
status.code += `str x0, [x29, #${ptr_slot}]\n`;
|
|
6544
6782
|
const reg_slots = Math.min(total_slots, 7);
|
|
6545
6783
|
for (let i = 0; i < reg_slots; i++) status.code += `ldr ${param_regs[i]}, [x29, #${slots_base + i * 8}]\n`;
|
|
@@ -7414,10 +7652,18 @@ function build_declaration_node$1(node, status) {
|
|
|
7414
7652
|
const value_node = node.value;
|
|
7415
7653
|
const raw = get_raw_value$1(value_node);
|
|
7416
7654
|
const is_literal = /^(\+|-)?\d+(\.\d+)?$/.test(raw) || raw.startsWith("\"") || raw === "true" || raw === "false";
|
|
7417
|
-
|
|
7655
|
+
const use_stack = !!status.function_return_label;
|
|
7656
|
+
const is_heap_alias = node.type.name === "string" && !is_literal && status.heap_strings?.has(raw);
|
|
7657
|
+
if (use_stack) {
|
|
7418
7658
|
const offset = allocate_stack_space(status, size, size);
|
|
7419
7659
|
status.stack_offsets.set(node.name, offset);
|
|
7420
|
-
if (node.type.name === "string" && !
|
|
7660
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
|
|
7661
|
+
emit_view_string_arg(node.value, status);
|
|
7662
|
+
emit_view_materialize_owned(status);
|
|
7663
|
+
status.code += `str x0, [x29, #${offset}]\n`;
|
|
7664
|
+
mark_heap_string(status, node.name);
|
|
7665
|
+
status.last_result_is_heap = false;
|
|
7666
|
+
} else if (is_heap_alias) {
|
|
7421
7667
|
emit_var_load(status, "x0", raw, 8);
|
|
7422
7668
|
emit_strdup(status);
|
|
7423
7669
|
status.code += `str x0, [x29, #${offset}]\n`;
|
|
@@ -7570,6 +7816,14 @@ function build_declaration_node$1(node, status) {
|
|
|
7570
7816
|
const offset = allocate_stack_space(status, size, size);
|
|
7571
7817
|
status.stack_offsets.set(node.name, offset);
|
|
7572
7818
|
} else emit_data(status, `${node.name}: .space ${size}\n`);
|
|
7819
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value$1(node.value, status)) {
|
|
7820
|
+
emit_view_string_arg(node.value, status);
|
|
7821
|
+
emit_view_materialize_owned(status);
|
|
7822
|
+
emit_var_store(status, "x0", node.name, size);
|
|
7823
|
+
mark_heap_string(status, node.name);
|
|
7824
|
+
status.last_result_is_heap = false;
|
|
7825
|
+
return;
|
|
7826
|
+
}
|
|
7573
7827
|
build_node$1(node.value, status);
|
|
7574
7828
|
emit_var_store(status, "x0", node.name, size);
|
|
7575
7829
|
check_heap();
|
|
@@ -8292,10 +8546,13 @@ function build_function_call_node$1(node, status) {
|
|
|
8292
8546
|
const elem_type_name = arr.type.name || "int";
|
|
8293
8547
|
const elem_struct = status.structs.find((s) => s.name === elem_type_name && !s.is_simple_type);
|
|
8294
8548
|
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);
|
|
8549
|
+
const arr_offset = allocate_stack_space(status, 8 + Math.max(arr.values.length, 1) * elem_size, 16);
|
|
8550
|
+
const data_base = arr_offset + 8;
|
|
8551
|
+
status.code += `mov x0, #${arr.values.length}\n`;
|
|
8552
|
+
status.code += `str x0, [x29, #${arr_offset}]\n`;
|
|
8296
8553
|
for (let j = arr.values.length - 1; j >= 0; j--) {
|
|
8297
8554
|
const arg = arr.values[j];
|
|
8298
|
-
const slot_offset =
|
|
8555
|
+
const slot_offset = data_base + j * elem_size;
|
|
8299
8556
|
if (elem_struct && arg.node_type === "func_call") {
|
|
8300
8557
|
const fc = arg;
|
|
8301
8558
|
const fc_param_regs = [
|
|
@@ -8338,16 +8595,29 @@ function build_function_call_node$1(node, status) {
|
|
|
8338
8595
|
non_variadic.push({
|
|
8339
8596
|
node: param,
|
|
8340
8597
|
is_struct: is_struct_type$2(param_type, status) || is_enum_with_data_type$1(param_type, status),
|
|
8341
|
-
is_ref: !!is_ref_param
|
|
8598
|
+
is_ref: !!is_ref_param,
|
|
8599
|
+
is_view: !!node.view_param_indices?.includes(i)
|
|
8342
8600
|
});
|
|
8343
8601
|
}
|
|
8344
8602
|
const nv = non_variadic.length;
|
|
8345
|
-
const
|
|
8603
|
+
const nv_slot = [];
|
|
8604
|
+
let nv_total = 0;
|
|
8605
|
+
for (let i = 0; i < nv; i++) {
|
|
8606
|
+
nv_slot.push(nv_total);
|
|
8607
|
+
nv_total += non_variadic[i].is_view ? 2 : 1;
|
|
8608
|
+
}
|
|
8609
|
+
const total_slots = nv_total + 2;
|
|
8346
8610
|
const slots_base = allocate_stack_space(status, total_slots * 8, 16);
|
|
8347
|
-
const count_slot = slots_base +
|
|
8611
|
+
const count_slot = slots_base + nv_total * 8;
|
|
8348
8612
|
const ptr_slot = count_slot + 8;
|
|
8349
8613
|
for (let i = nv - 1; i >= 0; i--) {
|
|
8350
8614
|
const ep = non_variadic[i];
|
|
8615
|
+
if (ep.is_view) {
|
|
8616
|
+
emit_view_string_arg(ep.node, status);
|
|
8617
|
+
status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
|
|
8618
|
+
status.code += `str x1, [x29, #${slots_base + (nv_slot[i] + 1) * 8}]\n`;
|
|
8619
|
+
continue;
|
|
8620
|
+
}
|
|
8351
8621
|
if (ep.is_struct) emit_struct_address(ep.node, status);
|
|
8352
8622
|
else if (ep.is_ref) {
|
|
8353
8623
|
const ep_name = ep.node.node_type === "value" ? ep.node.value : void 0;
|
|
@@ -8356,11 +8626,11 @@ function build_function_call_node$1(node, status) {
|
|
|
8356
8626
|
else emit_address_of(ep.node, status);
|
|
8357
8627
|
} else build_node$1(ep.node, status);
|
|
8358
8628
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
8359
|
-
status.code += `str x0, [x29, #${slots_base + i * 8}]\n`;
|
|
8629
|
+
status.code += `str x0, [x29, #${slots_base + nv_slot[i] * 8}]\n`;
|
|
8360
8630
|
}
|
|
8361
8631
|
status.code += `mov x0, #${arr.values.length}\n`;
|
|
8362
8632
|
status.code += `str x0, [x29, #${count_slot}]\n`;
|
|
8363
|
-
status.code += `add x0, x29, #${
|
|
8633
|
+
status.code += `add x0, x29, #${data_base}\n`;
|
|
8364
8634
|
status.code += `str x0, [x29, #${ptr_slot}]\n`;
|
|
8365
8635
|
for (let s = 0; s < total_slots; s++) {
|
|
8366
8636
|
const slot = start_reg + s;
|
|
@@ -8381,13 +8651,33 @@ function build_function_call_node$1(node, status) {
|
|
|
8381
8651
|
}
|
|
8382
8652
|
}
|
|
8383
8653
|
} else {
|
|
8384
|
-
const
|
|
8654
|
+
const view_arg_set = new Set(node.view_param_indices ?? []);
|
|
8655
|
+
const hidden_len_indices = callee_hidden_len_indices(node);
|
|
8656
|
+
const hidden_len_set = new Set(hidden_len_indices);
|
|
8657
|
+
const hidden_len_slots = /* @__PURE__ */ new Map();
|
|
8658
|
+
const arg_slot = [];
|
|
8659
|
+
let total_slots = 0;
|
|
8660
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
8661
|
+
arg_slot.push(total_slots);
|
|
8662
|
+
total_slots += view_arg_set.has(i) ? 2 : 1;
|
|
8663
|
+
if (hidden_len_set.has(i)) {
|
|
8664
|
+
hidden_len_slots.set(i, total_slots);
|
|
8665
|
+
total_slots += 1;
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
const has_args = total_slots > 0;
|
|
8385
8669
|
let args_base = 0;
|
|
8386
|
-
if (has_args) args_base = allocate_stack_space(status,
|
|
8670
|
+
if (has_args) args_base = allocate_stack_space(status, total_slots * 8, 16);
|
|
8387
8671
|
for (let i = node.params.length - 1; i >= 0; i--) {
|
|
8388
8672
|
const param = node.params[i];
|
|
8389
8673
|
const param_type = param.type?.name || "";
|
|
8390
8674
|
const is_ref_param = node.ref_param_indices?.includes(i);
|
|
8675
|
+
if (view_arg_set.has(i)) {
|
|
8676
|
+
emit_view_string_arg(param, status);
|
|
8677
|
+
status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8678
|
+
status.code += `str x1, [x29, #${args_base + (arg_slot[i] + 1) * 8}]\n`;
|
|
8679
|
+
continue;
|
|
8680
|
+
}
|
|
8391
8681
|
const arr_arg_name = param.node_type === "value" ? param.value : void 0;
|
|
8392
8682
|
if (!!arr_arg_name && !!status.heap_array_vars?.has(arr_arg_name)) build_node$1(param, status);
|
|
8393
8683
|
else if (param.node_type === "array" && param_type) {
|
|
@@ -8445,19 +8735,35 @@ function build_function_call_node$1(node, status) {
|
|
|
8445
8735
|
} 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
8736
|
else build_node$1(node.params[i], status);
|
|
8447
8737
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
8448
|
-
status.code += `str x0, [x29, #${args_base + i * 8}]\n`;
|
|
8738
|
+
status.code += `str x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8739
|
+
}
|
|
8740
|
+
for (const i of hidden_len_indices) {
|
|
8741
|
+
const arg = node.params[i];
|
|
8742
|
+
let computed = false;
|
|
8743
|
+
if (arg.node_type === "value") {
|
|
8744
|
+
const hoisted = status.string_length_slots?.get(arg.value);
|
|
8745
|
+
if (hoisted !== void 0) {
|
|
8746
|
+
status.code += `ldr x0, [x29, #${hoisted}]\n`;
|
|
8747
|
+
computed = true;
|
|
8748
|
+
}
|
|
8749
|
+
}
|
|
8750
|
+
if (!computed) {
|
|
8751
|
+
status.code += `ldr x0, [x29, #${args_base + arg_slot[i] * 8}]\n`;
|
|
8752
|
+
status.code += `bl _strlen\n`;
|
|
8753
|
+
}
|
|
8754
|
+
status.code += `str x0, [x29, #${args_base + hidden_len_slots.get(i) * 8}]\n`;
|
|
8449
8755
|
}
|
|
8450
8756
|
if (has_args) {
|
|
8451
|
-
for (let
|
|
8452
|
-
const slot = start_reg +
|
|
8757
|
+
for (let s = 0; s < total_slots; s++) {
|
|
8758
|
+
const slot = start_reg + s;
|
|
8453
8759
|
if (slot >= 8) continue;
|
|
8454
8760
|
const reg = param_regs[slot];
|
|
8455
8761
|
if (reg === "x0") continue;
|
|
8456
|
-
status.code += `ldr ${reg}, [x29, #${args_base +
|
|
8762
|
+
status.code += `ldr ${reg}, [x29, #${args_base + s * 8}]\n`;
|
|
8457
8763
|
}
|
|
8458
8764
|
if (!is_struct) status.code += `ldr x0, [x29, #${args_base}]\n`;
|
|
8459
8765
|
}
|
|
8460
|
-
overflow_count = Math.max(0,
|
|
8766
|
+
overflow_count = Math.max(0, total_slots - (8 - start_reg));
|
|
8461
8767
|
if (overflow_count > 0) {
|
|
8462
8768
|
outgoing_size = Math.ceil(overflow_count * 8 / 16) * 16;
|
|
8463
8769
|
status.code += `sub sp, sp, #${outgoing_size}\n`;
|
|
@@ -8769,9 +9075,13 @@ function static_enum_case(operand, enum_node, status) {
|
|
|
8769
9075
|
//#region ../src/build_aarch64/build_operation_node.ts
|
|
8770
9076
|
let string_counter$1 = 0;
|
|
8771
9077
|
let coalesce_counter = 0;
|
|
9078
|
+
let view_cmp_counter = 0;
|
|
9079
|
+
let sc_counter = 0;
|
|
8772
9080
|
function reset_string_counter$1() {
|
|
8773
9081
|
string_counter$1 = 0;
|
|
8774
9082
|
coalesce_counter = 0;
|
|
9083
|
+
view_cmp_counter = 0;
|
|
9084
|
+
sc_counter = 0;
|
|
8775
9085
|
}
|
|
8776
9086
|
function is_comparison(op) {
|
|
8777
9087
|
return [
|
|
@@ -9052,6 +9362,29 @@ function build_operation_node$1(node, status) {
|
|
|
9052
9362
|
status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
|
|
9053
9363
|
return;
|
|
9054
9364
|
}
|
|
9365
|
+
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))) {
|
|
9366
|
+
const id = view_cmp_counter++;
|
|
9367
|
+
const len_ne_label = `.Lview_cmp_len_ne_${id}`;
|
|
9368
|
+
const done_label = `.Lview_cmp_done_${id}`;
|
|
9369
|
+
emit_view_string_arg(node.right_value, status);
|
|
9370
|
+
status.code += `stp x0, x1, [sp, #-16]!\n`;
|
|
9371
|
+
emit_view_string_arg(node.left_value, status);
|
|
9372
|
+
status.code += `ldr x2, [sp, #8]\n`;
|
|
9373
|
+
status.code += `cmp x1, x2\n`;
|
|
9374
|
+
status.code += `b.ne ${len_ne_label}\n`;
|
|
9375
|
+
status.code += `mov x2, x1\n`;
|
|
9376
|
+
status.code += `ldr x1, [sp]\n`;
|
|
9377
|
+
status.code += `bl _memcmp\n`;
|
|
9378
|
+
status.code += `cmp x0, #0\n`;
|
|
9379
|
+
status.code += `cset x0, ${node.op === "==" ? "eq" : "ne"}\n`;
|
|
9380
|
+
status.code += `b ${done_label}\n`;
|
|
9381
|
+
status.code += `${len_ne_label}:\n`;
|
|
9382
|
+
status.code += `mov x0, #0\n`;
|
|
9383
|
+
if (node.op === "!=") status.code += `mov x0, #1\n`;
|
|
9384
|
+
status.code += `${done_label}:\n`;
|
|
9385
|
+
status.code += `add sp, sp, #16\n`;
|
|
9386
|
+
return;
|
|
9387
|
+
}
|
|
9055
9388
|
if (node.operator_func) {
|
|
9056
9389
|
const left_type = type_from_value_node$1(node.left_value);
|
|
9057
9390
|
const is_array_op = node.operator_func.struct_name.startsWith("Array") && left_type.is_array;
|
|
@@ -9185,6 +9518,25 @@ function build_operation_node$1(node, status) {
|
|
|
9185
9518
|
else status.code += `fmov x0, d0\n`;
|
|
9186
9519
|
return;
|
|
9187
9520
|
}
|
|
9521
|
+
if (node.op === "&&" || node.op === "||") {
|
|
9522
|
+
const id = sc_counter++;
|
|
9523
|
+
const skip_right = `.Lsc_skip_${id}`;
|
|
9524
|
+
const done = `.Lsc_done_${id}`;
|
|
9525
|
+
build_operand$1(node.left_value, "x0", status);
|
|
9526
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9527
|
+
status.code += `cmp x0, #0\n`;
|
|
9528
|
+
status.code += node.op === "&&" ? `b.eq ${skip_right}\n` : `b.ne ${skip_right}\n`;
|
|
9529
|
+
build_operand$1(node.right_value, "x0", status);
|
|
9530
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9531
|
+
status.code += `cmp x0, #0\n`;
|
|
9532
|
+
status.code += `cset x0, ne\n`;
|
|
9533
|
+
status.code += `b ${done}\n`;
|
|
9534
|
+
status.code += `${skip_right}:\n`;
|
|
9535
|
+
if (node.op === "&&") status.code += `mov x0, #0\n`;
|
|
9536
|
+
else status.code += `mov x0, #1\n`;
|
|
9537
|
+
status.code += `${done}:\n`;
|
|
9538
|
+
return;
|
|
9539
|
+
}
|
|
9188
9540
|
const need_spill = !is_simple(node.left_value);
|
|
9189
9541
|
build_operand$1(node.right_value, "x2", status);
|
|
9190
9542
|
if (need_spill) {
|
|
@@ -9195,19 +9547,7 @@ function build_operation_node$1(node, status) {
|
|
|
9195
9547
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9196
9548
|
if (need_spill) status.code += `ldr x2, [sp], #16\n`;
|
|
9197
9549
|
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)) {
|
|
9550
|
+
if (is_comparison(node.op)) {
|
|
9211
9551
|
status.code += `cmp x1, x2\n`;
|
|
9212
9552
|
status.code += `cset x0, ${map_cmp(node.op, unsigned)}\n`;
|
|
9213
9553
|
} else {
|
|
@@ -9244,6 +9584,7 @@ function is_owned_heap_temp$1(node, status) {
|
|
|
9244
9584
|
if (mangled.startsWith("_string_interpolate_")) return true;
|
|
9245
9585
|
if (mangled.endsWith("_to_string") && mangled !== "string_to_string") return true;
|
|
9246
9586
|
if (raw_name === "to_string" && target_type_name && target_type_name !== "string") return true;
|
|
9587
|
+
if (raw_name === "to_string" && status && node.node_type === "access" && is_view_value$1(node.target, status)) return true;
|
|
9247
9588
|
const heap_set = status?.heap_returning_functions;
|
|
9248
9589
|
if (heap_set?.has(mangled)) return true;
|
|
9249
9590
|
if (heap_set && target_value && heap_set.has(`${target_value}_${raw_name}`)) return true;
|
|
@@ -9447,6 +9788,12 @@ function build_return_node$1(node, status) {
|
|
|
9447
9788
|
if (override_ctor) status.struct_return_buffer = saved_buffer;
|
|
9448
9789
|
}
|
|
9449
9790
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
9791
|
+
if (status.function_return_type?.is_view && status.function_return_type.name === "string" && node.value && !is_view_value$1(node.value, status)) {
|
|
9792
|
+
status.code += `str x0, [sp, #-16]!\n`;
|
|
9793
|
+
status.code += `bl _strlen\n`;
|
|
9794
|
+
status.code += `mov x1, x0\n`;
|
|
9795
|
+
status.code += `ldr x0, [sp], #16\n`;
|
|
9796
|
+
}
|
|
9450
9797
|
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
9798
|
const fn_name = status.current_function_name;
|
|
9452
9799
|
const mangled = status.current_struct ? `${status.current_struct.name}_${fn_name}` : fn_name;
|
|
@@ -9767,7 +10114,7 @@ function build_value_node$1(node, status) {
|
|
|
9767
10114
|
}
|
|
9768
10115
|
//#endregion
|
|
9769
10116
|
//#region ../src/build_common/scan_string_length_hoists.ts
|
|
9770
|
-
const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
10117
|
+
const NON_IDENTIFIERS$1 = /* @__PURE__ */ new Set([
|
|
9771
10118
|
"true",
|
|
9772
10119
|
"false",
|
|
9773
10120
|
"null",
|
|
@@ -9776,12 +10123,12 @@ const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
|
9776
10123
|
"default"
|
|
9777
10124
|
]);
|
|
9778
10125
|
function is_identifier(value) {
|
|
9779
|
-
return !!value && !NON_IDENTIFIERS.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
|
|
10126
|
+
return !!value && !NON_IDENTIFIERS$1.has(value) && !/^(\+|-)?\d+(\.\d+)?$/.test(value) && !value.startsWith("\"") && !value.startsWith("'");
|
|
9780
10127
|
}
|
|
9781
|
-
function root_name(node) {
|
|
10128
|
+
function root_name$1(node) {
|
|
9782
10129
|
if (!node) return void 0;
|
|
9783
10130
|
if (node.node_type === "value") return node.value;
|
|
9784
|
-
if (node.node_type === "access") return root_name(node.target);
|
|
10131
|
+
if (node.node_type === "access") return root_name$1(node.target);
|
|
9785
10132
|
}
|
|
9786
10133
|
/**
|
|
9787
10134
|
* Find the bare `string` variables whose `.length` is read inside a while
|
|
@@ -9868,9 +10215,9 @@ function scan_string_length_hoists(condition, statements, update, status) {
|
|
|
9868
10215
|
}
|
|
9869
10216
|
case "assign": {
|
|
9870
10217
|
const assign = n;
|
|
9871
|
-
const lhs = root_name(assign.left_value);
|
|
10218
|
+
const lhs = root_name$1(assign.left_value);
|
|
9872
10219
|
if (lhs) invalidated.add(lhs);
|
|
9873
|
-
const swap = root_name(assign.swap);
|
|
10220
|
+
const swap = root_name$1(assign.swap);
|
|
9874
10221
|
if (swap) invalidated.add(swap);
|
|
9875
10222
|
walk_children(n, in_boundary);
|
|
9876
10223
|
return;
|
|
@@ -9892,11 +10239,20 @@ function scan_string_length_hoists(condition, statements, update, status) {
|
|
|
9892
10239
|
case "access_func": {
|
|
9893
10240
|
const call = n;
|
|
9894
10241
|
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]);
|
|
10242
|
+
const arg = root_name$1(call.params?.[i]);
|
|
9896
10243
|
if (arg) invalidated.add(arg);
|
|
9897
10244
|
}
|
|
9898
10245
|
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);
|
|
10246
|
+
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);
|
|
10247
|
+
}
|
|
10248
|
+
if (!in_boundary && call.resolved_function) {
|
|
10249
|
+
const self_offset = call.resolved_function.params[0]?.is_self_param ? 1 : 0;
|
|
10250
|
+
const params = call.params ?? [];
|
|
10251
|
+
for (let i = 0; i < params.length; i++) {
|
|
10252
|
+
if (!call.resolved_function.params[i + self_offset]?.hidden_len) continue;
|
|
10253
|
+
const arg = params[i];
|
|
10254
|
+
if (arg.node_type === "value" && is_identifier(arg.value) && is_string_target(arg)) candidates.set(arg.value, arg);
|
|
10255
|
+
}
|
|
9900
10256
|
}
|
|
9901
10257
|
walk_children(n, in_boundary);
|
|
9902
10258
|
return;
|
|
@@ -10073,6 +10429,7 @@ function build_while_loop_node$1(node, status) {
|
|
|
10073
10429
|
if (!status.string_length_slots) status.string_length_slots = /* @__PURE__ */ new Map();
|
|
10074
10430
|
for (const [name, target] of hoists) {
|
|
10075
10431
|
if (status.string_length_slots.has(name)) continue;
|
|
10432
|
+
if (status.hidden_len_params?.has(name)) continue;
|
|
10076
10433
|
const offset = allocate_stack_space(status, 8);
|
|
10077
10434
|
status.string_length_slots.set(name, offset);
|
|
10078
10435
|
build_node$1(target, status);
|
|
@@ -10317,6 +10674,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10317
10674
|
emit_owning_replace_T(elem, status);
|
|
10318
10675
|
return true;
|
|
10319
10676
|
}
|
|
10677
|
+
if (func_name === "shift_T") {
|
|
10678
|
+
emit_owning_shift_T(elem, status, "x0");
|
|
10679
|
+
return true;
|
|
10680
|
+
}
|
|
10320
10681
|
return false;
|
|
10321
10682
|
}
|
|
10322
10683
|
if (owning_buffer_is_string_elem_aarch64(struct_node)) {
|
|
@@ -10328,6 +10689,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10328
10689
|
emit_string_replace_T(status, "x0");
|
|
10329
10690
|
return true;
|
|
10330
10691
|
}
|
|
10692
|
+
if (func_name === "shift_T") {
|
|
10693
|
+
emit_string_shift_T(status, "x0");
|
|
10694
|
+
return true;
|
|
10695
|
+
}
|
|
10331
10696
|
}
|
|
10332
10697
|
return false;
|
|
10333
10698
|
}
|
|
@@ -10341,7 +10706,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
10341
10706
|
function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
10342
10707
|
const elem = owning_buffer_element_aarch64(node, status);
|
|
10343
10708
|
if (elem) {
|
|
10344
|
-
if (func_name !== "store_T" && func_name !== "replace_T") return false;
|
|
10709
|
+
if (func_name !== "store_T" && func_name !== "replace_T" && func_name !== "shift_T") return false;
|
|
10345
10710
|
emit_owning_standalone_struct(elem, func_name, status);
|
|
10346
10711
|
return true;
|
|
10347
10712
|
}
|
|
@@ -10354,12 +10719,20 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
|
10354
10719
|
emit_string_replace_T(status, "x19");
|
|
10355
10720
|
return true;
|
|
10356
10721
|
}
|
|
10722
|
+
if (func_name === "shift_T") {
|
|
10723
|
+
emit_string_shift_T(status, "x19");
|
|
10724
|
+
return true;
|
|
10725
|
+
}
|
|
10357
10726
|
}
|
|
10358
10727
|
return false;
|
|
10359
10728
|
}
|
|
10360
10729
|
function emit_owning_standalone_struct(elem, func_name, status) {
|
|
10361
10730
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
10362
10731
|
const string_fields = collect_string_fields(elem, status);
|
|
10732
|
+
if (func_name === "shift_T") {
|
|
10733
|
+
emit_owning_shift_T(elem, status, "x19");
|
|
10734
|
+
return true;
|
|
10735
|
+
}
|
|
10363
10736
|
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10364
10737
|
status.code += `str x22, [sp, #-16]!\n`;
|
|
10365
10738
|
status.code += `ldr x20, [x19, #8]\n`;
|
|
@@ -10459,6 +10832,59 @@ function emit_string_replace_T(status, self_reg) {
|
|
|
10459
10832
|
status.code += `${done}:\n`;
|
|
10460
10833
|
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10461
10834
|
}
|
|
10835
|
+
/**
|
|
10836
|
+
* Specialized `Buffer<string>` shift_T. `self_reg` is the register holding
|
|
10837
|
+
* the Buffer pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src.
|
|
10838
|
+
* Move slot src into slot dst — free dst's heap copy, take over src's
|
|
10839
|
+
* pointer, zero src. Exactly one slot owns the string afterwards.
|
|
10840
|
+
*/
|
|
10841
|
+
function emit_string_shift_T(status, self_reg) {
|
|
10842
|
+
const done = `.Lstr_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
10843
|
+
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10844
|
+
status.code += `ldr x9, [${self_reg}, #8]\n`;
|
|
10845
|
+
status.code += `add x20, x9, x1, lsl #3\n`;
|
|
10846
|
+
status.code += `add x21, x9, x2, lsl #3\n`;
|
|
10847
|
+
status.code += `cmp x20, x21\n`;
|
|
10848
|
+
status.code += `b.eq ${done}\n`;
|
|
10849
|
+
status.code += `ldr x0, [x20]\n`;
|
|
10850
|
+
emit_free(status);
|
|
10851
|
+
status.code += `ldr x9, [x21]\n`;
|
|
10852
|
+
status.code += `str x9, [x20]\n`;
|
|
10853
|
+
status.code += `str xzr, [x21]\n`;
|
|
10854
|
+
status.code += `${done}:\n`;
|
|
10855
|
+
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10856
|
+
}
|
|
10857
|
+
/**
|
|
10858
|
+
* Specialized owning-value-struct shift_T. `self_reg` holds the Buffer
|
|
10859
|
+
* pointer ("x0" inline, "x19" standalone); x1 = dst, x2 = src (slot indices).
|
|
10860
|
+
* Destroy dst's old value, copy src's bytes over it wholesale (the moved
|
|
10861
|
+
* struct's owned pointers transfer — no deep copy), zero src.
|
|
10862
|
+
*/
|
|
10863
|
+
function emit_owning_shift_T(elem, status, self_reg) {
|
|
10864
|
+
const T_SIZE = get_struct_size(elem.name, status);
|
|
10865
|
+
const done = `.Lown_sh_done_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
10866
|
+
status.code += `stp x20, x21, [sp, #-16]!\n`;
|
|
10867
|
+
status.code += `str x22, [sp, #-16]!\n`;
|
|
10868
|
+
status.code += `ldr x9, [${self_reg}, #8]\n`;
|
|
10869
|
+
status.code += `mov x22, #${T_SIZE}\n`;
|
|
10870
|
+
status.code += `madd x20, x1, x22, x9\n`;
|
|
10871
|
+
status.code += `madd x21, x2, x22, x9\n`;
|
|
10872
|
+
status.code += `cmp x20, x21\n`;
|
|
10873
|
+
status.code += `b.eq ${done}\n`;
|
|
10874
|
+
status.code += `mov x0, x20\n`;
|
|
10875
|
+
status.code += `bl ${elem.name}_destroy\n`;
|
|
10876
|
+
status.code += `mov x0, x20\n`;
|
|
10877
|
+
status.code += `mov x1, x21\n`;
|
|
10878
|
+
status.code += `mov x2, x22\n`;
|
|
10879
|
+
status.code += `bl _memcpy\n`;
|
|
10880
|
+
status.code += `mov x0, x21\n`;
|
|
10881
|
+
status.code += `mov x1, #0\n`;
|
|
10882
|
+
status.code += `mov x2, x22\n`;
|
|
10883
|
+
status.code += `bl _memset\n`;
|
|
10884
|
+
status.code += `${done}:\n`;
|
|
10885
|
+
status.code += `ldr x22, [sp], #16\n`;
|
|
10886
|
+
status.code += `ldp x20, x21, [sp], #16\n`;
|
|
10887
|
+
}
|
|
10462
10888
|
function emit_owning_store_T(elem, status) {
|
|
10463
10889
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
10464
10890
|
const string_fields = collect_string_fields(elem, status);
|
|
@@ -11192,10 +11618,14 @@ function build_struct_functions$1(node, status) {
|
|
|
11192
11618
|
if (needs_x19) callee_idx = 1;
|
|
11193
11619
|
const old_ref_params = status.function_ref_params;
|
|
11194
11620
|
const old_ref_class_slots = status.ref_class_slots;
|
|
11621
|
+
const old_view_params = status.function_view_params;
|
|
11622
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
11195
11623
|
status.function_param_regs = /* @__PURE__ */ new Map();
|
|
11196
11624
|
status.function_param_vars = /* @__PURE__ */ new Set();
|
|
11197
11625
|
status.function_ref_params = /* @__PURE__ */ new Set();
|
|
11198
11626
|
status.ref_class_slots = /* @__PURE__ */ new Map();
|
|
11627
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
11628
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
11199
11629
|
status.struct_return_buffer = void 0;
|
|
11200
11630
|
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
11631
|
let return_buffer_stack_offset;
|
|
@@ -11218,6 +11648,10 @@ function build_struct_functions$1(node, status) {
|
|
|
11218
11648
|
if (param.type.is_ref) status.function_ref_params.add(param.name);
|
|
11219
11649
|
continue;
|
|
11220
11650
|
}
|
|
11651
|
+
if (param.type.is_view) {
|
|
11652
|
+
slot_idx += 2;
|
|
11653
|
+
continue;
|
|
11654
|
+
}
|
|
11221
11655
|
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
11656
|
const saved_reg = callee_saved[callee_idx++];
|
|
11223
11657
|
if (saved_reg !== "x19" || !needs_x19) status.code += `str ${saved_reg}, [sp, #-16]!\n`;
|
|
@@ -11235,6 +11669,7 @@ function build_struct_functions$1(node, status) {
|
|
|
11235
11669
|
if (!status.class_vars) status.class_vars = /* @__PURE__ */ new Set();
|
|
11236
11670
|
status.class_vars.add(param.name);
|
|
11237
11671
|
}
|
|
11672
|
+
if (param.hidden_len) slot_idx++;
|
|
11238
11673
|
slot_idx++;
|
|
11239
11674
|
}
|
|
11240
11675
|
status.code += `sub sp, sp, #${stack_placeholder}\n`;
|
|
@@ -11255,6 +11690,22 @@ function build_struct_functions$1(node, status) {
|
|
|
11255
11690
|
second_slot_idx += 2;
|
|
11256
11691
|
continue;
|
|
11257
11692
|
}
|
|
11693
|
+
if (param.type.is_view) {
|
|
11694
|
+
const offset = allocate_stack_space(status, 16, 16);
|
|
11695
|
+
status.stack_offsets.set(param.name, offset);
|
|
11696
|
+
status.function_view_params.add(param.name);
|
|
11697
|
+
for (const half of [0, 1]) {
|
|
11698
|
+
const p_slot = second_slot_idx + half;
|
|
11699
|
+
if (p_slot < 8) status.code += `str ${param_regs[p_slot]}, [x29, #${offset + half * 8}]\n`;
|
|
11700
|
+
else {
|
|
11701
|
+
const k = p_slot - 8;
|
|
11702
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
|
|
11703
|
+
status.code += `str x9, [x29, #${offset + half * 8}]\n`;
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11706
|
+
second_slot_idx += 2;
|
|
11707
|
+
continue;
|
|
11708
|
+
}
|
|
11258
11709
|
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
11710
|
const size = aarch64_size(param.type.name);
|
|
11260
11711
|
const offset = allocate_stack_space(status, size, size);
|
|
@@ -11285,6 +11736,20 @@ function build_struct_functions$1(node, status) {
|
|
|
11285
11736
|
}
|
|
11286
11737
|
}
|
|
11287
11738
|
}
|
|
11739
|
+
if (param.hidden_len) {
|
|
11740
|
+
const len_offset = allocate_stack_space(status, 8, 8);
|
|
11741
|
+
status.stack_offsets.set(`_${param.name}_len`, len_offset);
|
|
11742
|
+
if (!status.hidden_len_params) status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
11743
|
+
status.hidden_len_params.add(param.name);
|
|
11744
|
+
const len_slot = second_slot_idx + 1;
|
|
11745
|
+
if (len_slot < 8) status.code += `str ${param_regs[len_slot]}, [x29, #${len_offset}]\n`;
|
|
11746
|
+
else {
|
|
11747
|
+
const k = len_slot - 8;
|
|
11748
|
+
status.code += `ldr x9, [x29, #${overflow_placeholder(func_label, k)}]\n`;
|
|
11749
|
+
status.code += `str x9, [x29, #${len_offset}]\n`;
|
|
11750
|
+
}
|
|
11751
|
+
second_slot_idx++;
|
|
11752
|
+
}
|
|
11288
11753
|
second_slot_idx++;
|
|
11289
11754
|
}
|
|
11290
11755
|
const moved_param_save_slots = /* @__PURE__ */ new Map();
|
|
@@ -11374,6 +11839,8 @@ function build_struct_functions$1(node, status) {
|
|
|
11374
11839
|
status.function_param_vars = old_param_vars;
|
|
11375
11840
|
status.function_ref_params = old_ref_params;
|
|
11376
11841
|
status.ref_class_slots = old_ref_class_slots;
|
|
11842
|
+
status.function_view_params = old_view_params;
|
|
11843
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
11377
11844
|
status.function_return_label = old_return_label;
|
|
11378
11845
|
status.force_heap_strings = old_force_heap;
|
|
11379
11846
|
status.struct_return_buffer = void 0;
|
|
@@ -11786,10 +12253,17 @@ function build_naked_inline(struct_node, func, status) {
|
|
|
11786
12253
|
let asm = extract_aarch64_asm(func, status.platform);
|
|
11787
12254
|
const standalone_return_label = `.return_${struct_node.name}_${func.name.replace(/#/g, "")}`;
|
|
11788
12255
|
asm = asm.replaceAll(`b ${standalone_return_label}`, "");
|
|
11789
|
-
|
|
12256
|
+
const x19_reads = count_x19_reads(asm);
|
|
12257
|
+
let prologue = "";
|
|
12258
|
+
let epilogue = "";
|
|
12259
|
+
if (x19_reads === 1) asm = asm.replace(/\bx19\b/g, "x0");
|
|
12260
|
+
else if (x19_reads > 1) {
|
|
12261
|
+
prologue = `str x19, [sp, #-16]!\nmov x19, x0\n`;
|
|
12262
|
+
epilogue = `ldr x19, [sp], #16\n`;
|
|
12263
|
+
}
|
|
11790
12264
|
const site = inline_counter++;
|
|
11791
|
-
asm = asm.replace(/(\.L[A-Za-z0-9_]+)/g, `$
|
|
11792
|
-
status.code += asm + "\n";
|
|
12265
|
+
asm = asm.replace(/(\.L[A-Za-z0-9_]+)/g, `$1__ni${site}`);
|
|
12266
|
+
status.code += prologue + asm + "\n" + epilogue;
|
|
11793
12267
|
}
|
|
11794
12268
|
function build_inline_method(struct_node, func, status) {
|
|
11795
12269
|
if (emit_owning_buffer_inline_aarch64(struct_node, func.name, status)) return;
|
|
@@ -11814,6 +12288,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11814
12288
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
11815
12289
|
const old_moved = status.moved;
|
|
11816
12290
|
const old_outer_scope_declarations = status.outer_scope_declarations;
|
|
12291
|
+
const old_current_struct = status.current_struct;
|
|
12292
|
+
const old_current_function_name = status.current_function_name;
|
|
12293
|
+
const old_return_assign = status.return_assign;
|
|
12294
|
+
const old_join_needs_owned_string = status.join_needs_owned_string;
|
|
12295
|
+
const old_emitted_allocations = status.emitted_allocations;
|
|
11817
12296
|
const return_label = `.inline_ret_${inline_counter++}`;
|
|
11818
12297
|
status.function_return_label = return_label;
|
|
11819
12298
|
status.scoped_declarations = [];
|
|
@@ -11824,6 +12303,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11824
12303
|
status.buffer_data_cache = void 0;
|
|
11825
12304
|
status.heap_cleanup_stack = [];
|
|
11826
12305
|
status.moved = /* @__PURE__ */ new Set();
|
|
12306
|
+
status.current_struct = struct_node;
|
|
12307
|
+
status.current_function_name = func.name;
|
|
12308
|
+
status.return_assign = void 0;
|
|
12309
|
+
status.join_needs_owned_string = void 0;
|
|
12310
|
+
status.emitted_allocations = /* @__PURE__ */ new Set();
|
|
11827
12311
|
if (needs_x19) {
|
|
11828
12312
|
status.code += `str x19, [sp, #-16]!\n`;
|
|
11829
12313
|
status.code += `mov x19, x0\n`;
|
|
@@ -11881,9 +12365,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11881
12365
|
status.code += `str x8, [x29, #${return_buffer_stack_offset}]\n`;
|
|
11882
12366
|
status.return_buffer_stack_offset = return_buffer_stack_offset;
|
|
11883
12367
|
}
|
|
12368
|
+
const code_length_before_body = status.code.length;
|
|
11884
12369
|
build_block_node$1(func, status);
|
|
11885
12370
|
const standalone_return_label = `.return_${struct_node.name}_${func.name.replace(/#/g, "")}`;
|
|
11886
|
-
|
|
12371
|
+
const body = status.code.slice(code_length_before_body);
|
|
12372
|
+
status.code = status.code.slice(0, code_length_before_body) + body.replaceAll(`b ${standalone_return_label}\n`, `b ${return_label}\n`);
|
|
11887
12373
|
status.code += `${return_label}:\n`;
|
|
11888
12374
|
for (let i = saved_stack_slots.length - 1; i >= 0; i--) status.code += `add sp, sp, #16\n`;
|
|
11889
12375
|
for (let ci = callee_idx - 1; ci >= 0; ci--) {
|
|
@@ -11905,6 +12391,11 @@ function build_inline_method(struct_node, func, status) {
|
|
|
11905
12391
|
status.heap_cleanup_stack = old_heap_cleanup_stack;
|
|
11906
12392
|
status.moved = old_moved;
|
|
11907
12393
|
status.outer_scope_declarations = old_outer_scope_declarations;
|
|
12394
|
+
status.current_struct = old_current_struct;
|
|
12395
|
+
status.current_function_name = old_current_function_name;
|
|
12396
|
+
status.return_assign = old_return_assign;
|
|
12397
|
+
status.join_needs_owned_string = old_join_needs_owned_string;
|
|
12398
|
+
status.emitted_allocations = old_emitted_allocations;
|
|
11908
12399
|
}
|
|
11909
12400
|
let inline_fn_depth = 0;
|
|
11910
12401
|
const MAX_INLINE_DEPTH = 2;
|
|
@@ -11925,6 +12416,11 @@ function build_inline_function(func, status) {
|
|
|
11925
12416
|
const old_heap_cleanup_stack = status.heap_cleanup_stack;
|
|
11926
12417
|
const old_moved = status.moved;
|
|
11927
12418
|
const old_outer_scope_declarations = status.outer_scope_declarations;
|
|
12419
|
+
const old_current_struct = status.current_struct;
|
|
12420
|
+
const old_current_function_name = status.current_function_name;
|
|
12421
|
+
const old_return_assign = status.return_assign;
|
|
12422
|
+
const old_join_needs_owned_string = status.join_needs_owned_string;
|
|
12423
|
+
const old_emitted_allocations = status.emitted_allocations;
|
|
11928
12424
|
const return_label = `.inline_fn_ret_${inline_counter++}`;
|
|
11929
12425
|
status.function_return_label = return_label;
|
|
11930
12426
|
status.scoped_declarations = [];
|
|
@@ -11935,6 +12431,11 @@ function build_inline_function(func, status) {
|
|
|
11935
12431
|
status.buffer_data_cache = void 0;
|
|
11936
12432
|
status.heap_cleanup_stack = [];
|
|
11937
12433
|
status.moved = /* @__PURE__ */ new Set();
|
|
12434
|
+
status.current_struct = void 0;
|
|
12435
|
+
status.current_function_name = func.name;
|
|
12436
|
+
status.return_assign = void 0;
|
|
12437
|
+
status.join_needs_owned_string = void 0;
|
|
12438
|
+
status.emitted_allocations = /* @__PURE__ */ new Set();
|
|
11938
12439
|
const param_regs = [
|
|
11939
12440
|
"x0",
|
|
11940
12441
|
"x1",
|
|
@@ -12004,6 +12505,11 @@ function build_inline_function(func, status) {
|
|
|
12004
12505
|
status.heap_cleanup_stack = old_heap_cleanup_stack;
|
|
12005
12506
|
status.moved = old_moved;
|
|
12006
12507
|
status.outer_scope_declarations = old_outer_scope_declarations;
|
|
12508
|
+
status.current_struct = old_current_struct;
|
|
12509
|
+
status.current_function_name = old_current_function_name;
|
|
12510
|
+
status.return_assign = old_return_assign;
|
|
12511
|
+
status.join_needs_owned_string = old_join_needs_owned_string;
|
|
12512
|
+
status.emitted_allocations = old_emitted_allocations;
|
|
12007
12513
|
inline_fn_depth--;
|
|
12008
12514
|
return true;
|
|
12009
12515
|
}
|
|
@@ -12158,8 +12664,25 @@ function load_nursery_struct_address(target, status) {
|
|
|
12158
12664
|
}
|
|
12159
12665
|
//#endregion
|
|
12160
12666
|
//#region ../src/build_aarch64/build_access_node.ts
|
|
12667
|
+
/**
|
|
12668
|
+
* Whether `struct_node` is a monomorphized `Array<T>` (`Array_<T>`). The mono
|
|
12669
|
+
* name is unambiguous — the compiler derives it itself (`"Array_" + elem`)
|
|
12670
|
+
* for every `Array<T>` instantiation, so a name match is always an Array mono.
|
|
12671
|
+
*/
|
|
12672
|
+
function is_array_mono_struct(struct_node, status) {
|
|
12673
|
+
return !!struct_node && !struct_node.is_generic && struct_node.name.startsWith("Array_") && !!status.structs.find((s) => s.name === struct_node.name);
|
|
12674
|
+
}
|
|
12161
12675
|
function emit_string_length$1(target, status) {
|
|
12162
12676
|
if (target.node_type === "value") {
|
|
12677
|
+
const name = target.value;
|
|
12678
|
+
if (name !== "self" && status.hidden_len_params?.has(name)) {
|
|
12679
|
+
const len_slot = status.stack_offsets?.get(`_${name}_len`);
|
|
12680
|
+
if (len_slot !== void 0) {
|
|
12681
|
+
status.last_result_is_heap = false;
|
|
12682
|
+
status.code += `ldr x0, [x29, #${len_slot}]\n`;
|
|
12683
|
+
return;
|
|
12684
|
+
}
|
|
12685
|
+
}
|
|
12163
12686
|
const slot = status.string_length_slots?.get(target.value);
|
|
12164
12687
|
if (slot !== void 0) {
|
|
12165
12688
|
status.last_result_is_heap = false;
|
|
@@ -12188,6 +12711,14 @@ function emit_string_length$1(target, status) {
|
|
|
12188
12711
|
* struct element, with x0 = temp address)
|
|
12189
12712
|
* v.to_string() → malloc(len+1); memcpy; null-terminate; owned copy in x0
|
|
12190
12713
|
* (string views only)
|
|
12714
|
+
* The receiver is either a NAMED view local/param (pair in its two stack
|
|
12715
|
+
* slots) or an INLINE view-producing expression such as
|
|
12716
|
+
* `text.slice(start, end).to_string()` — the expression leaves the pair in
|
|
12717
|
+
* x0/x1, which is spilled to scratch slots first. Without this, the inline
|
|
12718
|
+
* chain falls through to struct-method dispatch and resolves to
|
|
12719
|
+
* `string_to_string` (an identity `mov x0, x19`), handing consumers the raw
|
|
12720
|
+
* slice pointer — not NUL-terminated at len — and strlen-based readers run
|
|
12721
|
+
* past the slice.
|
|
12191
12722
|
* Returns true if handled (caller skips struct-method dispatch).
|
|
12192
12723
|
*/
|
|
12193
12724
|
function build_view_op(node, access_func, status) {
|
|
@@ -12196,11 +12727,19 @@ function build_view_op(node, access_func, status) {
|
|
|
12196
12727
|
const vt = status.variable_types?.get(node.target.value);
|
|
12197
12728
|
if (vt?.is_view) t = vt;
|
|
12198
12729
|
}
|
|
12199
|
-
if (!t?.is_view) return false;
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
if (base === void 0)
|
|
12203
|
-
|
|
12730
|
+
if (!t?.is_view && !is_view_value$1(node.target, status)) return false;
|
|
12731
|
+
let base;
|
|
12732
|
+
if (node.target.node_type === "value") base = status.stack_offsets?.get(node.target.value);
|
|
12733
|
+
if (base === void 0) {
|
|
12734
|
+
build_node$1(node.target, status);
|
|
12735
|
+
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
12736
|
+
const temp_base = allocate_stack_space(status, 16, 16);
|
|
12737
|
+
status.code += `str x0, [x29, #${temp_base}]\n`;
|
|
12738
|
+
status.code += `str x1, [x29, #${temp_base + 8}]\n`;
|
|
12739
|
+
base = temp_base;
|
|
12740
|
+
}
|
|
12741
|
+
const view_elem = t?.name || "string";
|
|
12742
|
+
const elem_name = view_elem === "string" ? "char" : view_elem;
|
|
12204
12743
|
if (access_func.name === "at" && access_func.params.length === 1) {
|
|
12205
12744
|
build_node$1(access_func.params[0], status);
|
|
12206
12745
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
@@ -12226,21 +12765,10 @@ function build_view_op(node, access_func, status) {
|
|
|
12226
12765
|
else status.code += `ldr x0, [x0, x1, lsl #3]\n`;
|
|
12227
12766
|
return true;
|
|
12228
12767
|
}
|
|
12229
|
-
if (access_func.name === "to_string" &&
|
|
12768
|
+
if (access_func.name === "to_string" && view_elem === "string") {
|
|
12230
12769
|
status.code += `ldr x0, [x29, #${base}]\n`;
|
|
12231
12770
|
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`;
|
|
12771
|
+
emit_view_materialize_owned(status);
|
|
12244
12772
|
status.last_result_is_heap = true;
|
|
12245
12773
|
return true;
|
|
12246
12774
|
}
|
|
@@ -12448,6 +12976,14 @@ function build_access_field(node, status) {
|
|
|
12448
12976
|
}
|
|
12449
12977
|
const target_name = node.target.node_type === "value" ? node.target.value : target_type?.name;
|
|
12450
12978
|
const access_field = node.access;
|
|
12979
|
+
if (access_field.name === "length" && node.target.node_type === "value" && node.target.value === "self" && is_array_mono_struct(status.current_struct, status)) {
|
|
12980
|
+
const self_reg = get_param_reg("self", status);
|
|
12981
|
+
if (self_reg) {
|
|
12982
|
+
if (self_reg !== "x0") status.code += `mov x0, ${self_reg}\n`;
|
|
12983
|
+
status.code += `ldr x0, [x0, #-8]\n`;
|
|
12984
|
+
return;
|
|
12985
|
+
}
|
|
12986
|
+
}
|
|
12451
12987
|
if (access_field.type?.name === "func") {
|
|
12452
12988
|
status.code += `adr x0, ${target_type.name}_${access_field.name}\n`;
|
|
12453
12989
|
return;
|
|
@@ -13086,14 +13622,41 @@ function build_access_method(node, access_func, status) {
|
|
|
13086
13622
|
"x6",
|
|
13087
13623
|
"x7"
|
|
13088
13624
|
];
|
|
13089
|
-
const
|
|
13625
|
+
const view_arg_set = new Set(access_func.view_param_indices ?? []);
|
|
13626
|
+
const hidden_len_indices = callee_hidden_len_indices(access_func);
|
|
13627
|
+
const hidden_len_set = new Set(hidden_len_indices);
|
|
13628
|
+
const hidden_len_slots = /* @__PURE__ */ new Map();
|
|
13629
|
+
const arg_slot = [];
|
|
13630
|
+
let total_arg_slots = 0;
|
|
13631
|
+
for (let i = 0; i < access_func.params.length; i++) {
|
|
13632
|
+
arg_slot.push(total_arg_slots);
|
|
13633
|
+
total_arg_slots += view_arg_set.has(i) ? 2 : 1;
|
|
13634
|
+
if (hidden_len_set.has(i)) {
|
|
13635
|
+
hidden_len_slots.set(i, total_arg_slots);
|
|
13636
|
+
total_arg_slots += 1;
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
const overflow_count = Math.max(0, total_arg_slots - (8 - start_reg));
|
|
13090
13640
|
let overflow_base = 0;
|
|
13091
13641
|
if (overflow_count > 0) overflow_base = allocate_stack_space(status, overflow_count * 8, 16);
|
|
13642
|
+
const has_view_args = view_arg_set.size > 0;
|
|
13643
|
+
let view_spill_base = 0;
|
|
13644
|
+
if (has_view_args) view_spill_base = allocate_stack_space(status, total_arg_slots * 8, 16);
|
|
13645
|
+
const view_half_store = (j, half) => {
|
|
13646
|
+
const half_slot = start_reg + arg_slot[j] + half;
|
|
13647
|
+
return half_slot >= 8 ? overflow_base + (half_slot - 8) * 8 : view_spill_base + (arg_slot[j] + half) * 8;
|
|
13648
|
+
};
|
|
13092
13649
|
const ref_class_param_reload = [];
|
|
13093
13650
|
for (let i = access_func.params.length - 1; i >= 0; i--) {
|
|
13094
13651
|
const param = access_func.params[i];
|
|
13095
13652
|
const is_ref_param = access_func.ref_param_indices?.includes(i);
|
|
13096
13653
|
const param_type = param.type?.name || "";
|
|
13654
|
+
if (view_arg_set.has(i)) {
|
|
13655
|
+
emit_view_string_arg(param, status);
|
|
13656
|
+
status.code += `str x0, [x29, #${view_half_store(i, 0)}]\n`;
|
|
13657
|
+
status.code += `str x1, [x29, #${view_half_store(i, 1)}]\n`;
|
|
13658
|
+
continue;
|
|
13659
|
+
}
|
|
13097
13660
|
const is_struct = is_struct_type(param_type, status) || is_enum_with_data_type(param_type, status);
|
|
13098
13661
|
if (is_ref_param) {
|
|
13099
13662
|
const rp_name = param.node_type === "value" ? param.value : void 0;
|
|
@@ -13115,14 +13678,66 @@ function build_access_method(node, access_func, status) {
|
|
|
13115
13678
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13116
13679
|
}
|
|
13117
13680
|
} else build_node$1(param, status);
|
|
13118
|
-
const slot = start_reg + i;
|
|
13681
|
+
const slot = start_reg + arg_slot[i];
|
|
13119
13682
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13120
13683
|
if (slot >= 8) status.code += `str x0, [x29, #${overflow_base + (slot - 8) * 8}]\n`;
|
|
13121
13684
|
else {
|
|
13122
|
-
const reg = param_regs[i];
|
|
13685
|
+
const reg = param_regs[arg_slot[i]];
|
|
13123
13686
|
if (reg && reg !== "x0") status.code += `mov ${reg}, x0\n`;
|
|
13124
13687
|
}
|
|
13125
13688
|
}
|
|
13689
|
+
if (has_view_args) for (let j = 0; j < access_func.params.length; j++) {
|
|
13690
|
+
if (!view_arg_set.has(j)) continue;
|
|
13691
|
+
for (const half of [0, 1]) {
|
|
13692
|
+
const half_slot = start_reg + arg_slot[j] + half;
|
|
13693
|
+
if (half_slot >= 8) continue;
|
|
13694
|
+
status.code += `ldr x${half_slot}, [x29, #${view_spill_base + (arg_slot[j] + half) * 8}]\n`;
|
|
13695
|
+
}
|
|
13696
|
+
}
|
|
13697
|
+
if (hidden_len_indices.length > 0) {
|
|
13698
|
+
const needs_strlen = hidden_len_indices.some((i) => {
|
|
13699
|
+
const arg = access_func.params[i];
|
|
13700
|
+
if (arg.node_type !== "value") return true;
|
|
13701
|
+
return !status.string_length_slots?.has(arg.value);
|
|
13702
|
+
});
|
|
13703
|
+
const live_regs = [];
|
|
13704
|
+
if (needs_strlen) {
|
|
13705
|
+
const max_live = Math.min(7, start_reg + total_arg_slots - 1);
|
|
13706
|
+
for (let s = start_reg; s <= max_live; s++) live_regs.push(`x${s}`);
|
|
13707
|
+
}
|
|
13708
|
+
const reg_save_base = needs_strlen ? allocate_stack_space(status, Math.max(live_regs.length, 1) * 8, 16) : 0;
|
|
13709
|
+
const len_save_base = allocate_stack_space(status, hidden_len_indices.length * 8, 16);
|
|
13710
|
+
for (let k = 0; k < live_regs.length; k++) status.code += `str ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
|
|
13711
|
+
for (let h = 0; h < hidden_len_indices.length; h++) {
|
|
13712
|
+
const i = hidden_len_indices[h];
|
|
13713
|
+
const arg = access_func.params[i];
|
|
13714
|
+
let computed = false;
|
|
13715
|
+
if (arg.node_type === "value") {
|
|
13716
|
+
const hoisted = status.string_length_slots?.get(arg.value);
|
|
13717
|
+
if (hoisted !== void 0) {
|
|
13718
|
+
status.code += `ldr x9, [x29, #${hoisted}]\n`;
|
|
13719
|
+
status.code += `str x9, [x29, #${len_save_base + h * 8}]\n`;
|
|
13720
|
+
computed = true;
|
|
13721
|
+
}
|
|
13722
|
+
}
|
|
13723
|
+
if (!computed) {
|
|
13724
|
+
const src_slot = start_reg + arg_slot[i];
|
|
13725
|
+
if (src_slot < 8) {
|
|
13726
|
+
const save_index = src_slot - start_reg;
|
|
13727
|
+
status.code += `ldr x0, [x29, #${reg_save_base + save_index * 8}]\n`;
|
|
13728
|
+
} else status.code += `ldr x0, [x29, #${overflow_base + (src_slot - 8) * 8}]\n`;
|
|
13729
|
+
status.code += `bl _strlen\n`;
|
|
13730
|
+
status.code += `str x0, [x29, #${len_save_base + h * 8}]\n`;
|
|
13731
|
+
}
|
|
13732
|
+
}
|
|
13733
|
+
for (let k = live_regs.length - 1; k >= 0; k--) status.code += `ldr ${live_regs[k]}, [x29, #${reg_save_base + k * 8}]\n`;
|
|
13734
|
+
for (let h = 0; h < hidden_len_indices.length; h++) {
|
|
13735
|
+
const dest_slot = start_reg + hidden_len_slots.get(hidden_len_indices[h]);
|
|
13736
|
+
status.code += `ldr x9, [x29, #${len_save_base + h * 8}]\n`;
|
|
13737
|
+
if (dest_slot < 8) status.code += `mov x${dest_slot}, x9\n`;
|
|
13738
|
+
else status.code += `str x9, [x29, #${overflow_base + (dest_slot - 8) * 8}]\n`;
|
|
13739
|
+
}
|
|
13740
|
+
}
|
|
13126
13741
|
if (!status.code.endsWith("\n")) status.code += "\n";
|
|
13127
13742
|
if (needs_self_save) status.code += `ldr x0, [sp], #16\n`;
|
|
13128
13743
|
let outgoing_size = 0;
|
|
@@ -14226,6 +14841,84 @@ function has_flag_name(name) {
|
|
|
14226
14841
|
return `${name}_has`;
|
|
14227
14842
|
}
|
|
14228
14843
|
//#endregion
|
|
14844
|
+
//#region ../src/build_c/utils/view_value.ts
|
|
14845
|
+
/**
|
|
14846
|
+
* Resolve the struct name of an expression's value by walking the access
|
|
14847
|
+
* chain through the (monomorphized) struct table — bare names via
|
|
14848
|
+
* variable_types / scoped declarations / `self`, field accesses via the
|
|
14849
|
+
* base struct's field type, method calls via the method's return type.
|
|
14850
|
+
* Fallback for cached node types that lost their modifiers (generic bodies
|
|
14851
|
+
* re-checked after monomorphization can carry a stale plain `string`).
|
|
14852
|
+
*/
|
|
14853
|
+
function value_struct_name(node, status) {
|
|
14854
|
+
if (node.node_type === "value") {
|
|
14855
|
+
const name = node.value;
|
|
14856
|
+
if (name === "self" && status.current_struct) return status.current_struct.name;
|
|
14857
|
+
const vt = status.variable_types?.get(name);
|
|
14858
|
+
if (vt?.name) return vt.name;
|
|
14859
|
+
const decl = status.scoped_declarations.findLast((d) => d.name === name);
|
|
14860
|
+
if (decl?.type?.name) return decl.type.name;
|
|
14861
|
+
return;
|
|
14862
|
+
}
|
|
14863
|
+
if (node.node_type === "access") {
|
|
14864
|
+
const inner = node.access;
|
|
14865
|
+
const base = value_struct_name(node.target, status);
|
|
14866
|
+
if (!base) return void 0;
|
|
14867
|
+
const struct = status.structs.find((s) => s.name === base && !s.is_generic);
|
|
14868
|
+
if (!struct) return void 0;
|
|
14869
|
+
if (inner.node_type === "access_field") return struct.fields.find((f) => f.name === inner.name)?.type?.name;
|
|
14870
|
+
if (inner.node_type === "access_func") return struct.functions.find((f) => f.name === inner.name)?.return_type?.name;
|
|
14871
|
+
}
|
|
14872
|
+
}
|
|
14873
|
+
/**
|
|
14874
|
+
* Whether an expression is a `view T` VALUE at the C level — a nomen_view
|
|
14875
|
+
* (ptr, len) struct expression that can pass through a `view T` parameter
|
|
14876
|
+
* unchanged. Recovers `is_view` from the cached node type, the declared
|
|
14877
|
+
* variable type, the current function's view params, or (for method calls
|
|
14878
|
+
* whose cached type lost the modifier) the callee's declared return type.
|
|
14879
|
+
*/
|
|
14880
|
+
function is_view_value(node, status) {
|
|
14881
|
+
if (type_from_value_node$1(node)?.is_view) return true;
|
|
14882
|
+
if (node.node_type === "value") {
|
|
14883
|
+
const name = node.value;
|
|
14884
|
+
if (status.variable_types?.get(name)?.is_view) return true;
|
|
14885
|
+
if (status.function_view_params?.has(name)) return true;
|
|
14886
|
+
}
|
|
14887
|
+
if (node.node_type === "access" && node.access.node_type === "access_func") {
|
|
14888
|
+
const access = node.access;
|
|
14889
|
+
const recv_struct = value_struct_name(node.target, status);
|
|
14890
|
+
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;
|
|
14891
|
+
}
|
|
14892
|
+
return false;
|
|
14893
|
+
}
|
|
14894
|
+
/**
|
|
14895
|
+
* Emit a C expression for an argument passed to a `view string` parameter.
|
|
14896
|
+
* A view-typed argument is already a nomen_view — pass it through. An owned
|
|
14897
|
+
* `string` expression is wrapped into a nomen_view carrying its strlen (an
|
|
14898
|
+
* implicit owned→view borrow; the caller keeps ownership of the string).
|
|
14899
|
+
* Statement-expression form evaluates the operand exactly once.
|
|
14900
|
+
*/
|
|
14901
|
+
function c_view_string_arg(arg, status) {
|
|
14902
|
+
if (is_view_value(arg, status)) {
|
|
14903
|
+
build_node(arg, status);
|
|
14904
|
+
return;
|
|
14905
|
+
}
|
|
14906
|
+
status.code += `({ char* _p = `;
|
|
14907
|
+
build_node(arg, status);
|
|
14908
|
+
status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
|
|
14909
|
+
}
|
|
14910
|
+
/**
|
|
14911
|
+
* Emit a C expression materializing a `view string` into an OWNED heap
|
|
14912
|
+
* string (malloc len+1 / memcpy / null-terminate). Used when a view value
|
|
14913
|
+
* initializes an owned `string` declaration (`const string s = v`) — the
|
|
14914
|
+
* resulting pointer is heap-owned and must be freed at scope exit.
|
|
14915
|
+
*/
|
|
14916
|
+
function c_materialize_view_string(arg, status) {
|
|
14917
|
+
status.code += `({ nomen_view _t = `;
|
|
14918
|
+
build_node(arg, status);
|
|
14919
|
+
status.code += `; char* _r = malloc(_t.len + 1); memcpy(_r, _t.ptr, _t.len); _r[_t.len] = 0; _r; })`;
|
|
14920
|
+
}
|
|
14921
|
+
//#endregion
|
|
14229
14922
|
//#region ../src/build_c/build_operation_node.ts
|
|
14230
14923
|
function build_operation_node(node, status) {
|
|
14231
14924
|
if (node.operator_func?.deferred) {
|
|
@@ -14332,8 +15025,16 @@ function build_operation_node(node, status) {
|
|
|
14332
15025
|
status.code += `)`;
|
|
14333
15026
|
} else if (node.operator_func) {
|
|
14334
15027
|
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
15028
|
const is_string_cmp = node.op === "==" || node.op === "!=";
|
|
15029
|
+
if (is_string_cmp && node.operator_func.struct_name === "string" && (is_view_value(node.left_value, status) || is_view_value(node.right_value, status))) {
|
|
15030
|
+
status.code += `({ nomen_view _a = `;
|
|
15031
|
+
c_view_string_arg(node.left_value, status);
|
|
15032
|
+
status.code += `, _b = `;
|
|
15033
|
+
c_view_string_arg(node.right_value, status);
|
|
15034
|
+
status.code += `; ((_a.len == _b.len) && (memcmp(_a.ptr, _b.ptr, _a.len) == 0))${node.op === "!=" ? " == 0" : ""}; })`;
|
|
15035
|
+
return;
|
|
15036
|
+
}
|
|
15037
|
+
const is_string_op = node.type?.name === "string";
|
|
14337
15038
|
const left_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.left_value, status);
|
|
14338
15039
|
const right_temp = (is_string_op || is_string_cmp) && is_owned_heap_temp(node.right_value, status);
|
|
14339
15040
|
if (left_temp || right_temp) {
|
|
@@ -14587,7 +15288,7 @@ function free_scoped_declarations(status, decls, persist_string_field_records =
|
|
|
14587
15288
|
}
|
|
14588
15289
|
const is_destructured_field_access = dec.value?.node_type === "access" && dec.value.access.node_type === "access_field" && !dec.value.is_moved;
|
|
14589
15290
|
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");
|
|
15291
|
+
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
15292
|
const dec_value = dec.value;
|
|
14592
15293
|
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
15294
|
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 +15566,70 @@ function reclaim_to_loop_body(status) {
|
|
|
14865
15566
|
return loopBodyIdx;
|
|
14866
15567
|
}
|
|
14867
15568
|
//#endregion
|
|
15569
|
+
//#region ../src/build_c/utils/hidden_len_call.ts
|
|
15570
|
+
function needs_len_materialize(arg) {
|
|
15571
|
+
if (arg.node_type === "value") return false;
|
|
15572
|
+
if (arg.node_type === "access" && arg.access.node_type === "access_field") return false;
|
|
15573
|
+
return true;
|
|
15574
|
+
}
|
|
15575
|
+
/**
|
|
15576
|
+
* Emit the statement-expression prologue materialising every non-lvalue
|
|
15577
|
+
* hidden-len argument once. Must be called immediately BEFORE the call's
|
|
15578
|
+
* label text is emitted; the args loop then substitutes the temps, and
|
|
15579
|
+
* close_hidden_len_wrap() terminates the expression after the call.
|
|
15580
|
+
*/
|
|
15581
|
+
function open_hidden_len_wrap(indices, params, status) {
|
|
15582
|
+
const temps = /* @__PURE__ */ new Map();
|
|
15583
|
+
const to_materialize = indices.filter((i) => params[i] && needs_len_materialize(params[i]));
|
|
15584
|
+
if (to_materialize.length === 0) return {
|
|
15585
|
+
temps,
|
|
15586
|
+
open: false
|
|
15587
|
+
};
|
|
15588
|
+
status.code += `({ `;
|
|
15589
|
+
for (const i of to_materialize) {
|
|
15590
|
+
const tmp = `_hlm_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
15591
|
+
status.code += `char* ${tmp} = `;
|
|
15592
|
+
build_node(params[i], status);
|
|
15593
|
+
status.code += `; `;
|
|
15594
|
+
temps.set(i, tmp);
|
|
15595
|
+
}
|
|
15596
|
+
return {
|
|
15597
|
+
temps,
|
|
15598
|
+
open: true
|
|
15599
|
+
};
|
|
15600
|
+
}
|
|
15601
|
+
function close_hidden_len_wrap(wrap, status) {
|
|
15602
|
+
if (wrap.open) status.code += `; })`;
|
|
15603
|
+
}
|
|
15604
|
+
/**
|
|
15605
|
+
* Emit the companion length EXPRESSION for the argument at a hidden-len
|
|
15606
|
+
* index (the caller emits the separating `, `). `temp` is the materialised
|
|
15607
|
+
* pointer temp when one exists (open_hidden_len_wrap).
|
|
15608
|
+
*/
|
|
15609
|
+
function emit_hidden_len_expr(arg, temp, status) {
|
|
15610
|
+
if (temp) {
|
|
15611
|
+
status.code += `strlen(${temp})`;
|
|
15612
|
+
return;
|
|
15613
|
+
}
|
|
15614
|
+
if (arg.node_type === "value") {
|
|
15615
|
+
const value = arg.value;
|
|
15616
|
+
if (value.startsWith("\"")) {
|
|
15617
|
+
status.code += `sizeof(${value}) - 1`;
|
|
15618
|
+
return;
|
|
15619
|
+
}
|
|
15620
|
+
const hoisted = status.string_length_temps?.get(value);
|
|
15621
|
+
if (hoisted) {
|
|
15622
|
+
status.code += hoisted;
|
|
15623
|
+
return;
|
|
15624
|
+
}
|
|
15625
|
+
status.code += `strlen(${value})`;
|
|
15626
|
+
return;
|
|
15627
|
+
}
|
|
15628
|
+
status.code += `strlen(`;
|
|
15629
|
+
build_node(arg, status);
|
|
15630
|
+
status.code += `)`;
|
|
15631
|
+
}
|
|
15632
|
+
//#endregion
|
|
14868
15633
|
//#region ../src/build_c/build_access_node.ts
|
|
14869
15634
|
/**
|
|
14870
15635
|
* The C type of a single element of a `view T` slice, used to cast the
|
|
@@ -14926,6 +15691,66 @@ function build_vtable_target(node, status) {
|
|
|
14926
15691
|
status.code = status.code.substring(0, before);
|
|
14927
15692
|
status.code += "&" + expr;
|
|
14928
15693
|
}
|
|
15694
|
+
/**
|
|
15695
|
+
* An Array method's `self` is a `struct Array_<T>*` — header `[0]=_vt`,
|
|
15696
|
+
* `[8]=length`, elements at `+16` — which is exactly what a heap receiver
|
|
15697
|
+
* (`struct Array_<T>*`) already is. A NON-heap receiver (a plain stack C
|
|
15698
|
+
* array `T w[N]`, or a variadic `...T` param lowered to `T *w`) would pass
|
|
15699
|
+
* the bare element pointer, so the callee's `self->length` and the raw
|
|
15700
|
+
* bodies' `(T*)((char*)self + sizeof(*self))` reads land on garbage
|
|
15701
|
+
* (`w[1]`, `w + 2`). When such a receiver with a usable length calls a
|
|
15702
|
+
* method on the mono `Array_<T>` struct, build a statement-expression
|
|
15703
|
+
* temporary carrying a proper header plus a copy of the elements — the
|
|
15704
|
+
* same wrap the binary-operator operand path uses
|
|
15705
|
+
* (build_array_operand_for_call). A `ref self` callee additionally gets
|
|
15706
|
+
* its element copy written back after the call, so mutations through
|
|
15707
|
+
* `self` reach the caller's array.
|
|
15708
|
+
*/
|
|
15709
|
+
function array_receiver_wrap(node, target_type, target_method, mono_struct_name, status) {
|
|
15710
|
+
if (!target_type?.is_array || !mono_struct_name.startsWith("Array_")) return void 0;
|
|
15711
|
+
if (node.target.node_type !== "value") return void 0;
|
|
15712
|
+
const target_value = node.target.value;
|
|
15713
|
+
if (target_type.is_array_heap || status.heap_array_vars?.has(target_value)) return void 0;
|
|
15714
|
+
let length;
|
|
15715
|
+
if (status.stack_array_lengths?.has(target_value)) length = status.stack_array_lengths.get(target_value);
|
|
15716
|
+
else if (target_type.length) {
|
|
15717
|
+
const before = status.code.length;
|
|
15718
|
+
build_node(target_type.length, status);
|
|
15719
|
+
length = status.code.substring(before);
|
|
15720
|
+
status.code = status.code.substring(0, before);
|
|
15721
|
+
} else if (status.function_variadic_params?.has(target_value)) length = `_${target_value}_len`;
|
|
15722
|
+
if (!length) return void 0;
|
|
15723
|
+
const before = status.code.length;
|
|
15724
|
+
status.suppress_dereference = true;
|
|
15725
|
+
build_node(node.target, status);
|
|
15726
|
+
status.suppress_dereference = false;
|
|
15727
|
+
const recv = status.code.substring(before);
|
|
15728
|
+
status.code = status.code.substring(0, before);
|
|
15729
|
+
const elem_c = c_type(target_type.name);
|
|
15730
|
+
const id = status.label_counter = (status.label_counter ?? 0) + 1;
|
|
15731
|
+
const wrap = `_arrm_${id}`;
|
|
15732
|
+
const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
|
|
15733
|
+
const ret = target_method?.return_type;
|
|
15734
|
+
const ret_c = !ret?.name ? "void" : ret.is_array ? "void*" : c_type(ret.name);
|
|
15735
|
+
const bytes = `${length} * sizeof(${elem_c})`;
|
|
15736
|
+
const length_is_literal = /^(\d+L?|0x[0-9a-fA-F]+)$/.test(length.trim());
|
|
15737
|
+
let prefix;
|
|
15738
|
+
let self_expr;
|
|
15739
|
+
if (length_is_literal) {
|
|
15740
|
+
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} = ` : "");
|
|
15741
|
+
self_expr = `(struct ${mono_struct_name}*)&${wrap}`;
|
|
15742
|
+
} else {
|
|
15743
|
+
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} = ` : "");
|
|
15744
|
+
self_expr = `_arrm_p_${id}`;
|
|
15745
|
+
}
|
|
15746
|
+
const copy_back = `memcpy(${recv}, ${length_is_literal ? `${wrap}._d` : `${wrap} + sizeof(struct ${mono_struct_name})`}, ${bytes}); `;
|
|
15747
|
+
const suffix = (method_self_is_ref ? `; ${copy_back}` + (ret_c !== "void" ? `_arrm_r_${id}; ` : `(void)0; `) : "") + `; })`;
|
|
15748
|
+
return {
|
|
15749
|
+
prefix,
|
|
15750
|
+
self_expr,
|
|
15751
|
+
suffix
|
|
15752
|
+
};
|
|
15753
|
+
}
|
|
14929
15754
|
function build_access_node(node, status) {
|
|
14930
15755
|
let target_type = type_from_value_node$1(node.target);
|
|
14931
15756
|
if (!target_type?.name && node.target.node_type === "value") {
|
|
@@ -15233,20 +16058,36 @@ function build_access_node(node, status) {
|
|
|
15233
16058
|
}
|
|
15234
16059
|
}
|
|
15235
16060
|
const label = access_func.mangled_name || trait_default_label || `${mono_struct_name}_${access_func.name.replace(/#/g, "")}`;
|
|
16061
|
+
const array_wrap = array_receiver_wrap(node, method_type, target_method, mono_struct_name, status);
|
|
16062
|
+
if (array_wrap) status.code += array_wrap.prefix;
|
|
16063
|
+
const hidden_len = callee_hidden_len_indices(access_func);
|
|
16064
|
+
const hidden_wrap = open_hidden_len_wrap(hidden_len, access_func.params, status);
|
|
15236
16065
|
status.code += `${label}(`;
|
|
15237
16066
|
if (!access_func.is_static) {
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
const
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
16067
|
+
if (array_wrap) status.code += array_wrap.self_expr;
|
|
16068
|
+
else {
|
|
16069
|
+
const method_self_is_ref = !!target_method?.params?.some((p) => p.is_self_param && (p.is_ref || p.type?.is_ref));
|
|
16070
|
+
if (!built_in_types.includes(method_type?.name || "") || method_self_is_ref) {
|
|
16071
|
+
const target_value = node.target.node_type === "value" ? node.target.value : "";
|
|
16072
|
+
const target_is_ref_class_param = !!status.ref_class_params?.has(target_value);
|
|
16073
|
+
if (!(!!status.function_ref_params?.has(target_value) || !!status.class_vars?.has(target_value) || !!status.heap_array_vars?.has(target_value))) status.code += "&";
|
|
16074
|
+
else if (target_is_ref_class_param) {} else status.suppress_dereference = true;
|
|
16075
|
+
}
|
|
16076
|
+
build_node(node.target, status);
|
|
16077
|
+
status.suppress_dereference = false;
|
|
15244
16078
|
}
|
|
15245
|
-
build_node(node.target, status);
|
|
15246
|
-
status.suppress_dereference = false;
|
|
15247
16079
|
}
|
|
15248
16080
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
15249
16081
|
if (!access_func.is_static || i > 0) status.code += ", ";
|
|
16082
|
+
const hidden_temp = hidden_wrap.temps.get(i);
|
|
16083
|
+
if (hidden_temp) {
|
|
16084
|
+
status.code += `${hidden_temp}, strlen(${hidden_temp})`;
|
|
16085
|
+
continue;
|
|
16086
|
+
}
|
|
16087
|
+
if (access_func.view_param_indices?.includes(i)) {
|
|
16088
|
+
c_view_string_arg(access_func.params[i], status);
|
|
16089
|
+
continue;
|
|
16090
|
+
}
|
|
15250
16091
|
const param_type = type_from_value_node$1(access_func.params[i]);
|
|
15251
16092
|
const param_value = access_func.params[i].node_type === "value" ? access_func.params[i].value : "";
|
|
15252
16093
|
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 +16108,14 @@ function build_access_node(node, status) {
|
|
|
15267
16108
|
build_node(access_func.params[i], status);
|
|
15268
16109
|
status.suppress_dereference = false;
|
|
15269
16110
|
} else build_node(access_func.params[i], status);
|
|
16111
|
+
if (hidden_len.includes(i)) {
|
|
16112
|
+
status.code += `, `;
|
|
16113
|
+
emit_hidden_len_expr(access_func.params[i], void 0, status);
|
|
16114
|
+
}
|
|
15270
16115
|
}
|
|
15271
16116
|
status.code += ")";
|
|
16117
|
+
close_hidden_len_wrap(hidden_wrap, status);
|
|
16118
|
+
if (array_wrap) status.code += array_wrap.suffix;
|
|
15272
16119
|
}
|
|
15273
16120
|
if (access_func.mov_param_indices) for (const idx of access_func.mov_param_indices) {
|
|
15274
16121
|
const param = access_func.params[idx];
|
|
@@ -15310,6 +16157,11 @@ function resolve_access_field_type(node, status) {
|
|
|
15310
16157
|
}
|
|
15311
16158
|
function emit_string_length(target, status) {
|
|
15312
16159
|
if (target.node_type === "value") {
|
|
16160
|
+
const name = target.value;
|
|
16161
|
+
if (name !== "self" && status.hidden_len_params?.has(c_function_name(name))) {
|
|
16162
|
+
status.code += `_${c_function_name(name)}_len`;
|
|
16163
|
+
return;
|
|
16164
|
+
}
|
|
15313
16165
|
const temp = status.string_length_temps?.get(target.value);
|
|
15314
16166
|
if (temp) {
|
|
15315
16167
|
status.code += temp;
|
|
@@ -15834,6 +16686,10 @@ function build_parameter_node(node, status) {
|
|
|
15834
16686
|
status.code += `)`;
|
|
15835
16687
|
return;
|
|
15836
16688
|
}
|
|
16689
|
+
if (!node.is_self_param && node.type.is_view) {
|
|
16690
|
+
status.code += `nomen_view ${c_function_name(node.name)}`;
|
|
16691
|
+
return;
|
|
16692
|
+
}
|
|
15837
16693
|
const cls = classify_param(node.type, type_name, {
|
|
15838
16694
|
is_ref: node.is_ref || node.type.is_ref,
|
|
15839
16695
|
is_self: node.is_self_param,
|
|
@@ -15961,6 +16817,7 @@ function has_string_fields(node, status) {
|
|
|
15961
16817
|
const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
|
|
15962
16818
|
"store_T",
|
|
15963
16819
|
"replace_T",
|
|
16820
|
+
"shift_T",
|
|
15964
16821
|
"destroy",
|
|
15965
16822
|
"#destroy"
|
|
15966
16823
|
]);
|
|
@@ -15995,6 +16852,15 @@ function emit_owning_buffer_string_body(func_name, status) {
|
|
|
15995
16852
|
status.code += `if (val != _old) { free(_old); _slots[i] = val ? strdup(val) : 0; }\n`;
|
|
15996
16853
|
return true;
|
|
15997
16854
|
}
|
|
16855
|
+
if (func_name === "shift_T") {
|
|
16856
|
+
status.code += `char** _slots = (char**)self->data;\n`;
|
|
16857
|
+
status.code += `if (dst != src) {\n`;
|
|
16858
|
+
status.code += `free(_slots[dst]);\n`;
|
|
16859
|
+
status.code += `_slots[dst] = _slots[src];\n`;
|
|
16860
|
+
status.code += `_slots[src] = 0;\n`;
|
|
16861
|
+
status.code += `}\n`;
|
|
16862
|
+
return true;
|
|
16863
|
+
}
|
|
15998
16864
|
if (func_name === "destroy") {
|
|
15999
16865
|
status.code += `if (self->data) {\n`;
|
|
16000
16866
|
status.code += `char** _slots = (char**)self->data;\n`;
|
|
@@ -16034,6 +16900,15 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
16034
16900
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status);
|
|
16035
16901
|
return true;
|
|
16036
16902
|
}
|
|
16903
|
+
if (func_name === "shift_T") {
|
|
16904
|
+
status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
|
|
16905
|
+
status.code += `if (dst != src) {\n`;
|
|
16906
|
+
status.code += `${elem.name}_destroy(&_slots[dst]);\n`;
|
|
16907
|
+
status.code += `_slots[dst] = _slots[src];\n`;
|
|
16908
|
+
status.code += `memset((void*)&_slots[src], 0, sizeof(struct ${elem.name}));\n`;
|
|
16909
|
+
status.code += `}\n`;
|
|
16910
|
+
return true;
|
|
16911
|
+
}
|
|
16037
16912
|
if (func_name === "destroy") {
|
|
16038
16913
|
status.code += `if (self->data) {\n`;
|
|
16039
16914
|
status.code += `${Tptr}_slots = ${Tcast}(unsigned long long)self->data;\n`;
|
|
@@ -16356,6 +17231,10 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16356
17231
|
const old_borrow_only = status.c_borrow_only_strings;
|
|
16357
17232
|
const old_return_type = status.function_return_type;
|
|
16358
17233
|
const old_function_name = status.current_function_name;
|
|
17234
|
+
const old_view_params = status.function_view_params;
|
|
17235
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
17236
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
17237
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
16359
17238
|
status.current_function_name = func.name;
|
|
16360
17239
|
status.function_ref_params = /* @__PURE__ */ new Set();
|
|
16361
17240
|
status.class_vars = /* @__PURE__ */ new Set();
|
|
@@ -16367,6 +17246,8 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16367
17246
|
const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
|
|
16368
17247
|
status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
|
|
16369
17248
|
for (let param of func.params) {
|
|
17249
|
+
if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
|
|
17250
|
+
if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
|
|
16370
17251
|
const param_struct = status.structs.find((s) => s.name === param.type.name);
|
|
16371
17252
|
const param_trait = status.traits.find((t) => t.name === param.type.name);
|
|
16372
17253
|
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 +17298,7 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16417
17298
|
for (let i = 0; i < func.params.length; i++) {
|
|
16418
17299
|
if (i > 0) status.code += ", ";
|
|
16419
17300
|
build_parameter_node(func.params[i], status);
|
|
17301
|
+
if (func.params[i].hidden_len) status.code += `, long _${c_function_name(func.params[i].name)}_len`;
|
|
16420
17302
|
}
|
|
16421
17303
|
status.code += `)`;
|
|
16422
17304
|
forward_decl_referenced_types(func, status);
|
|
@@ -16446,6 +17328,8 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
16446
17328
|
status.c_borrow_only_strings = old_borrow_only;
|
|
16447
17329
|
status.function_return_type = old_return_type;
|
|
16448
17330
|
status.current_function_name = old_function_name;
|
|
17331
|
+
status.function_view_params = old_view_params;
|
|
17332
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
16449
17333
|
}
|
|
16450
17334
|
status.current_struct = old_current_struct;
|
|
16451
17335
|
for (let traitName of node.traits) {
|
|
@@ -16576,6 +17460,7 @@ function build_function_node(node, status) {
|
|
|
16576
17460
|
else {
|
|
16577
17461
|
if (node.return_type.name) {
|
|
16578
17462
|
if (node.return_type.is_array) status.code += `struct Array_${node.return_type.name}* `;
|
|
17463
|
+
else if (node.return_type.is_view) status.code += `nomen_view `;
|
|
16579
17464
|
else {
|
|
16580
17465
|
const mono_return_name = mono_type_name(node.return_type);
|
|
16581
17466
|
const return_is_class = !!status.structs.find((s) => s.name === mono_return_name && s.is_class);
|
|
@@ -16596,6 +17481,7 @@ function build_function_node(node, status) {
|
|
|
16596
17481
|
if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
|
|
16597
17482
|
build_parameter_node(node.params[i], status);
|
|
16598
17483
|
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))}`;
|
|
17484
|
+
if (node.params[i].hidden_len) status.code += `, long _${c_function_name(node.params[i].name)}_len`;
|
|
16599
17485
|
}
|
|
16600
17486
|
if (is_nullable_struct_type(node.return_type, status)) {
|
|
16601
17487
|
if (!first_param) status.code += ", ";
|
|
@@ -16624,6 +17510,10 @@ function build_function_node(node, status) {
|
|
|
16624
17510
|
status.ref_class_param_types = /* @__PURE__ */ new Map();
|
|
16625
17511
|
const old_variadic_params = status.function_variadic_params;
|
|
16626
17512
|
status.function_variadic_params = /* @__PURE__ */ new Set();
|
|
17513
|
+
const old_hidden_len_params = status.hidden_len_params;
|
|
17514
|
+
status.hidden_len_params = /* @__PURE__ */ new Set();
|
|
17515
|
+
const old_view_params = status.function_view_params;
|
|
17516
|
+
status.function_view_params = /* @__PURE__ */ new Set();
|
|
16627
17517
|
const old_return_type = status.function_return_type;
|
|
16628
17518
|
status.function_return_type = node.return_type;
|
|
16629
17519
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
@@ -16633,6 +17523,8 @@ function build_function_node(node, status) {
|
|
|
16633
17523
|
status.current_function_name = node.name;
|
|
16634
17524
|
for (let param of node.params) {
|
|
16635
17525
|
if (param.is_variadic) status.function_variadic_params.add(c_function_name(param.name));
|
|
17526
|
+
if (param.hidden_len) status.hidden_len_params.add(c_function_name(param.name));
|
|
17527
|
+
if (param.type.is_view && !param.is_self_param) status.function_view_params.add(c_function_name(param.name));
|
|
16636
17528
|
if (!param.is_variadic ? array_struct_name(param.type, status) : void 0) {
|
|
16637
17529
|
const pname = c_function_name(param.name);
|
|
16638
17530
|
if (!status.heap_array_vars) status.heap_array_vars = /* @__PURE__ */ new Set();
|
|
@@ -16665,6 +17557,8 @@ function build_function_node(node, status) {
|
|
|
16665
17557
|
status.ref_class_params = old_ref_class_params;
|
|
16666
17558
|
status.ref_class_param_types = old_ref_class_param_types;
|
|
16667
17559
|
status.function_variadic_params = old_variadic_params;
|
|
17560
|
+
status.hidden_len_params = old_hidden_len_params;
|
|
17561
|
+
status.function_view_params = old_view_params;
|
|
16668
17562
|
status.function_return_type = old_return_type;
|
|
16669
17563
|
status.nullable_ret_has_param = old_nullable_ret_has;
|
|
16670
17564
|
status.current_function_name = old_function_name;
|
|
@@ -17378,7 +18272,8 @@ function build_declaration_node(node, status) {
|
|
|
17378
18272
|
const val_node = node.value;
|
|
17379
18273
|
const val_is_string_literal = node.value.node_type === "value" && val_node.value.length >= 2 && val_node.value.startsWith("\"") && val_node.value.endsWith("\"");
|
|
17380
18274
|
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.
|
|
18275
|
+
if (node.type.name === "string" && !node.type.is_view && is_view_value(node.value, status)) c_materialize_view_string(node.value, status);
|
|
18276
|
+
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
18277
|
status.code += `strdup(`;
|
|
17383
18278
|
build_node(node.value, status);
|
|
17384
18279
|
status.code += `)`;
|
|
@@ -17595,10 +18490,17 @@ function build_function_call_node(node, status) {
|
|
|
17595
18490
|
}
|
|
17596
18491
|
const is_struct = status.structs.find((s) => s.name === node.name && !s.is_simple_type);
|
|
17597
18492
|
const func_name = is_struct ? `${node.name}_init` : c_function_name(node.name);
|
|
18493
|
+
const hidden_len = callee_hidden_len_indices(node);
|
|
18494
|
+
const hidden_wrap = open_hidden_len_wrap(hidden_len, node.params, status);
|
|
17598
18495
|
status.code += `${func_name}(`;
|
|
17599
18496
|
const variadic_idx = node.variadic_param_name ? node.params.findIndex((p) => p.node_type === "array") : -1;
|
|
17600
18497
|
for (let i = 0; i < node.params.length; i++) {
|
|
17601
18498
|
if (i > 0) status.code += ", ";
|
|
18499
|
+
const hidden_temp = hidden_wrap.temps.get(i);
|
|
18500
|
+
if (hidden_temp) {
|
|
18501
|
+
status.code += `${hidden_temp}, strlen(${hidden_temp})`;
|
|
18502
|
+
continue;
|
|
18503
|
+
}
|
|
17602
18504
|
if (i === variadic_idx && node.params[i].node_type === "array") {
|
|
17603
18505
|
const arr = node.params[i];
|
|
17604
18506
|
status.code += `${arr.values.length}, (${c_type(arr.type.name)}[]){`;
|
|
@@ -17620,6 +18522,10 @@ function build_function_call_node(node, status) {
|
|
|
17620
18522
|
status.suppress_dereference = false;
|
|
17621
18523
|
continue;
|
|
17622
18524
|
}
|
|
18525
|
+
if (node.view_param_indices?.includes(i)) {
|
|
18526
|
+
c_view_string_arg(node.params[i], status);
|
|
18527
|
+
continue;
|
|
18528
|
+
}
|
|
17623
18529
|
const is_ref_param = node.ref_param_indices?.includes(i);
|
|
17624
18530
|
const param_is_class = !!status.structs.find((s) => s.name === param_type.name && s.is_class);
|
|
17625
18531
|
if (is_ref_param && param_is_class) {
|
|
@@ -17649,6 +18555,10 @@ function build_function_call_node(node, status) {
|
|
|
17649
18555
|
status.code += `, `;
|
|
17650
18556
|
emit_nullable_arg_flag(node.params[i], status);
|
|
17651
18557
|
}
|
|
18558
|
+
if (hidden_len.includes(i)) {
|
|
18559
|
+
status.code += `, `;
|
|
18560
|
+
emit_hidden_len_expr(node.params[i], void 0, status);
|
|
18561
|
+
}
|
|
17652
18562
|
}
|
|
17653
18563
|
if (is_nullable_struct_type(node.type, status)) {
|
|
17654
18564
|
const flag_name = status.current_nullable_call_flag;
|
|
@@ -17664,6 +18574,7 @@ function build_function_call_node(node, status) {
|
|
|
17664
18574
|
}
|
|
17665
18575
|
}
|
|
17666
18576
|
status.code += ")";
|
|
18577
|
+
close_hidden_len_wrap(hidden_wrap, status);
|
|
17667
18578
|
if (node.name.startsWith("_string_interpolate_")) status.interpolate_string_counts.add(node.params.length - 1);
|
|
17668
18579
|
if (node.swap_params?.size) for (const [idx, swap_expr] of node.swap_params) {
|
|
17669
18580
|
const source = node.params[idx];
|
|
@@ -18072,10 +18983,13 @@ function build_return_node(node, status) {
|
|
|
18072
18983
|
}
|
|
18073
18984
|
}
|
|
18074
18985
|
if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `strdup(`;
|
|
18986
|
+
const wrap_view_string_return = !!ret_type.is_view && ret_type.name === "string" && !is_view_value(node.value, status);
|
|
18075
18987
|
const expr_type_name = node.type?.name || "";
|
|
18076
18988
|
const expr_is_simple = !status.structs.find((s) => s.name === expr_type_name && !s.is_simple_type);
|
|
18077
18989
|
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})`;
|
|
18990
|
+
if (wrap_view_string_return) status.code += `({ char* _p = `;
|
|
18078
18991
|
build_node(node.value, status);
|
|
18992
|
+
if (wrap_view_string_return) status.code += `; nomen_view _v = { (void*)_p, (long)strlen(_p) }; _v; })`;
|
|
18079
18993
|
if (returns_borrowed_string || returns_string_literal || returns_borrow_var) status.code += `)`;
|
|
18080
18994
|
status.code += `;\n`;
|
|
18081
18995
|
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 +19203,7 @@ function build_while_loop_node(node, status) {
|
|
|
18289
19203
|
if (!status.string_length_temps) status.string_length_temps = /* @__PURE__ */ new Map();
|
|
18290
19204
|
for (const [name, target] of hoists) {
|
|
18291
19205
|
if (status.string_length_temps.has(name)) continue;
|
|
19206
|
+
if (status.hidden_len_params?.has(c_function_name(name))) continue;
|
|
18292
19207
|
const temp = `_slh_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
18293
19208
|
status.string_length_temps.set(name, temp);
|
|
18294
19209
|
status.code += `const long ${temp} = (long)strlen(`;
|
|
@@ -20131,6 +21046,16 @@ function add_error(status, message, start) {
|
|
|
20131
21046
|
});
|
|
20132
21047
|
}
|
|
20133
21048
|
//#endregion
|
|
21049
|
+
//#region ../src/nodes/set_resolved_function.ts
|
|
21050
|
+
function set_resolved_function(node, func) {
|
|
21051
|
+
Object.defineProperty(node, "resolved_function", {
|
|
21052
|
+
value: func,
|
|
21053
|
+
enumerable: false,
|
|
21054
|
+
writable: true,
|
|
21055
|
+
configurable: true
|
|
21056
|
+
});
|
|
21057
|
+
}
|
|
21058
|
+
//#endregion
|
|
20134
21059
|
//#region ../src/nodes/clone_node.ts
|
|
20135
21060
|
function clone_type$2(type) {
|
|
20136
21061
|
const t = new Type(type.name, type.is_static, type.is_array, type.length ? clone_node(type.length) : void 0);
|
|
@@ -20165,10 +21090,13 @@ function clone_node(node) {
|
|
|
20165
21090
|
c.type_args = n.type_args?.map(clone_type$2);
|
|
20166
21091
|
c.ref_param_indices = n.ref_param_indices?.slice();
|
|
20167
21092
|
c.mov_param_indices = n.mov_param_indices?.slice();
|
|
21093
|
+
c.nullable_param_indices = n.nullable_param_indices?.slice();
|
|
21094
|
+
c.view_param_indices = n.view_param_indices?.slice();
|
|
20168
21095
|
c.swap_params = n.swap_params ? new Map([...n.swap_params].map(([k, v]) => [k, clone_node(v)])) : void 0;
|
|
20169
21096
|
c.variadic_param_name = n.variadic_param_name;
|
|
20170
21097
|
c.variadic_param_index = n.variadic_param_index;
|
|
20171
21098
|
c.is_enum_shorthand = n.is_enum_shorthand;
|
|
21099
|
+
set_resolved_function(c, n.resolved_function);
|
|
20172
21100
|
c.allocations = n.allocations?.map(clone_node);
|
|
20173
21101
|
return c;
|
|
20174
21102
|
}
|
|
@@ -20196,6 +21124,7 @@ function clone_node(node) {
|
|
|
20196
21124
|
c.mangled_name = n.mangled_name;
|
|
20197
21125
|
c.owned_return = n.owned_return;
|
|
20198
21126
|
c.nullable_param_indices = n.nullable_param_indices?.slice();
|
|
21127
|
+
c.view_param_indices = n.view_param_indices?.slice();
|
|
20199
21128
|
c.return_bounds = n.return_bounds ? {
|
|
20200
21129
|
upper: n.return_bounds.upper.slice(),
|
|
20201
21130
|
lower: n.return_bounds.lower.slice(),
|
|
@@ -20207,6 +21136,7 @@ function clone_node(node) {
|
|
|
20207
21136
|
c.function_return_type = n.function_return_type ? clone_type$2(n.function_return_type) : void 0;
|
|
20208
21137
|
c.is_statement = n.is_statement;
|
|
20209
21138
|
c.skip_bounds_check = n.skip_bounds_check;
|
|
21139
|
+
set_resolved_function(c, n.resolved_function);
|
|
20210
21140
|
c.allocations = n.allocations?.map(clone_node);
|
|
20211
21141
|
return c;
|
|
20212
21142
|
}
|
|
@@ -20405,7 +21335,10 @@ function clone_node(node) {
|
|
|
20405
21335
|
c.default_value_start = n.default_value_start;
|
|
20406
21336
|
c.is_variadic = n.is_variadic;
|
|
20407
21337
|
c.is_ref = n.is_ref;
|
|
21338
|
+
c.is_variadic_tuple = n.is_variadic_tuple;
|
|
21339
|
+
c.hidden_len = n.hidden_len;
|
|
20408
21340
|
c.constraint = n.constraint ? clone_node(n.constraint) : void 0;
|
|
21341
|
+
c.stripped_length_equalities = n.stripped_length_equalities?.map((e) => ({ ...e }));
|
|
20409
21342
|
c.func_params = n.func_params?.map((p) => clone_node(p));
|
|
20410
21343
|
c.func_return_type = n.func_return_type ? clone_type$2(n.func_return_type) : void 0;
|
|
20411
21344
|
c.allocations = n.allocations?.map(clone_node);
|
|
@@ -21111,6 +22044,66 @@ function strip_length_equalities(constraint, param_name, status) {
|
|
|
21111
22044
|
});
|
|
21112
22045
|
}
|
|
21113
22046
|
/**
|
|
22047
|
+
* Registration-time variant of strip_length_equalities: removes
|
|
22048
|
+
* `<container>.<size> == <container>.<size>` conjuncts from a parameter
|
|
22049
|
+
* constraint and returns them alongside the remaining constraint. Used when a
|
|
22050
|
+
* function signature is REGISTERED — before any call site can observe it — so
|
|
22051
|
+
* a forward-referenced callee never hands its caller a clause no call site
|
|
22052
|
+
* could prove (declaration order must not matter). Bases are validated
|
|
22053
|
+
* against `known_names` (the function's own parameters) instead of
|
|
22054
|
+
* status.values, because at registration time nothing has been pushed into
|
|
22055
|
+
* any scope yet. Does NOT touch status.equal_lengths: the extracted
|
|
22056
|
+
* equalities are returned for the callee's own param check to seed.
|
|
22057
|
+
*/
|
|
22058
|
+
function extract_length_equalities_at_registration(constraint, param_name, known_names) {
|
|
22059
|
+
const equalities = [];
|
|
22060
|
+
if (!constraint) return {
|
|
22061
|
+
constraint,
|
|
22062
|
+
equalities
|
|
22063
|
+
};
|
|
22064
|
+
const walk = (node) => {
|
|
22065
|
+
if (node.node_type !== "op") return node;
|
|
22066
|
+
const op = node;
|
|
22067
|
+
if (op.op === "&&") {
|
|
22068
|
+
const left = walk(op.left_value);
|
|
22069
|
+
const right = walk(op.right_value);
|
|
22070
|
+
if (left && right) {
|
|
22071
|
+
if (left === op.left_value && right === op.right_value) return node;
|
|
22072
|
+
return new OperationNode(op.start, "&&", left, right, op.type);
|
|
22073
|
+
}
|
|
22074
|
+
return left ?? right;
|
|
22075
|
+
}
|
|
22076
|
+
if (op.op !== "==") return node;
|
|
22077
|
+
const extract = (side) => {
|
|
22078
|
+
if (side.node_type !== "access") return void 0;
|
|
22079
|
+
const access = side;
|
|
22080
|
+
let field;
|
|
22081
|
+
if (access.access.node_type === "access_field") field = access.access.name;
|
|
22082
|
+
else if (access.access.node_type === "access_func") field = access.access.name;
|
|
22083
|
+
if (!field || !NON_NEGATIVE_FIELDS$1.has(field)) return void 0;
|
|
22084
|
+
if (access.target.node_type !== "value") return void 0;
|
|
22085
|
+
return {
|
|
22086
|
+
base: access.target.value,
|
|
22087
|
+
field
|
|
22088
|
+
};
|
|
22089
|
+
};
|
|
22090
|
+
const lhs = extract(op.left_value);
|
|
22091
|
+
const rhs = extract(op.right_value);
|
|
22092
|
+
if (!lhs || !rhs || lhs.field !== rhs.field) return node;
|
|
22093
|
+
if (lhs.base === rhs.base) return node;
|
|
22094
|
+
for (const base of [lhs.base, rhs.base]) if (base !== param_name && !known_names.has(base)) return node;
|
|
22095
|
+
equalities.push({
|
|
22096
|
+
a: lhs.base,
|
|
22097
|
+
b: rhs.base,
|
|
22098
|
+
field: lhs.field
|
|
22099
|
+
});
|
|
22100
|
+
};
|
|
22101
|
+
return {
|
|
22102
|
+
constraint: walk(constraint),
|
|
22103
|
+
equalities
|
|
22104
|
+
};
|
|
22105
|
+
}
|
|
22106
|
+
/**
|
|
21114
22107
|
* Extract flow-sensitive bounds from a comparison condition.
|
|
21115
22108
|
* E.g. `j < list.length` → { var: "j", op: "<", expr: "list.length" }
|
|
21116
22109
|
* Returns undefined if the condition isn't a simple var-vs-expr comparison.
|
|
@@ -22433,6 +23426,7 @@ function is_inside_core_method(status) {
|
|
|
22433
23426
|
return false;
|
|
22434
23427
|
}
|
|
22435
23428
|
function check_function_call(node, status, func, target_type, self_value, self_path) {
|
|
23429
|
+
set_resolved_function(node, func);
|
|
22436
23430
|
const access_scope = status.stack.at(-1);
|
|
22437
23431
|
if (func.name === "#init") {
|
|
22438
23432
|
const struct_name = target_type?.name || func.return_type.name;
|
|
@@ -22498,6 +23492,10 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
22498
23492
|
if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
|
|
22499
23493
|
}
|
|
22500
23494
|
}
|
|
23495
|
+
if (func_param && func_param.type.is_view && !func_param.is_self_param) {
|
|
23496
|
+
if (!node.view_param_indices) node.view_param_indices = [];
|
|
23497
|
+
if (!node.view_param_indices.includes(i)) node.view_param_indices.push(i);
|
|
23498
|
+
}
|
|
22501
23499
|
const old_expected_type = status.expected_type;
|
|
22502
23500
|
const old_allow_null = status.allow_null_value;
|
|
22503
23501
|
if (func_param) {
|
|
@@ -24075,6 +25073,29 @@ function clone_type(t) {
|
|
|
24075
25073
|
c.type_args = t.type_args;
|
|
24076
25074
|
return c;
|
|
24077
25075
|
}
|
|
25076
|
+
/**
|
|
25077
|
+
* Strip parallel-length equality clauses (`a.length == b.length`) from every
|
|
25078
|
+
* parameter's constraint of `func` at SIGNATURE-GATHER time — before any call
|
|
25079
|
+
* site can observe the signature, regardless of declaration order. A
|
|
25080
|
+
* forward-referenced callee must not hand its caller a clause no call site
|
|
25081
|
+
* could ever prove; the clause becomes an assumed equality for the callee's
|
|
25082
|
+
* own body instead (stashed on the param, seeded into scope when its params
|
|
25083
|
+
* are checked). Idempotent: a constraint already stripped has no clauses
|
|
25084
|
+
* left to extract. `check_function_node` re-runs the same extraction at
|
|
25085
|
+
* registration for function clones that never pass through a gather pass
|
|
25086
|
+
* (generic monomorphizations).
|
|
25087
|
+
*/
|
|
25088
|
+
function strip_param_length_equalities(func) {
|
|
25089
|
+
const param_names = new Set(func.params.map((p) => p.name));
|
|
25090
|
+
for (const param of func.params) {
|
|
25091
|
+
if (!param.constraint) continue;
|
|
25092
|
+
const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
|
|
25093
|
+
if (equalities.length) {
|
|
25094
|
+
param.constraint = constraint;
|
|
25095
|
+
param.stripped_length_equalities = equalities;
|
|
25096
|
+
}
|
|
25097
|
+
}
|
|
25098
|
+
}
|
|
24078
25099
|
function gather_structs(block, status) {
|
|
24079
25100
|
const names_in_block = {
|
|
24080
25101
|
structs: /* @__PURE__ */ new Set(),
|
|
@@ -24091,6 +25112,7 @@ function gather_structs(block, status) {
|
|
|
24091
25112
|
names_in_block.structs.add(struct.name);
|
|
24092
25113
|
struct.scope = status.stack.at(-1) || block;
|
|
24093
25114
|
struct.is_generic = struct.type_params.length > 0;
|
|
25115
|
+
for (const func of struct.functions) strip_param_length_equalities(func);
|
|
24094
25116
|
status.types.push(struct.name);
|
|
24095
25117
|
status.structs.push(struct);
|
|
24096
25118
|
}
|
|
@@ -24103,6 +25125,7 @@ function gather_structs(block, status) {
|
|
|
24103
25125
|
names_in_block.traits.add(trait.name);
|
|
24104
25126
|
status.types.push(trait.name);
|
|
24105
25127
|
status.traits.push(trait);
|
|
25128
|
+
for (const func of trait.functions) strip_param_length_equalities(func);
|
|
24106
25129
|
}
|
|
24107
25130
|
break;
|
|
24108
25131
|
}
|
|
@@ -24114,6 +25137,7 @@ function gather_structs(block, status) {
|
|
|
24114
25137
|
func.scope = status.stack.at(-1) || block;
|
|
24115
25138
|
func.return_type = materialize_type(func.return_type, status);
|
|
24116
25139
|
instantiate_generic_type(func.return_type, status);
|
|
25140
|
+
strip_param_length_equalities(func);
|
|
24117
25141
|
status.functions.push(func);
|
|
24118
25142
|
}
|
|
24119
25143
|
break;
|
|
@@ -24179,7 +25203,10 @@ function apply_extensions(block, status) {
|
|
|
24179
25203
|
continue;
|
|
24180
25204
|
}
|
|
24181
25205
|
ext.scope = target;
|
|
24182
|
-
for (const func of ext.functions)
|
|
25206
|
+
for (const func of ext.functions) {
|
|
25207
|
+
strip_param_length_equalities(func);
|
|
25208
|
+
target.functions.push(func);
|
|
25209
|
+
}
|
|
24183
25210
|
for (let i = 0; i < ext.traits.length; i++) {
|
|
24184
25211
|
if (target.traits.includes(ext.traits[i])) {
|
|
24185
25212
|
add_error(status, `Type '${ext.name}' already conforms to trait '${ext.traits[i]}'`, ext.start);
|
|
@@ -24227,6 +25254,10 @@ function check_function_parameter_node(param, status) {
|
|
|
24227
25254
|
is_set: true,
|
|
24228
25255
|
is_null: param.type.is_nullable ? true : void 0
|
|
24229
25256
|
});
|
|
25257
|
+
if (param.stripped_length_equalities?.length) {
|
|
25258
|
+
if (!status.equal_lengths) status.equal_lengths = [];
|
|
25259
|
+
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);
|
|
25260
|
+
}
|
|
24230
25261
|
if (param.constraint) {
|
|
24231
25262
|
const stripped = strip_length_equalities(param.constraint, param.name, status);
|
|
24232
25263
|
if (stripped !== param.constraint) {
|
|
@@ -24289,6 +25320,15 @@ function check_function_node(func, status) {
|
|
|
24289
25320
|
if (func.checked) return;
|
|
24290
25321
|
func.checked = true;
|
|
24291
25322
|
status.functions.push(func);
|
|
25323
|
+
const param_names = new Set(func.params.map((p) => p.name));
|
|
25324
|
+
for (const param of func.params) {
|
|
25325
|
+
if (!param.constraint) continue;
|
|
25326
|
+
const { constraint, equalities } = extract_length_equalities_at_registration(param.constraint, param.name, param_names);
|
|
25327
|
+
if (equalities.length) {
|
|
25328
|
+
param.constraint = constraint;
|
|
25329
|
+
param.stripped_length_equalities = equalities;
|
|
25330
|
+
}
|
|
25331
|
+
}
|
|
24292
25332
|
if (is_generic_func(func)) {
|
|
24293
25333
|
func.is_generic = true;
|
|
24294
25334
|
return;
|
|
@@ -26905,7 +27945,7 @@ function check_if_else_node(if_else, status) {
|
|
|
26905
27945
|
const else_returns = if_else.else_branch && block_always_returns(if_else.else_branch);
|
|
26906
27946
|
const if_falls_through = if_reachable && !if_returns;
|
|
26907
27947
|
const else_falls_through = else_reachable && (!has_else || !else_returns);
|
|
26908
|
-
if (!has_else && else_reachable) apply_negated_bounds(if_else.condition, status);
|
|
27948
|
+
if (!has_else && else_reachable && !if_returns) apply_negated_bounds(if_else.condition, status);
|
|
26909
27949
|
for (let [i, value] of status.values.entries()) {
|
|
26910
27950
|
const contributing_sets = [];
|
|
26911
27951
|
if (if_falls_through) contributing_sets.push(!!if_status.values[i].is_set);
|
|
@@ -26959,6 +27999,7 @@ function check_if_else_node(if_else, status) {
|
|
|
26959
27999
|
}
|
|
26960
28000
|
}
|
|
26961
28001
|
}
|
|
28002
|
+
if (!has_else && if_returns && else_reachable) apply_negated_bounds(if_else.condition, status);
|
|
26962
28003
|
if (if_else.if_branch && !if_else.else_branch) {
|
|
26963
28004
|
const parent = status.stack.at(-1);
|
|
26964
28005
|
if (parent && (parent.node_type === "declare" || parent.node_type === "assign")) add_error(status, "If expression must have an else branch", if_else.start);
|
|
@@ -27711,6 +28752,251 @@ function promote_allocations(node, status) {
|
|
|
27711
28752
|
}
|
|
27712
28753
|
}
|
|
27713
28754
|
//#endregion
|
|
28755
|
+
//#region ../src/check/stamp_hidden_string_lens.ts
|
|
28756
|
+
/**
|
|
28757
|
+
* Post-check pass implementing the PERF gap 2.4 fix: thread a string's
|
|
28758
|
+
* length through the call boundary so a per-character helper doesn't pay one
|
|
28759
|
+
* `strlen` per CALL.
|
|
28760
|
+
*
|
|
28761
|
+
* For every function whose Nomen-level body reads `<param>.length` on a
|
|
28762
|
+
* by-value `string` param (and never rebinds, mutates, or escapes it), the
|
|
28763
|
+
* param is stamped `hidden_len`. The backends then lower it as TWO
|
|
28764
|
+
* parameters — the `char*` plus a trailing `long _<name>_len` companion
|
|
28765
|
+
* (mirroring the nullable `_has` companion) — and the body's `.length` reads
|
|
28766
|
+
* become loads of that companion instead of `strlen`. Call sites append the
|
|
28767
|
+
* companion argument, preferring a loop-invariant hoisted strlen temp
|
|
28768
|
+
* (scan_string_length_hoists treats such args like `.length` reads), so a
|
|
28769
|
+
* helper called once per line over a large document costs ONE strlen, not
|
|
28770
|
+
* O(lines).
|
|
28771
|
+
*
|
|
28772
|
+
* Conservative exclusions (any of these keeps the plain one-pointer ABI):
|
|
28773
|
+
* - raw blocks anywhere in the body (opaque code may touch the param), or no
|
|
28774
|
+
* body at all
|
|
28775
|
+
* - `#init`/`#destroy`/`init`/`destroy`/`main` and any non-identifier or
|
|
28776
|
+
* `_`/`#`-prefixed name (operators, compiler-generated helpers) — their
|
|
28777
|
+
* call sites use bespoke emission paths
|
|
28778
|
+
* - variadic params anywhere (their call sites pack hidden count/ptr pairs)
|
|
28779
|
+
* - `is_inline` functions (the aarch64 inline splicer)
|
|
28780
|
+
* - trait method declarations/default bodies, and struct methods that
|
|
28781
|
+
* implement a trait method (vtable dispatch uses the trait's declared
|
|
28782
|
+
* signature)
|
|
28783
|
+
* - functions referenced as VALUES anywhere in the program (`var func f =
|
|
28784
|
+
* helper`, `obj.method` as a value, nursery.spawn targets) — indirect
|
|
28785
|
+
* calls use the declared ABI
|
|
28786
|
+
* - params that are ref/mov/view/nullable/variadic/self, or that the body
|
|
28787
|
+
* assigns to, passes ref/mov/swap, mutates through a `var self`/`ref self`
|
|
28788
|
+
* string method, or references across a nested func/async boundary, or
|
|
28789
|
+
* whose companion name `_<name>_len` collides with any declared name in
|
|
28790
|
+
* the body
|
|
28791
|
+
*/
|
|
28792
|
+
const NON_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
28793
|
+
"true",
|
|
28794
|
+
"false",
|
|
28795
|
+
"null",
|
|
28796
|
+
"self",
|
|
28797
|
+
"as",
|
|
28798
|
+
"default"
|
|
28799
|
+
]);
|
|
28800
|
+
function is_plain_identifier(value) {
|
|
28801
|
+
if (!value) return false;
|
|
28802
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) return false;
|
|
28803
|
+
if (value.startsWith("_")) return false;
|
|
28804
|
+
return !NON_IDENTIFIERS.has(value);
|
|
28805
|
+
}
|
|
28806
|
+
function root_name(node) {
|
|
28807
|
+
if (!node) return void 0;
|
|
28808
|
+
if (node.node_type === "value") {
|
|
28809
|
+
const v = node.value;
|
|
28810
|
+
return is_plain_identifier(v) ? v : void 0;
|
|
28811
|
+
}
|
|
28812
|
+
if (node.node_type === "access") return root_name(node.target);
|
|
28813
|
+
}
|
|
28814
|
+
function collect_functions(root, status) {
|
|
28815
|
+
const out = [];
|
|
28816
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28817
|
+
const add = (func, struct, is_trait_func) => {
|
|
28818
|
+
if (seen.has(func)) return;
|
|
28819
|
+
seen.add(func);
|
|
28820
|
+
out.push({
|
|
28821
|
+
func,
|
|
28822
|
+
struct,
|
|
28823
|
+
is_trait_func
|
|
28824
|
+
});
|
|
28825
|
+
};
|
|
28826
|
+
for (const struct of status.structs) for (const func of struct.functions) add(func, struct);
|
|
28827
|
+
for (const trait of status.traits) for (const func of trait.functions) add(func, void 0, true);
|
|
28828
|
+
for (const func of status.functions) add(func);
|
|
28829
|
+
walk_tree(root, (n) => {
|
|
28830
|
+
if (n.node_type === "struct") {
|
|
28831
|
+
const s = n;
|
|
28832
|
+
for (const func of s.functions) add(func, s);
|
|
28833
|
+
} else if (n.node_type === "trait") {
|
|
28834
|
+
const t = n;
|
|
28835
|
+
for (const func of t.functions) add(func, void 0, true);
|
|
28836
|
+
} else if (n.node_type === "func") add(n);
|
|
28837
|
+
});
|
|
28838
|
+
return out;
|
|
28839
|
+
}
|
|
28840
|
+
/** Names of functions referenced as VALUES (func-typed, non-call nodes). */
|
|
28841
|
+
function collect_value_referenced_names(root, status) {
|
|
28842
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
28843
|
+
const visit = (n) => {
|
|
28844
|
+
if (n.type?.name !== "func") return;
|
|
28845
|
+
if (n.node_type === "value") {
|
|
28846
|
+
const v = n.value;
|
|
28847
|
+
if (is_plain_identifier(v)) excluded.add(v);
|
|
28848
|
+
} else if (n.node_type === "access_field") {
|
|
28849
|
+
const name = n.name;
|
|
28850
|
+
if (is_plain_identifier(name)) excluded.add(name);
|
|
28851
|
+
}
|
|
28852
|
+
};
|
|
28853
|
+
walk_tree(root, visit);
|
|
28854
|
+
for (const struct of status.structs) walk_tree(struct, visit);
|
|
28855
|
+
for (const trait of status.traits) walk_tree(trait, visit);
|
|
28856
|
+
for (const func of status.functions) walk_tree(func, visit);
|
|
28857
|
+
return excluded;
|
|
28858
|
+
}
|
|
28859
|
+
function walk_tree(node, visit) {
|
|
28860
|
+
if (!node || typeof node !== "object") return;
|
|
28861
|
+
if (Array.isArray(node)) {
|
|
28862
|
+
for (const item of node) walk_tree(item, visit);
|
|
28863
|
+
return;
|
|
28864
|
+
}
|
|
28865
|
+
const n = node;
|
|
28866
|
+
if (typeof n.node_type !== "string") return;
|
|
28867
|
+
visit(n);
|
|
28868
|
+
const record = n;
|
|
28869
|
+
for (const key of Object.keys(record)) {
|
|
28870
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
28871
|
+
walk_tree(record[key], visit);
|
|
28872
|
+
}
|
|
28873
|
+
}
|
|
28874
|
+
function stamp_hidden_string_lens(root, status) {
|
|
28875
|
+
const value_referenced = collect_value_referenced_names(root, status);
|
|
28876
|
+
const owners = collect_functions(root, status);
|
|
28877
|
+
const string_struct = status.structs.find((s) => s.name === "string");
|
|
28878
|
+
for (const { func, struct, is_trait_func } of owners) {
|
|
28879
|
+
if (func.hidden_len_stamped) continue;
|
|
28880
|
+
func.hidden_len_stamped = true;
|
|
28881
|
+
if (!func.has_body || func.statements.length === 0) continue;
|
|
28882
|
+
if (func.is_inline || is_trait_func) continue;
|
|
28883
|
+
const bare_name = func.name.replace(/^#/, "");
|
|
28884
|
+
if (!is_plain_identifier(func.name)) continue;
|
|
28885
|
+
if ([
|
|
28886
|
+
"main",
|
|
28887
|
+
"init",
|
|
28888
|
+
"destroy"
|
|
28889
|
+
].includes(bare_name)) continue;
|
|
28890
|
+
if (value_referenced.has(func.name)) continue;
|
|
28891
|
+
if (func.params.some((p) => p.is_variadic || p.is_variadic_tuple)) continue;
|
|
28892
|
+
if (struct && trait_declares_method(struct, func.name, status)) continue;
|
|
28893
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
28894
|
+
for (const param of func.params) if (param_is_hidden_len_candidate(param)) candidates.add(param.name);
|
|
28895
|
+
if (candidates.size === 0) continue;
|
|
28896
|
+
const reads = /* @__PURE__ */ new Set();
|
|
28897
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
28898
|
+
const declared_names = /* @__PURE__ */ new Set();
|
|
28899
|
+
let has_raw = false;
|
|
28900
|
+
const visit_children = (n, in_boundary) => {
|
|
28901
|
+
const record = n;
|
|
28902
|
+
for (const key of Object.keys(record)) {
|
|
28903
|
+
if (key === "parent" || key === "scope" || key === "node_type") continue;
|
|
28904
|
+
const child = record[key];
|
|
28905
|
+
if (Array.isArray(child)) {
|
|
28906
|
+
for (const item of child) if (item && typeof item === "object" && item.node_type) visit(item, in_boundary);
|
|
28907
|
+
} else if (child && typeof child === "object" && child.node_type) visit(child, in_boundary);
|
|
28908
|
+
}
|
|
28909
|
+
};
|
|
28910
|
+
const visit = (n, in_boundary) => {
|
|
28911
|
+
switch (n.node_type) {
|
|
28912
|
+
case "raw":
|
|
28913
|
+
has_raw = true;
|
|
28914
|
+
return;
|
|
28915
|
+
case "func":
|
|
28916
|
+
case "async_block":
|
|
28917
|
+
visit_children(n, true);
|
|
28918
|
+
return;
|
|
28919
|
+
case "value": {
|
|
28920
|
+
const v = n.value;
|
|
28921
|
+
if (in_boundary && is_plain_identifier(v)) invalidated.add(v);
|
|
28922
|
+
return;
|
|
28923
|
+
}
|
|
28924
|
+
case "access": {
|
|
28925
|
+
const access = n;
|
|
28926
|
+
if (!in_boundary && access.access.node_type === "access_field" && access.access.name === "length" && access.target.node_type === "value") {
|
|
28927
|
+
const name = access.target.value;
|
|
28928
|
+
if (name !== void 0 && candidates.has(name)) reads.add(name);
|
|
28929
|
+
}
|
|
28930
|
+
if (!in_boundary && access.access.node_type === "access_func" && access.target.node_type === "value") {
|
|
28931
|
+
const name = access.target.value;
|
|
28932
|
+
const call = access.access;
|
|
28933
|
+
if (name !== void 0 && candidates.has(name)) {
|
|
28934
|
+
const self_param = (string_struct?.functions.find((f) => f.name === call.name || f.name === `#${call.name}`))?.params?.[0];
|
|
28935
|
+
if (self_param?.is_self_param && (self_param.declaration === "var" || self_param.is_ref)) invalidated.add(name);
|
|
28936
|
+
}
|
|
28937
|
+
}
|
|
28938
|
+
visit_children(n, in_boundary);
|
|
28939
|
+
return;
|
|
28940
|
+
}
|
|
28941
|
+
case "assign": {
|
|
28942
|
+
const assign = n;
|
|
28943
|
+
const lhs = root_name(assign.left_value);
|
|
28944
|
+
if (lhs) invalidated.add(lhs);
|
|
28945
|
+
const swap = root_name(assign.swap);
|
|
28946
|
+
if (swap) invalidated.add(swap);
|
|
28947
|
+
visit_children(n, in_boundary);
|
|
28948
|
+
return;
|
|
28949
|
+
}
|
|
28950
|
+
case "declare":
|
|
28951
|
+
case "param": {
|
|
28952
|
+
const name = n.name;
|
|
28953
|
+
if (name) declared_names.add(name);
|
|
28954
|
+
visit_children(n, in_boundary);
|
|
28955
|
+
return;
|
|
28956
|
+
}
|
|
28957
|
+
case "for": {
|
|
28958
|
+
const item = n.item;
|
|
28959
|
+
if (item?.value) declared_names.add(item.value);
|
|
28960
|
+
visit_children(n, in_boundary);
|
|
28961
|
+
return;
|
|
28962
|
+
}
|
|
28963
|
+
case "func_call":
|
|
28964
|
+
case "access_func": {
|
|
28965
|
+
const call = n;
|
|
28966
|
+
for (const indices of [call.ref_param_indices, call.mov_param_indices]) for (const i of indices ?? []) {
|
|
28967
|
+
const arg = root_name(call.params?.[i]);
|
|
28968
|
+
if (arg) invalidated.add(arg);
|
|
28969
|
+
}
|
|
28970
|
+
if (call.swap_params) {
|
|
28971
|
+
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);
|
|
28972
|
+
}
|
|
28973
|
+
visit_children(n, in_boundary);
|
|
28974
|
+
return;
|
|
28975
|
+
}
|
|
28976
|
+
default:
|
|
28977
|
+
visit_children(n, in_boundary);
|
|
28978
|
+
return;
|
|
28979
|
+
}
|
|
28980
|
+
};
|
|
28981
|
+
for (const stmt of func.statements) visit(stmt, false);
|
|
28982
|
+
if (has_raw) continue;
|
|
28983
|
+
for (const name of candidates) {
|
|
28984
|
+
if (!reads.has(name)) continue;
|
|
28985
|
+
if (invalidated.has(name)) continue;
|
|
28986
|
+
if (declared_names.has(`_${name}_len`)) continue;
|
|
28987
|
+
const param = func.params.find((p) => p.name === name);
|
|
28988
|
+
param.hidden_len = true;
|
|
28989
|
+
}
|
|
28990
|
+
}
|
|
28991
|
+
}
|
|
28992
|
+
function trait_declares_method(struct, method_name, status) {
|
|
28993
|
+
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;
|
|
28994
|
+
return false;
|
|
28995
|
+
}
|
|
28996
|
+
function param_is_hidden_len_candidate(param) {
|
|
28997
|
+
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;
|
|
28998
|
+
}
|
|
28999
|
+
//#endregion
|
|
27714
29000
|
//#region ../src/check/warnings.ts
|
|
27715
29001
|
/**
|
|
27716
29002
|
* Lint pass run after a successful check. It walks the AST to surface three
|
|
@@ -27913,6 +29199,7 @@ function check(root) {
|
|
|
27913
29199
|
mutated_local_names: /* @__PURE__ */ new Set()
|
|
27914
29200
|
};
|
|
27915
29201
|
check_node(root, status);
|
|
29202
|
+
stamp_hidden_string_lens(root, status);
|
|
27916
29203
|
if (!status.errors.length) emit_warnings(root, status);
|
|
27917
29204
|
return {
|
|
27918
29205
|
ok: !status.errors.length,
|