nomen-lang 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOMEN_AGENTS.md +12 -3
- package/core/System/Buffer.nm +38 -0
- package/dist/index.mjs +942 -229
- package/package.json +1 -1
- package/src/args.ts +1 -1
- package/src/index.ts +1 -0
- package/src/test.ts +40 -3
package/dist/index.mjs
CHANGED
|
@@ -6811,6 +6811,27 @@ function find_enum(name, status) {
|
|
|
6811
6811
|
}
|
|
6812
6812
|
//#endregion
|
|
6813
6813
|
//#region ../src/build_common/mono_name.ts
|
|
6814
|
+
/**
|
|
6815
|
+
* Whether `name` is a monomorphized owning-container struct whose
|
|
6816
|
+
* `<Mono>_copy` is safe to call from the owning-`Buffer.load_T`
|
|
6817
|
+
* specialization — `List_<T>` / `Buffer_<T>` (and their bare generics).
|
|
6818
|
+
* These own a heap slab their `#destroy` frees unconditionally, and `copy`
|
|
6819
|
+
* yields an independent slab, so a returned element's field must be
|
|
6820
|
+
* deep-copied or the caller's unconditional container-field teardown
|
|
6821
|
+
* double-frees the slot's slab.
|
|
6822
|
+
*
|
|
6823
|
+
* `Map`/`Set` are deliberately EXCLUDED from the automatic deep copy: their
|
|
6824
|
+
* monomorphized `copy` (which composes several `Buffer.copy` calls) is
|
|
6825
|
+
* verified standalone on both backends, but invoking it from the load
|
|
6826
|
+
* specialization corrupts the returned struct on aarch64 (a codegen
|
|
6827
|
+
* interaction still open — see FOLLOWUP.md). A `Map`/`Set` FIELD reached
|
|
6828
|
+
* through a container element's `load` therefore stays shallow (the
|
|
6829
|
+
* pre-existing bounded gap); an EXPLICIT `.copy()` on the owning struct or
|
|
6830
|
+
* the Map/Set itself works on both backends.
|
|
6831
|
+
*/
|
|
6832
|
+
function is_load_deep_copy_container(name) {
|
|
6833
|
+
return name === "List" || name === "Buffer" || name.startsWith("List_") || name.startsWith("Buffer_");
|
|
6834
|
+
}
|
|
6814
6835
|
function mono_type_name(type_or_name, type_args) {
|
|
6815
6836
|
const name = typeof type_or_name === "string" ? type_or_name : type_or_name.name;
|
|
6816
6837
|
const args = typeof type_or_name === "string" ? type_args : type_or_name.type_args;
|
|
@@ -7147,14 +7168,37 @@ function call_result_type(node) {
|
|
|
7147
7168
|
* so `null == 0` conflates them. Nullable `string` (in-band fat zero) and
|
|
7148
7169
|
* nullable `func`/class values (null pointer) keep their in-band null and
|
|
7149
7170
|
* are NOT nullable scalars.
|
|
7171
|
+
*
|
|
7172
|
+
* The same holds for a nullable SIMPLE enum (`Color?`) or bitset
|
|
7173
|
+
* (`Permissions?`): the in-band zero (the first case's tag 0 / an empty
|
|
7174
|
+
* bitset) is a real value, so `null` needs the companion flag. Enum-with-data
|
|
7175
|
+
* and generic template enums are NOT nullable scalars — the checker rejects
|
|
7176
|
+
* `?` on enums with associated data, so every nullable enum that reaches the
|
|
7177
|
+
* backends is simple.
|
|
7150
7178
|
*/
|
|
7151
|
-
/** True if
|
|
7152
|
-
|
|
7179
|
+
/** True if the TYPE NAME is a built-in scalar, simple enum, or bitset — the
|
|
7180
|
+
* shapes that get the `<slot>_has` flag when nullable. */
|
|
7181
|
+
function is_nullable_scalar_name(name, tables) {
|
|
7182
|
+
if (!name) return false;
|
|
7183
|
+
const info = get_built_in_type(name);
|
|
7184
|
+
if (info) return info.kind !== "string" && info.kind !== "func";
|
|
7185
|
+
const enum_node = tables.enums.findLast((e) => (e.name === name || e.source_name === name) && !e.is_generic);
|
|
7186
|
+
if (enum_node) return !enum_node.has_associated_data;
|
|
7187
|
+
return tables.bitsets.some((b) => b.name === name || b.source_name === name);
|
|
7188
|
+
}
|
|
7189
|
+
/** True if `type` is a nullable built-in scalar, simple enum, or bitset. */
|
|
7190
|
+
function is_nullable_scalar_type(type, tables) {
|
|
7153
7191
|
if (!type?.is_nullable) return false;
|
|
7154
7192
|
if (type.is_array || type.is_ref || type.storage_kind === "view") return false;
|
|
7155
7193
|
if (type.is_pointer) return false;
|
|
7156
|
-
|
|
7157
|
-
|
|
7194
|
+
return is_nullable_scalar_name(type.name, tables);
|
|
7195
|
+
}
|
|
7196
|
+
/** True if `type` is a nullable struct (see nullable_struct.ts) or nullable scalar. */
|
|
7197
|
+
function is_nullable_flagged_type(type, status) {
|
|
7198
|
+
if (!type?.is_nullable) return false;
|
|
7199
|
+
if (type.name === "string") return false;
|
|
7200
|
+
if (type.name === "func") return false;
|
|
7201
|
+
return is_nullable_scalar_type(type, status) || !!status.structs.find((s) => s.name === type.name && !s.is_class && !s.is_simple_type);
|
|
7158
7202
|
}
|
|
7159
7203
|
//#endregion
|
|
7160
7204
|
//#region ../src/build_common/nullable_struct.ts
|
|
@@ -7653,10 +7697,15 @@ function direct_string_fields(node) {
|
|
|
7653
7697
|
* callee's own records are scope-local and die at return, so without the
|
|
7654
7698
|
* transfer the stored copy leaks.
|
|
7655
7699
|
*
|
|
7656
|
-
* Only DIRECT statements qualify: a store nested in a
|
|
7657
|
-
* execute, and a record over the pre-call value would
|
|
7658
|
-
* scope exit (possibly rodata — an invalid free).
|
|
7659
|
-
*
|
|
7700
|
+
* Only DIRECT statements qualify for the plain transfer: a store nested in a
|
|
7701
|
+
* branch/loop may not execute, and a record over the pre-call value would
|
|
7702
|
+
* free it at the caller's scope exit (possibly rodata — an invalid free).
|
|
7703
|
+
* Those CONDITIONAL stores are reported separately
|
|
7704
|
+
* (scan_ref_param_string_field_may_writes): the callee's function builder
|
|
7705
|
+
* emits an ENTRY DUP for each of them (`field = strdup(field)` at function
|
|
7706
|
+
* entry, recorded), which makes the field heap-owned on EVERY path and the
|
|
7707
|
+
* may-store record sound to transfer. Memoized on the function node;
|
|
7708
|
+
* forwarding cycles are cut by a visited set (the cycle member's own
|
|
7660
7709
|
* contribution is skipped, bounded-leak).
|
|
7661
7710
|
*/
|
|
7662
7711
|
function scan_ref_param_string_field_writes(func, structs) {
|
|
@@ -7664,6 +7713,51 @@ function scan_ref_param_string_field_writes(func, structs) {
|
|
|
7664
7713
|
if (stamp._ref_param_string_field_writes) return stamp._ref_param_string_field_writes;
|
|
7665
7714
|
const result = /* @__PURE__ */ new Map();
|
|
7666
7715
|
stamp._ref_param_string_field_writes = result;
|
|
7716
|
+
const tracked = tracked_ref_params(func, structs);
|
|
7717
|
+
if (tracked.size) scan_top_level(func.statements ?? [], tracked, structs, result);
|
|
7718
|
+
return result;
|
|
7719
|
+
}
|
|
7720
|
+
/**
|
|
7721
|
+
* The CONDITIONAL (`may-executed`) ref-param field stores: direct stores
|
|
7722
|
+
* nested in if/else branches, switch cases, and loop bodies. Forwarded
|
|
7723
|
+
* calls from a nested position are deliberately EXCLUDED: a helper's store
|
|
7724
|
+
* builds with its own (fresh) record set, so it cannot reclaim the
|
|
7725
|
+
* displaced entry-dup — dup'ing for a forwarded store would trade the
|
|
7726
|
+
* old bounded leak for a new one. Those chains keep the documented
|
|
7727
|
+
* bounded-leak posture (the stored copy leaks, never an invalid free).
|
|
7728
|
+
* The function builder entry-dups these fields (see
|
|
7729
|
+
* ref_param_entry_dup_fields), making the may-store record transferable.
|
|
7730
|
+
*/
|
|
7731
|
+
function scan_ref_param_string_field_may_writes(func, structs) {
|
|
7732
|
+
const stamp = func;
|
|
7733
|
+
if (stamp._ref_param_string_field_may_writes) return stamp._ref_param_string_field_may_writes;
|
|
7734
|
+
const result = /* @__PURE__ */ new Map();
|
|
7735
|
+
stamp._ref_param_string_field_may_writes = result;
|
|
7736
|
+
const tracked = tracked_ref_params(func, structs);
|
|
7737
|
+
if (tracked.size) scan_nested(func.statements ?? [], tracked, result);
|
|
7738
|
+
return result;
|
|
7739
|
+
}
|
|
7740
|
+
/**
|
|
7741
|
+
* The fields a function's builder must ENTRY DUP: the conditional
|
|
7742
|
+
* (may-executed) ref-param string-field stores that are NOT also
|
|
7743
|
+
* definitely-executed (a must-store already leaves the field heap-owned on
|
|
7744
|
+
* every path — dup'ing it too would just add a pointless strdup per call).
|
|
7745
|
+
* The dup runs at callee entry on both backends and records the field, so a
|
|
7746
|
+
* call site may transfer these as heap records soundly.
|
|
7747
|
+
*/
|
|
7748
|
+
function ref_param_entry_dup_fields(func, structs) {
|
|
7749
|
+
const must = scan_ref_param_string_field_writes(func, structs);
|
|
7750
|
+
const may = scan_ref_param_string_field_may_writes(func, structs);
|
|
7751
|
+
const result = /* @__PURE__ */ new Map();
|
|
7752
|
+
for (const [param, fields] of may) {
|
|
7753
|
+
const dup = /* @__PURE__ */ new Set();
|
|
7754
|
+
for (const field of fields) if (!must.get(param)?.has(field)) dup.add(field);
|
|
7755
|
+
if (dup.size) result.set(param, dup);
|
|
7756
|
+
}
|
|
7757
|
+
return result;
|
|
7758
|
+
}
|
|
7759
|
+
/** The tracked ref params of `func`: name -> writable plain string fields. */
|
|
7760
|
+
function tracked_ref_params(func, structs) {
|
|
7667
7761
|
const tracked = /* @__PURE__ */ new Map();
|
|
7668
7762
|
for (const param of func.params ?? []) {
|
|
7669
7763
|
if (param.is_self_param) continue;
|
|
@@ -7672,8 +7766,7 @@ function scan_ref_param_string_field_writes(func, structs) {
|
|
|
7672
7766
|
const fields = ref_param_writable_fields(param.type.name, structs);
|
|
7673
7767
|
if (fields.size) tracked.set(param.name, fields);
|
|
7674
7768
|
}
|
|
7675
|
-
|
|
7676
|
-
return result;
|
|
7769
|
+
return tracked;
|
|
7677
7770
|
}
|
|
7678
7771
|
/** The writable plain string fields of the value struct `type_name` resolves
|
|
7679
7772
|
* to, or the empty set (non-struct, class, or no string fields). */
|
|
@@ -7683,46 +7776,91 @@ function ref_param_writable_fields(type_name, structs) {
|
|
|
7683
7776
|
if (!struct) return /* @__PURE__ */ new Set();
|
|
7684
7777
|
return new Set(direct_string_fields(struct).map((f) => f.name));
|
|
7685
7778
|
}
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7779
|
+
/**
|
|
7780
|
+
* The top-level (definitely-executed) walk: direct `param.field = …`
|
|
7781
|
+
* statements and top-level forwarded calls only — nothing nested.
|
|
7782
|
+
*/
|
|
7783
|
+
function scan_top_level(statements, tracked, structs, result) {
|
|
7784
|
+
for (const stmt of statements) {
|
|
7689
7785
|
if (stmt.node_type === "assign") {
|
|
7690
|
-
|
|
7691
|
-
if (lhs?.node_type === "access") {
|
|
7692
|
-
const access = lhs;
|
|
7693
|
-
if (access.access.node_type === "access_field" && access.target.node_type === "value") {
|
|
7694
|
-
const name = access.target.value ?? "";
|
|
7695
|
-
const fields = tracked.get(name);
|
|
7696
|
-
const field = access.access.name ?? "";
|
|
7697
|
-
if (fields?.has(field)) {
|
|
7698
|
-
if (!result.has(name)) result.set(name, /* @__PURE__ */ new Set());
|
|
7699
|
-
result.get(name).add(field);
|
|
7700
|
-
}
|
|
7701
|
-
}
|
|
7702
|
-
}
|
|
7786
|
+
record_direct_store(stmt, tracked, result);
|
|
7703
7787
|
continue;
|
|
7704
7788
|
}
|
|
7705
7789
|
const call = ref_forward_call(stmt);
|
|
7706
|
-
if (
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7790
|
+
if (call) merge_forwarded(call, tracked, structs, result);
|
|
7791
|
+
}
|
|
7792
|
+
}
|
|
7793
|
+
/**
|
|
7794
|
+
* The nested walk: direct stores at ANY depth below the top level are
|
|
7795
|
+
* may-stores. Forwarded calls are skipped everywhere (their contributions
|
|
7796
|
+
* would need cross-function displaced-value knowledge the callee's fresh
|
|
7797
|
+
* record set cannot carry — see the may-scan doc).
|
|
7798
|
+
*/
|
|
7799
|
+
function scan_nested(statements, tracked, result) {
|
|
7800
|
+
for (const stmt of statements) {
|
|
7801
|
+
if (stmt.node_type === "assign") {
|
|
7802
|
+
record_direct_store(stmt, tracked, result);
|
|
7803
|
+
continue;
|
|
7804
|
+
}
|
|
7805
|
+
for (const nested of nested_statement_lists(stmt)) scan_nested(nested, tracked, result);
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
/** Record a direct `param.<field> = …` store into `result`. */
|
|
7809
|
+
function record_direct_store(stmt, tracked, result) {
|
|
7810
|
+
const lhs = stmt.left_value;
|
|
7811
|
+
if (lhs?.node_type !== "access") return;
|
|
7812
|
+
const access = lhs;
|
|
7813
|
+
if (access.access.node_type !== "access_field" || access.target.node_type !== "value") return;
|
|
7814
|
+
const name = access.target.value ?? "";
|
|
7815
|
+
const fields = tracked.get(name);
|
|
7816
|
+
const field = access.access.name ?? "";
|
|
7817
|
+
if (!fields?.has(field)) return;
|
|
7818
|
+
if (!result.has(name)) result.set(name, /* @__PURE__ */ new Set());
|
|
7819
|
+
result.get(name).add(field);
|
|
7820
|
+
}
|
|
7821
|
+
/** Merge a forwarded call's callee stores for the forwarded param: the
|
|
7822
|
+
* callee's MUST stores at a top-level position. */
|
|
7823
|
+
function merge_forwarded(call, tracked, structs, result) {
|
|
7824
|
+
const [callee, args] = call;
|
|
7825
|
+
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7826
|
+
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7827
|
+
const arg = args[i];
|
|
7828
|
+
if (arg?.node_type !== "value") continue;
|
|
7829
|
+
const arg_name = arg.value ?? "";
|
|
7830
|
+
const my_fields = tracked.get(arg_name);
|
|
7831
|
+
if (!my_fields) continue;
|
|
7832
|
+
const callee_param = non_self_params[i];
|
|
7833
|
+
if (!(callee_param.is_ref || callee_param.type.is_ref)) continue;
|
|
7834
|
+
const source = scan_ref_param_string_field_writes(callee, structs).get(callee_param.name);
|
|
7835
|
+
if (!source?.size) continue;
|
|
7836
|
+
if (!result.has(arg_name)) result.set(arg_name, /* @__PURE__ */ new Set());
|
|
7837
|
+
const mine = result.get(arg_name);
|
|
7838
|
+
for (const field of source) if (my_fields.has(field)) mine.add(field);
|
|
7839
|
+
}
|
|
7840
|
+
}
|
|
7841
|
+
/** The statement lists nested inside `stmt` (branch bodies, loop bodies) —
|
|
7842
|
+
* the same walk scan_reassigned_vars uses. */
|
|
7843
|
+
function nested_statement_lists(stmt) {
|
|
7844
|
+
const branch_of = (b) => b?.statements ?? [];
|
|
7845
|
+
const lists = [];
|
|
7846
|
+
if (stmt.node_type === "while" || stmt.node_type === "for") {
|
|
7847
|
+
const nested = stmt.statements;
|
|
7848
|
+
if (nested) lists.push(nested);
|
|
7849
|
+
return lists;
|
|
7850
|
+
}
|
|
7851
|
+
if (stmt.node_type === "if") {
|
|
7852
|
+
const n = stmt;
|
|
7853
|
+
if (n.if_branch) lists.push(branch_of(n.if_branch));
|
|
7854
|
+
if (n.else_branch) lists.push(branch_of(n.else_branch));
|
|
7855
|
+
return lists;
|
|
7856
|
+
}
|
|
7857
|
+
if (stmt.node_type === "switch" || stmt.node_type === "match") {
|
|
7858
|
+
const n = stmt;
|
|
7859
|
+
for (const c of n.cases ?? []) if (c.branch) lists.push(branch_of(c.branch));
|
|
7860
|
+
if (n.else_branch) lists.push(branch_of(n.else_branch));
|
|
7861
|
+
return lists;
|
|
7862
|
+
}
|
|
7863
|
+
return lists;
|
|
7726
7864
|
}
|
|
7727
7865
|
/**
|
|
7728
7866
|
* The forwarded-call shape of a top-level statement: a resolved free-function
|
|
@@ -7753,6 +7891,12 @@ function ref_forward_call(stmt) {
|
|
|
7753
7891
|
* stores (scan_ref_param_string_field_writes) onto the caller's records for
|
|
7754
7892
|
* the argument variable, making the caller's scope exit free them.
|
|
7755
7893
|
*
|
|
7894
|
+
* The callee's CONDITIONAL stores transfer too
|
|
7895
|
+
* (ref_param_entry_dup_fields): the callee's builder entry-dups those
|
|
7896
|
+
* fields (`field = strdup(field)` at entry, recorded), so the field is
|
|
7897
|
+
* heap-owned on EVERY path out of the call — the not-taken path holds the
|
|
7898
|
+
* dup — and a caller-side record is sound.
|
|
7899
|
+
*
|
|
7756
7900
|
* `arg_is_trackable_local` must report true only for variables whose storage
|
|
7757
7901
|
* THIS function's scope exit owns — a plain local. A `ref` param of the
|
|
7758
7902
|
* enclosing function forwards caller storage, so a record here would free the
|
|
@@ -7760,19 +7904,22 @@ function ref_forward_call(stmt) {
|
|
|
7760
7904
|
*/
|
|
7761
7905
|
function transfer_ref_param_field_records(callee, args, status, arg_is_trackable_local) {
|
|
7762
7906
|
const writes = scan_ref_param_string_field_writes(callee, status.structs);
|
|
7763
|
-
|
|
7907
|
+
const dups = ref_param_entry_dup_fields(callee, status.structs);
|
|
7908
|
+
if (!writes.size && !dups.size) return;
|
|
7764
7909
|
const non_self_params = (callee.params ?? []).filter((p) => !p.is_self_param);
|
|
7765
7910
|
for (let i = 0; i < args.length && i < non_self_params.length; i++) {
|
|
7766
7911
|
const param = non_self_params[i];
|
|
7767
7912
|
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
7768
7913
|
const fields = writes.get(param.name);
|
|
7769
|
-
|
|
7914
|
+
const dup_fields = dups.get(param.name);
|
|
7915
|
+
if (!fields?.size && !dup_fields?.size) continue;
|
|
7770
7916
|
const arg = args[i];
|
|
7771
7917
|
if (arg?.node_type !== "value") continue;
|
|
7772
7918
|
const name = arg.value;
|
|
7773
7919
|
if (!name || !arg_is_trackable_local(name)) continue;
|
|
7774
7920
|
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
7775
|
-
for (const field of fields) status.heap_string_fields.add(`${name}.${field}`);
|
|
7921
|
+
for (const field of fields ?? []) status.heap_string_fields.add(`${name}.${field}`);
|
|
7922
|
+
for (const field of dup_fields ?? []) status.heap_string_fields.add(`${name}.${field}`);
|
|
7776
7923
|
}
|
|
7777
7924
|
}
|
|
7778
7925
|
//#endregion
|
|
@@ -8084,7 +8231,7 @@ var AssignmentNode = class extends BaseNode {
|
|
|
8084
8231
|
super("assign", start);
|
|
8085
8232
|
this.left_value = left_value;
|
|
8086
8233
|
this.right_value = right_value;
|
|
8087
|
-
this.operator = operator;
|
|
8234
|
+
this.operator = operator === "=" ? void 0 : operator;
|
|
8088
8235
|
}
|
|
8089
8236
|
};
|
|
8090
8237
|
//#endregion
|
|
@@ -8265,7 +8412,7 @@ function is_container_borrow_accessor_name(name) {
|
|
|
8265
8412
|
* site (see build_return_node), so it counts as owned for classification.
|
|
8266
8413
|
*/
|
|
8267
8414
|
function is_call_site_borrow_accessor(fn_name) {
|
|
8268
|
-
return fn_name === "at" || fn_name === "first";
|
|
8415
|
+
return fn_name === "at" || fn_name === "first" || fn_name === "load";
|
|
8269
8416
|
}
|
|
8270
8417
|
/**
|
|
8271
8418
|
* Whether `node` is a container/buffer borrow accessor CALL (an
|
|
@@ -12011,7 +12158,7 @@ function get_struct_size(name, status) {
|
|
|
12011
12158
|
size = align_to(size, get_type_alignment(field.type, status));
|
|
12012
12159
|
size += get_type_size(field.type, status);
|
|
12013
12160
|
if (is_nullable_struct_type(field.type, status)) size += 8;
|
|
12014
|
-
else if (is_nullable_scalar_type(field.type)) size += 1;
|
|
12161
|
+
else if (is_nullable_scalar_type(field.type, status)) size += 1;
|
|
12015
12162
|
}
|
|
12016
12163
|
size = align_to(size, 8);
|
|
12017
12164
|
return size;
|
|
@@ -12063,7 +12210,7 @@ function get_field_offset_of_fields(fields, field_name, status) {
|
|
|
12063
12210
|
if (field.name === field_name) return offset;
|
|
12064
12211
|
offset += get_type_size(field.type, status);
|
|
12065
12212
|
if (is_nullable_struct_type(field.type, status)) offset += 8;
|
|
12066
|
-
else if (is_nullable_scalar_type(field.type)) offset += 1;
|
|
12213
|
+
else if (is_nullable_scalar_type(field.type, status)) offset += 1;
|
|
12067
12214
|
}
|
|
12068
12215
|
return offset;
|
|
12069
12216
|
}
|
|
@@ -17729,7 +17876,7 @@ function owning_buffer_element_aarch64(node, status) {
|
|
|
17729
17876
|
const elem_name = node.name.substring(7);
|
|
17730
17877
|
const elem = status.structs.find((s) => s.name === elem_name && !s.is_simple_type && !s.is_generic);
|
|
17731
17878
|
if (!elem || elem.is_class) return void 0;
|
|
17732
|
-
if (!has_string_fields(elem, status)) return void 0;
|
|
17879
|
+
if (!has_string_fields(elem, status) && !struct_needs_destroy(elem, status)) return void 0;
|
|
17733
17880
|
return elem;
|
|
17734
17881
|
}
|
|
17735
17882
|
/**
|
|
@@ -17875,6 +18022,75 @@ function collect_string_fields(elem, status, base_offset = 0) {
|
|
|
17875
18022
|
return result;
|
|
17876
18023
|
}
|
|
17877
18024
|
/**
|
|
18025
|
+
* Collect the (offset, size) pairs of every owned CONTAINER field (`List<T>`/
|
|
18026
|
+
* `Buffer<T>`/… mono with a `#destroy`) the element's teardown would reclaim.
|
|
18027
|
+
* The owning store_T/replace_T memcpy the source's bytes into the slot, so
|
|
18028
|
+
* zeroing these fields of the SOURCE completes the ownership transfer: the
|
|
18029
|
+
* slot is the sole owner and the source's own teardown (when it still runs)
|
|
18030
|
+
* finds zeroed fields. Nested value struct fields recurse so their container
|
|
18031
|
+
* leaves are zeroed at their own flat offsets; their string fields are
|
|
18032
|
+
* untouched (the slot owns the strdup'd copies via collect_string_fields).
|
|
18033
|
+
* Mirrors the C backend's `emit_transfer_owning_fields`. `view T`/nullable
|
|
18034
|
+
* fields are skipped; class/enum payload fields are left alone (recorded in
|
|
18035
|
+
* FOLLOWUP.md).
|
|
18036
|
+
*/
|
|
18037
|
+
function collect_transfer_fields(elem, status, base_offset = 0) {
|
|
18038
|
+
const result = [];
|
|
18039
|
+
let offset = 8;
|
|
18040
|
+
for (const field of elem.fields) {
|
|
18041
|
+
const size = get_type_size(field.type, status);
|
|
18042
|
+
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable) {
|
|
18043
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
18044
|
+
if (field_struct && !field_struct.is_class) {
|
|
18045
|
+
if (has_destroy(field_struct)) result.push({
|
|
18046
|
+
offset: base_offset + offset,
|
|
18047
|
+
size
|
|
18048
|
+
});
|
|
18049
|
+
else if (struct_needs_destroy(field_struct, status)) result.push(...collect_transfer_fields(field_struct, status, base_offset + offset));
|
|
18050
|
+
}
|
|
18051
|
+
}
|
|
18052
|
+
offset += size;
|
|
18053
|
+
}
|
|
18054
|
+
return result;
|
|
18055
|
+
}
|
|
18056
|
+
/**
|
|
18057
|
+
* Emit `memset(&src.field, 0, size)` for every owned field collected by
|
|
18058
|
+
* `collect_transfer_fields`. `src` is the register holding the SOURCE struct
|
|
18059
|
+
* address (x2 inline / x21 standalone for store_T). Clobbers x0-x2.
|
|
18060
|
+
*/
|
|
18061
|
+
function emit_transfer_owning_fields$1(status, src, fields) {
|
|
18062
|
+
for (const { offset, size } of fields) {
|
|
18063
|
+
emit_asm(status, `add x0, ${src}, #${offset}\n`);
|
|
18064
|
+
emit_asm(status, `mov x1, #0\n`);
|
|
18065
|
+
emit_asm(status, `mov x2, #${size}\n`);
|
|
18066
|
+
emit_asm(status, `bl _memset\n`);
|
|
18067
|
+
}
|
|
18068
|
+
}
|
|
18069
|
+
/**
|
|
18070
|
+
* Emit the deep-copy half of owning load_T into the sret buffer (x8): for
|
|
18071
|
+
* each `List<T>` field whose mono has a `copy` method, replace the
|
|
18072
|
+
* shallow-copied field with `<List_T>_copy(&slot.field)` (sret → the sret
|
|
18073
|
+
* buffer's field; self == x8 is safe — the copy reads its input before the
|
|
18074
|
+
* final result write). Mirrors the C backend's `emit_load_deep_copy_fields`.
|
|
18075
|
+
* `sret` is the register holding the returned struct's address; `slot` holds
|
|
18076
|
+
* the source slot's address (the copy reads from the slot, writes to sret).
|
|
18077
|
+
*/
|
|
18078
|
+
function emit_load_deep_copy_fields$1(elem, status, sret, slot) {
|
|
18079
|
+
let offset = 8;
|
|
18080
|
+
for (const field of elem.fields) {
|
|
18081
|
+
const size = get_type_size(field.type, status);
|
|
18082
|
+
if (!field.type.is_ref && !field.type.is_view && !field.type.is_array && !field.type.is_nullable && field.type.name !== "string") {
|
|
18083
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
18084
|
+
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
18085
|
+
emit_asm(status, `add x8, ${sret}, #${offset}\n`);
|
|
18086
|
+
emit_asm(status, `add x0, ${slot}, #${offset}\n`);
|
|
18087
|
+
emit_asm(status, `bl ${field_struct.name}_copy\n`);
|
|
18088
|
+
}
|
|
18089
|
+
}
|
|
18090
|
+
offset += size;
|
|
18091
|
+
}
|
|
18092
|
+
}
|
|
18093
|
+
/**
|
|
17878
18094
|
* Emit `slot.field = strdup(src.field)` guarded against a NULL source field
|
|
17879
18095
|
* (e.g. JsonTree's "no text" sentinel): a NULL field is copied as NULL —
|
|
17880
18096
|
* strdup(NULL) would crash. `src`/`dst` are the register names holding the
|
|
@@ -17912,6 +18128,10 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
17912
18128
|
emit_owning_shift_T(elem, status, "x0");
|
|
17913
18129
|
return true;
|
|
17914
18130
|
}
|
|
18131
|
+
if (func_name === "load") {
|
|
18132
|
+
emit_owning_load_T(elem, status, "x0");
|
|
18133
|
+
return true;
|
|
18134
|
+
}
|
|
17915
18135
|
return false;
|
|
17916
18136
|
}
|
|
17917
18137
|
const enum_elem = owning_buffer_enum_element_aarch64(struct_node, status);
|
|
@@ -17973,7 +18193,7 @@ function emit_owning_buffer_inline_aarch64(struct_node, func_name, status) {
|
|
|
17973
18193
|
function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
17974
18194
|
const elem = owning_buffer_element_aarch64(node, status);
|
|
17975
18195
|
if (elem) {
|
|
17976
|
-
if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify") return false;
|
|
18196
|
+
if (func_name !== "store" && func_name !== "replace" && func_name !== "shift" && func_name !== "modify" && func_name !== "load") return false;
|
|
17977
18197
|
emit_owning_standalone_struct(elem, func_name, status);
|
|
17978
18198
|
return true;
|
|
17979
18199
|
}
|
|
@@ -18032,6 +18252,7 @@ function emit_owning_buffer_standalone_aarch64(node, func_name, status) {
|
|
|
18032
18252
|
function emit_owning_standalone_struct(elem, func_name, status) {
|
|
18033
18253
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
18034
18254
|
const string_fields = collect_string_fields(elem, status);
|
|
18255
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
18035
18256
|
if (func_name === "shift") {
|
|
18036
18257
|
emit_owning_shift_T(elem, status, "x19");
|
|
18037
18258
|
return true;
|
|
@@ -18040,6 +18261,23 @@ function emit_owning_standalone_struct(elem, func_name, status) {
|
|
|
18040
18261
|
emit_owning_modify_T(elem, status, T_SIZE, string_fields);
|
|
18041
18262
|
return true;
|
|
18042
18263
|
}
|
|
18264
|
+
if (func_name === "load") {
|
|
18265
|
+
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18266
|
+
emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
|
|
18267
|
+
emit_asm(status, `ldr x20, [x19, #8]\n`);
|
|
18268
|
+
emit_asm(status, `mov x9, #${T_SIZE}\n`);
|
|
18269
|
+
emit_asm(status, `madd x21, x1, x9, x20\n`);
|
|
18270
|
+
emit_asm(status, `mov x22, x8\n`);
|
|
18271
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18272
|
+
emit_asm(status, `mov x1, x21\n`);
|
|
18273
|
+
emit_asm(status, `mov x2, x9\n`);
|
|
18274
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
18275
|
+
emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
|
|
18276
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18277
|
+
emit_asm(status, `ldp x22, x23, [sp], #16\n`);
|
|
18278
|
+
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18279
|
+
return true;
|
|
18280
|
+
}
|
|
18043
18281
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18044
18282
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
18045
18283
|
emit_asm(status, `ldr x20, [x19, #8]\n`);
|
|
@@ -18077,6 +18315,7 @@ function emit_owning_standalone_struct(elem, func_name, status) {
|
|
|
18077
18315
|
}
|
|
18078
18316
|
if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
|
|
18079
18317
|
} else for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
|
|
18318
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
18080
18319
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
18081
18320
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18082
18321
|
return true;
|
|
@@ -18278,9 +18517,35 @@ function emit_owning_shift_T(elem, status, self_reg) {
|
|
|
18278
18517
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
18279
18518
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18280
18519
|
}
|
|
18520
|
+
/**
|
|
18521
|
+
* Specialized owning-value-struct load_T (inline context: x0 = self, x1 = i,
|
|
18522
|
+
* x8 = sret). The caller's scope-exit destroy ALWAYS reclaims a struct
|
|
18523
|
+
* local's container fields, so the returned copy must own independent
|
|
18524
|
+
* containers — a shallow return would double-free the slot's buffer at the
|
|
18525
|
+
* caller's teardown. String fields stay borrowed (struct-local teardown only
|
|
18526
|
+
* frees RECORDED heap strings, so a strdup here would leak).
|
|
18527
|
+
*/
|
|
18528
|
+
function emit_owning_load_T(elem, status, self_reg) {
|
|
18529
|
+
const T_SIZE = get_struct_size(elem.name, status);
|
|
18530
|
+
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18531
|
+
emit_asm(status, `stp x22, x23, [sp, #-16]!\n`);
|
|
18532
|
+
emit_asm(status, `ldr x20, [${self_reg}, #8]\n`);
|
|
18533
|
+
emit_asm(status, `mov x9, #${T_SIZE}\n`);
|
|
18534
|
+
emit_asm(status, `madd x21, x1, x9, x20\n`);
|
|
18535
|
+
emit_asm(status, `mov x22, x8\n`);
|
|
18536
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18537
|
+
emit_asm(status, `mov x1, x21\n`);
|
|
18538
|
+
emit_asm(status, `mov x2, x9\n`);
|
|
18539
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
18540
|
+
emit_load_deep_copy_fields$1(elem, status, "x22", "x21");
|
|
18541
|
+
emit_asm(status, `mov x0, x22\n`);
|
|
18542
|
+
emit_asm(status, `ldp x22, x23, [sp], #16\n`);
|
|
18543
|
+
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18544
|
+
}
|
|
18281
18545
|
function emit_owning_store_T(elem, status) {
|
|
18282
18546
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
18283
18547
|
const string_fields = collect_string_fields(elem, status);
|
|
18548
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
18284
18549
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18285
18550
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
18286
18551
|
emit_asm(status, `ldr x20, [x0, #8]\n`);
|
|
@@ -18312,12 +18577,14 @@ function emit_owning_store_T(elem, status) {
|
|
|
18312
18577
|
emit_asm(status, `${lbl}:\n`);
|
|
18313
18578
|
}
|
|
18314
18579
|
if (string_fields.length) emit_asm(status, `add sp, sp, #${Math.ceil(string_fields.length * 8 / 16) * 16}\n`);
|
|
18580
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
18315
18581
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
18316
18582
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18317
18583
|
}
|
|
18318
18584
|
function emit_owning_replace_T(elem, status) {
|
|
18319
18585
|
const T_SIZE = get_struct_size(elem.name, status);
|
|
18320
18586
|
const string_fields = collect_string_fields(elem, status);
|
|
18587
|
+
const transfer_fields = collect_transfer_fields(elem, status);
|
|
18321
18588
|
emit_asm(status, `stp x20, x21, [sp, #-16]!\n`);
|
|
18322
18589
|
emit_asm(status, `str x22, [sp, #-16]!\n`);
|
|
18323
18590
|
emit_asm(status, `ldr x20, [x0, #8]\n`);
|
|
@@ -18332,6 +18599,7 @@ function emit_owning_replace_T(elem, status) {
|
|
|
18332
18599
|
emit_asm(status, `mov x2, x22\n`);
|
|
18333
18600
|
emit_asm(status, `bl _memcpy\n`);
|
|
18334
18601
|
for (const { offset: foff } of string_fields) emit_strdup_field(status, "x21", "x20", foff, status.label_counter = (status.label_counter ?? 0) + 1);
|
|
18602
|
+
emit_transfer_owning_fields$1(status, "x21", transfer_fields);
|
|
18335
18603
|
emit_asm(status, `ldr x22, [sp], #16\n`);
|
|
18336
18604
|
emit_asm(status, `ldp x20, x21, [sp], #16\n`);
|
|
18337
18605
|
}
|
|
@@ -19996,7 +20264,7 @@ function build_function_node$1(node, status) {
|
|
|
19996
20264
|
status.code = "";
|
|
19997
20265
|
node.return_type = resolve_mono_type(node.return_type, status);
|
|
19998
20266
|
const return_struct = !node.return_type.is_view && !node.return_type.is_array && (!!status.structs.find((s) => s.name === node.return_type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(node.return_type.name, status) !== void 0);
|
|
19999
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.return_type) && !node.return_type.is_ref;
|
|
20267
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.return_type, status) && !node.return_type.is_ref;
|
|
20000
20268
|
const uses_sret = return_struct || nullable_scalar_ret;
|
|
20001
20269
|
if (uses_sret) status.struct_return_buffer = "x8";
|
|
20002
20270
|
status.function_return_type = node.return_type;
|
|
@@ -20083,7 +20351,7 @@ function build_function_node$1(node, status) {
|
|
|
20083
20351
|
first_pass_slot += 2;
|
|
20084
20352
|
continue;
|
|
20085
20353
|
}
|
|
20086
|
-
if (is_nullable_scalar_type(param.type)) {
|
|
20354
|
+
if (is_nullable_scalar_type(param.type, status)) {
|
|
20087
20355
|
first_pass_slot += 2;
|
|
20088
20356
|
continue;
|
|
20089
20357
|
}
|
|
@@ -20250,7 +20518,7 @@ function build_function_node$1(node, status) {
|
|
|
20250
20518
|
param_idx += 2;
|
|
20251
20519
|
continue;
|
|
20252
20520
|
}
|
|
20253
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
|
|
20521
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
|
|
20254
20522
|
const size = aarch64_size(param.type.name);
|
|
20255
20523
|
const offset = allocate_stack_space(status, size, size);
|
|
20256
20524
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -20467,6 +20735,7 @@ function build_function_node$1(node, status) {
|
|
|
20467
20735
|
} else status.nir_emit_ctx = void 0;
|
|
20468
20736
|
const old_raw_param_reloads = status.raw_param_reloads;
|
|
20469
20737
|
install_raw_reload_plan(status, raw_reload_plan_lines);
|
|
20738
|
+
emit_ref_param_entry_dups$1(node, status);
|
|
20470
20739
|
try {
|
|
20471
20740
|
build_block_node$1(node, status);
|
|
20472
20741
|
} finally {
|
|
@@ -20612,6 +20881,49 @@ function build_function_node$1(node, status) {
|
|
|
20612
20881
|
status.return_buffer_stack_offset = void 0;
|
|
20613
20882
|
status.function_return_type = void 0;
|
|
20614
20883
|
}
|
|
20884
|
+
/**
|
|
20885
|
+
* Entry-dup the ref-param string fields the body stores through
|
|
20886
|
+
* CONDITIONALLY (see ref_param_entry_dup_fields): load the field pair's
|
|
20887
|
+
* ptr half, strdup it, store the fresh pointer back — with the field
|
|
20888
|
+
* recorded in heap_string_fields — so the field is heap-owned on every
|
|
20889
|
+
* path out of the function and the call-site record transfer is sound.
|
|
20890
|
+
* Mirrors the C backend's emit_ref_param_entry_dups; the len half is
|
|
20891
|
+
* unchanged by a dup, so only the ptr half is rewritten. A nullable
|
|
20892
|
+
* string field guards the dup (strdup(NULL) traps; a null pair stores as
|
|
20893
|
+
* null and every reclaim path no-ops on free(NULL)).
|
|
20894
|
+
*/
|
|
20895
|
+
function emit_ref_param_entry_dups$1(node, status) {
|
|
20896
|
+
const dups = ref_param_entry_dup_fields(node, status.structs);
|
|
20897
|
+
if (!dups.size) return;
|
|
20898
|
+
for (const param of node.params ?? []) {
|
|
20899
|
+
if (param.is_self_param) continue;
|
|
20900
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
20901
|
+
const fields = dups.get(param.name);
|
|
20902
|
+
if (!fields?.size) continue;
|
|
20903
|
+
const struct = status.structs.find((s) => s.name === param.type.name && !s.is_simple_type && !s.is_class);
|
|
20904
|
+
for (const field of fields) {
|
|
20905
|
+
const offset = get_field_offset(param.type.name, field, status);
|
|
20906
|
+
const paramReg = status.function_param_regs?.get(param.name);
|
|
20907
|
+
if (paramReg) emit_asm(status, `mov x0, ${paramReg}\n`);
|
|
20908
|
+
else if (is_local_ref_var(param.name, status)) emit_deref_var_address(status, "x0", param.name);
|
|
20909
|
+
else emit_var_address(status, "x0", param.name);
|
|
20910
|
+
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
20911
|
+
emit_asm(status, `ldr x0, [x0, #${offset}]\n`);
|
|
20912
|
+
const nullable = !!struct?.fields.find((f) => f.name === field)?.type.is_nullable;
|
|
20913
|
+
let skip_label;
|
|
20914
|
+
if (nullable) {
|
|
20915
|
+
skip_label = `.Lentry_dup_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
20916
|
+
emit_asm(status, `cbz x0, ${skip_label}\n`);
|
|
20917
|
+
}
|
|
20918
|
+
emit_strdup(status);
|
|
20919
|
+
if (skip_label) emit_asm(status, `${skip_label}:\n`);
|
|
20920
|
+
emit_asm(status, `ldr x1, [sp], #16\n`);
|
|
20921
|
+
emit_asm(status, `str x0, [x1, #${offset}]\n`);
|
|
20922
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
20923
|
+
status.heap_string_fields.add(`${param.name}.${field}`);
|
|
20924
|
+
}
|
|
20925
|
+
}
|
|
20926
|
+
}
|
|
20615
20927
|
//#endregion
|
|
20616
20928
|
//#region ../src/build_aarch64/build_struct_node.ts
|
|
20617
20929
|
let field_strdup_guard_counter = 0;
|
|
@@ -20666,7 +20978,7 @@ function init_enum_shorthand_field_default(node, field, base_reg, status) {
|
|
|
20666
20978
|
* struct base register (`x0` for auto-init, `x19` for custom-init).
|
|
20667
20979
|
*/
|
|
20668
20980
|
function init_nullable_field_default(node, field, base_reg, status) {
|
|
20669
|
-
const scalar_nullable = is_nullable_scalar_type(field.type);
|
|
20981
|
+
const scalar_nullable = is_nullable_scalar_type(field.type, status);
|
|
20670
20982
|
if (!is_nullable_struct_type(field.type, status) && !scalar_nullable) return false;
|
|
20671
20983
|
if (!field.value) return false;
|
|
20672
20984
|
const offset = get_field_offset(node.name, field.name, status);
|
|
@@ -21008,7 +21320,7 @@ function build_init_function(node, status) {
|
|
|
21008
21320
|
const slot = ctor_slot;
|
|
21009
21321
|
const field_is_view = !!field.type.is_view && !field.type.is_array;
|
|
21010
21322
|
const field_is_fat_string = field.type.name === "string" && !field.type.is_view && !field.type.is_ref && !field.type.is_array;
|
|
21011
|
-
const field_is_nullable_scalar = is_nullable_scalar_type(field.type);
|
|
21323
|
+
const field_is_nullable_scalar = is_nullable_scalar_type(field.type, status);
|
|
21012
21324
|
if (field_is_view || field_is_fat_string || field_is_nullable_scalar) ctor_slot += 2;
|
|
21013
21325
|
else ctor_slot += 1;
|
|
21014
21326
|
let src_reg;
|
|
@@ -21186,9 +21498,9 @@ function build_init_function(node, status) {
|
|
|
21186
21498
|
if (node.fields.find((nf) => nf.name === field.name)) continue;
|
|
21187
21499
|
const offset = get_field_offset(node.name, field.name, status);
|
|
21188
21500
|
const val = field.value.node_type === "value" ? field.value.value : void 0;
|
|
21189
|
-
if (val === "null" && (is_nullable_scalar_type(field.type) || is_nullable_struct_type(field.type, status))) {
|
|
21501
|
+
if (val === "null" && (is_nullable_scalar_type(field.type, status) || is_nullable_struct_type(field.type, status))) {
|
|
21190
21502
|
const has_off = get_field_has_offset(node.name, field.name, status);
|
|
21191
|
-
if (is_nullable_scalar_type(field.type)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
|
|
21503
|
+
if (is_nullable_scalar_type(field.type, status)) emit_asm(status, `strb wzr, [x19, #${has_off}]\n`);
|
|
21192
21504
|
else emit_asm(status, `str xzr, [x19, #${has_off}]\n`);
|
|
21193
21505
|
continue;
|
|
21194
21506
|
}
|
|
@@ -21329,7 +21641,7 @@ function build_custom_init_function(node, func, status) {
|
|
|
21329
21641
|
param_idx += 2;
|
|
21330
21642
|
continue;
|
|
21331
21643
|
}
|
|
21332
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref && !param.type.is_array) {
|
|
21644
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref && !param.type.is_array) {
|
|
21333
21645
|
const size = aarch64_size(param.type.name);
|
|
21334
21646
|
const offset = allocate_stack_space(status, size, size);
|
|
21335
21647
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -21620,7 +21932,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21620
21932
|
status.function_view_params = /* @__PURE__ */ new Set();
|
|
21621
21933
|
status.struct_return_buffer = void 0;
|
|
21622
21934
|
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) || get_enum_sret_size(func.return_type?.name, status) !== void 0);
|
|
21623
|
-
const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type) && !func.return_type.is_ref;
|
|
21935
|
+
const nullable_scalar_ret = !!func.return_type && is_nullable_scalar_type(func.return_type, status) && !func.return_type.is_ref;
|
|
21624
21936
|
const uses_sret = return_struct || nullable_scalar_ret;
|
|
21625
21937
|
let return_buffer_stack_offset;
|
|
21626
21938
|
status.current_function_name = func.name;
|
|
@@ -21647,7 +21959,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21647
21959
|
slot_idx += 2;
|
|
21648
21960
|
continue;
|
|
21649
21961
|
}
|
|
21650
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
|
|
21962
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
|
|
21651
21963
|
if (param.declaration === "var") status.function_param_vars.add(param.name);
|
|
21652
21964
|
slot_idx += 2;
|
|
21653
21965
|
continue;
|
|
@@ -21730,7 +22042,7 @@ function build_struct_functions$1(node, status) {
|
|
|
21730
22042
|
second_slot_idx += 2;
|
|
21731
22043
|
continue;
|
|
21732
22044
|
}
|
|
21733
|
-
if (is_nullable_scalar_type(param.type) && !param.type.is_ref) {
|
|
22045
|
+
if (is_nullable_scalar_type(param.type, status) && !param.type.is_ref) {
|
|
21734
22046
|
const size = aarch64_size(param.type.name);
|
|
21735
22047
|
const offset = allocate_stack_space(status, size, size);
|
|
21736
22048
|
status.stack_offsets.set(param.name, offset);
|
|
@@ -23189,11 +23501,11 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
23189
23501
|
let nullable_scalar = false;
|
|
23190
23502
|
if (fc.nullable_param_indices?.includes(i)) {
|
|
23191
23503
|
const cp = ctor_callee_params?.[i];
|
|
23192
|
-
if (cp && is_nullable_scalar_type(cp.type)) nullable_scalar = true;
|
|
23193
|
-
else if (is_nullable_scalar_type(p.type)) nullable_scalar = true;
|
|
23504
|
+
if (cp && is_nullable_scalar_type(cp.type, status)) nullable_scalar = true;
|
|
23505
|
+
else if (is_nullable_scalar_type(p.type, status)) nullable_scalar = true;
|
|
23194
23506
|
else if (!cp) {
|
|
23195
23507
|
const field = (status.structs.find((s) => s.name === fc.name && !s.is_simple_type)?.fields.filter((f) => f.value == null))?.[i];
|
|
23196
|
-
if (field && is_nullable_scalar_type(field.type)) nullable_scalar = true;
|
|
23508
|
+
if (field && is_nullable_scalar_type(field.type, status)) nullable_scalar = true;
|
|
23197
23509
|
}
|
|
23198
23510
|
}
|
|
23199
23511
|
const is_pair = pt === "string" || lit || is_view || nullable_scalar;
|
|
@@ -23220,7 +23532,7 @@ function build_constructor_params(fc, param_regs, status) {
|
|
|
23220
23532
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
23221
23533
|
emit_asm(status, `mov x0, #0\n`);
|
|
23222
23534
|
emit_asm(status, `mov x1, #0\n`);
|
|
23223
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
23535
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
23224
23536
|
const vname = arg.value;
|
|
23225
23537
|
emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
|
|
23226
23538
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -23610,7 +23922,7 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23610
23922
|
emit_var_store(status, "x0", node.name, 8);
|
|
23611
23923
|
return;
|
|
23612
23924
|
}
|
|
23613
|
-
if (is_nullable_scalar_type(node.type)) {
|
|
23925
|
+
if (is_nullable_scalar_type(node.type, status)) {
|
|
23614
23926
|
if (status.function_return_label) {
|
|
23615
23927
|
const offset = allocate_stack_space(status, 16, 16);
|
|
23616
23928
|
status.stack_offsets.set(node.name, offset);
|
|
@@ -23621,14 +23933,23 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23621
23933
|
}
|
|
23622
23934
|
const init_value = node.value;
|
|
23623
23935
|
const init_type = call_result_type(init_value) ?? type_from_value_node$1(init_value);
|
|
23624
|
-
const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type);
|
|
23625
|
-
|
|
23936
|
+
const value_is_nullable_call = init_value.node_type === "func_call" && is_nullable_scalar_type(init_type, status);
|
|
23937
|
+
const value_is_nullable_method = init_value.node_type === "access" && init_value.access.node_type === "access_func" && is_nullable_scalar_type(init_type, status);
|
|
23938
|
+
const value_is_nullable_field = init_value.node_type === "access" && init_value.access.node_type === "access_field" && is_nullable_scalar_type(init_type, status);
|
|
23939
|
+
if (value_is_nullable_method) {
|
|
23626
23940
|
emit_init_value$1(init_value, nir_init, status);
|
|
23627
23941
|
ensure_newline(status);
|
|
23628
23942
|
emit_asm(status, `ldr x9, [x0]\n`);
|
|
23629
23943
|
emit_asm(status, `str x9, [x29, #${offset}]\n`);
|
|
23630
23944
|
emit_asm(status, `ldr x9, [x0, #8]\n`);
|
|
23631
23945
|
emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
|
|
23946
|
+
} else if (value_is_nullable_field) {
|
|
23947
|
+
emit_init_value$1(init_value, nir_init, status);
|
|
23948
|
+
ensure_newline(status);
|
|
23949
|
+
emit_var_store(status, "x0", node.name, 8);
|
|
23950
|
+
load_nullable_has(init_value, "x9", status);
|
|
23951
|
+
ensure_newline(status);
|
|
23952
|
+
emit_asm(status, `str x9, [x29, #${offset + 8}]\n`);
|
|
23632
23953
|
} else if (value_is_nullable_call) {
|
|
23633
23954
|
const old_buffer = status.struct_return_buffer;
|
|
23634
23955
|
const old_buffer_var = status.struct_return_buffer_var;
|
|
@@ -23650,8 +23971,13 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23650
23971
|
}
|
|
23651
23972
|
return;
|
|
23652
23973
|
}
|
|
23653
|
-
|
|
23654
|
-
|
|
23974
|
+
const global_is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
|
|
23975
|
+
if (global_is_null) emit_data(status, `${node.name}: .space 8\n`);
|
|
23976
|
+
else {
|
|
23977
|
+
emit_data(status, `${node.name}: ${aarch64_type(node.type.name)} ${get_raw_value$1(node.value, status)}\n`);
|
|
23978
|
+
if (aarch64_size(node.type.name) < 8) emit_data(status, `.p2align 3\n`);
|
|
23979
|
+
}
|
|
23980
|
+
emit_data(status, `${has_flag_name(node.name)}: .quad ${global_is_null ? 0 : 1}\n`);
|
|
23655
23981
|
return;
|
|
23656
23982
|
}
|
|
23657
23983
|
const directive = aarch64_type(node.type.name);
|
|
@@ -23839,6 +24165,14 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23839
24165
|
if (element_size === 1) emit_asm(status, `strb w0, [x9, #${slot}]\n`);
|
|
23840
24166
|
else if (element_size === 4) emit_asm(status, `str w0, [x9, #${slot}]\n`);
|
|
23841
24167
|
else emit_asm(status, `str x0, [x9, #${slot}]\n`);
|
|
24168
|
+
} else if (element_size > 8 && struct_element && !struct_element.is_class) {
|
|
24169
|
+
emit_struct_address_param(value, status);
|
|
24170
|
+
ensure_newline(status);
|
|
24171
|
+
emit_asm(status, `ldr x9, [x29, #${offset}]\n`);
|
|
24172
|
+
emit_asm(status, `mov x1, x0\n`);
|
|
24173
|
+
emit_asm(status, `add x0, x9, #${slot}\n`);
|
|
24174
|
+
emit_asm(status, `mov x2, #${element_size}\n`);
|
|
24175
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
23842
24176
|
} else {
|
|
23843
24177
|
emit_init_value$1(value, nir_array_element(nir_init, array_literal_node, i), status);
|
|
23844
24178
|
ensure_newline(status);
|
|
@@ -23878,6 +24212,14 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23878
24212
|
if (element_size === 1) emit_asm(status, `strb w0, [x9, #${slot}]\n`);
|
|
23879
24213
|
else if (element_size === 4) emit_asm(status, `str w0, [x9, #${slot}]\n`);
|
|
23880
24214
|
else emit_asm(status, `str x0, [x9, #${slot}]\n`);
|
|
24215
|
+
} else if (element_size > 8 && struct_element && !struct_element.is_class) {
|
|
24216
|
+
emit_struct_address_param(value, status);
|
|
24217
|
+
ensure_newline(status);
|
|
24218
|
+
emit_asm(status, `ldr x9, [${node.name}]\n`);
|
|
24219
|
+
emit_asm(status, `mov x1, x0\n`);
|
|
24220
|
+
emit_asm(status, `add x0, x9, #${slot}\n`);
|
|
24221
|
+
emit_asm(status, `mov x2, #${element_size}\n`);
|
|
24222
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
23881
24223
|
} else {
|
|
23882
24224
|
emit_init_value$1(value, nir_array_element(nir_init, array_literal_node, i), status);
|
|
23883
24225
|
ensure_newline(status);
|
|
@@ -23984,6 +24326,13 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
23984
24326
|
if (element_size === 1) emit_asm(status, `strb w0, [x29, #${slot_offset}]\n`);
|
|
23985
24327
|
else if (element_size === 4) emit_asm(status, `str w0, [x29, #${slot_offset}]\n`);
|
|
23986
24328
|
else emit_asm(status, `str x0, [x29, #${slot_offset}]\n`);
|
|
24329
|
+
} else if (element_size > 8 && struct_element && !struct_element.is_class) {
|
|
24330
|
+
emit_struct_address_param(value, status);
|
|
24331
|
+
ensure_newline(status);
|
|
24332
|
+
emit_asm(status, `mov x1, x0\n`);
|
|
24333
|
+
emit_asm(status, `add x0, x29, #${slot_offset}\n`);
|
|
24334
|
+
emit_asm(status, `mov x2, #${element_size}\n`);
|
|
24335
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
23987
24336
|
} else {
|
|
23988
24337
|
emit_init_value$1(value, nir_array_element(nir_init, array_values, i), status);
|
|
23989
24338
|
ensure_newline(status);
|
|
@@ -24009,6 +24358,13 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
|
|
|
24009
24358
|
if (element_size === 1) emit_asm(status, `strb w0, [${node.name} + ${i * element_size}]\n`);
|
|
24010
24359
|
else if (element_size === 4) emit_asm(status, `str w0, [${node.name} + ${i * element_size}]\n`);
|
|
24011
24360
|
else emit_asm(status, `str x0, [${node.name} + ${i * element_size}]\n`);
|
|
24361
|
+
} else if (element_size > 8 && struct_element && !struct_element.is_class) {
|
|
24362
|
+
emit_struct_address_param(value, status);
|
|
24363
|
+
ensure_newline(status);
|
|
24364
|
+
emit_asm(status, `mov x1, x0\n`);
|
|
24365
|
+
emit_global_slot_addr(status, node.name, i * element_size);
|
|
24366
|
+
emit_asm(status, `mov x2, #${element_size}\n`);
|
|
24367
|
+
emit_asm(status, `bl _memcpy\n`);
|
|
24012
24368
|
} else {
|
|
24013
24369
|
emit_init_value$1(value, nir_array_element(nir_init, array_values, i), status);
|
|
24014
24370
|
ensure_newline(status);
|
|
@@ -26115,7 +26471,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
26115
26471
|
hoist_field_overrides(node.value, build_node, status);
|
|
26116
26472
|
if (node.from_inline) return;
|
|
26117
26473
|
const returns_nullable_struct = is_nullable_struct_type(status.function_return_type, status);
|
|
26118
|
-
const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type);
|
|
26474
|
+
const returns_nullable_scalar = is_nullable_scalar_type(status.function_return_type, status);
|
|
26119
26475
|
if ((returns_nullable_struct || returns_nullable_scalar) && (!node.value || node.value.node_type === "value" && node.value.value === "null")) {
|
|
26120
26476
|
if (status.return_buffer_stack_offset !== void 0) {
|
|
26121
26477
|
const struct_size = get_struct_size(status.function_return_type.name, status);
|
|
@@ -26305,7 +26661,7 @@ function build_return_node$1(node, status, nir_value) {
|
|
|
26305
26661
|
if (status.return_buffer_stack_offset !== void 0) emit_asm(status, `ldr x8, [x29, #${status.return_buffer_stack_offset}]\n`);
|
|
26306
26662
|
emit_typed_store(status, "x0", "x8", 0, aarch64_size(status.function_return_type.name));
|
|
26307
26663
|
const value_type = node.value ? type_from_value_node$1(node.value) : void 0;
|
|
26308
|
-
if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type)) {
|
|
26664
|
+
if (node.value && value_type?.is_nullable && is_nullable_scalar_type(value_type, status)) {
|
|
26309
26665
|
load_nullable_has(node.value, "x9", status);
|
|
26310
26666
|
ensure_newline(status);
|
|
26311
26667
|
emit_asm(status, `str x9, [x8, #8]\n`);
|
|
@@ -29935,7 +30291,7 @@ function build_function_call_node$1(node, status) {
|
|
|
29935
30291
|
if (!node.nullable_param_indices?.includes(i)) continue;
|
|
29936
30292
|
const arg_type = node.params[i].type;
|
|
29937
30293
|
const callee_param = node.resolved_function?.params?.[i];
|
|
29938
|
-
if (is_nullable_scalar_type(arg_type) || callee_param && is_nullable_scalar_type(callee_param.type)) nullable_scalar_arg_set.add(i);
|
|
30294
|
+
if (is_nullable_scalar_type(arg_type, status) || callee_param && is_nullable_scalar_type(callee_param.type, status)) nullable_scalar_arg_set.add(i);
|
|
29939
30295
|
}
|
|
29940
30296
|
const arg_slot = [];
|
|
29941
30297
|
let total_slots = 0;
|
|
@@ -30033,7 +30389,7 @@ function build_function_call_node$1(node, status) {
|
|
|
30033
30389
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
30034
30390
|
emit_asm(status, `mov x0, #0\n`);
|
|
30035
30391
|
emit_asm(status, `mov x1, #0\n`);
|
|
30036
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
30392
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
30037
30393
|
const vname = arg.value;
|
|
30038
30394
|
emit_var_load(status, "x0", vname, aarch64_size(param_type.name));
|
|
30039
30395
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -30147,7 +30503,7 @@ function build_function_call_node$1(node, status) {
|
|
|
30147
30503
|
} else if (!is_struct && node.type?.name && !status.call_x8_preset) {
|
|
30148
30504
|
const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
30149
30505
|
const return_enum_size = get_enum_sret_size(node.type?.name, status);
|
|
30150
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.type);
|
|
30506
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
|
|
30151
30507
|
if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
|
|
30152
30508
|
const nullable_ret = is_nullable_struct_type(node.type, status);
|
|
30153
30509
|
const struct_size = return_struct ? get_struct_size(node.type.name, status) : nullable_scalar_ret ? 8 : return_enum_size;
|
|
@@ -30203,7 +30559,7 @@ function build_function_call_node$1(node, status) {
|
|
|
30203
30559
|
if (!is_struct && node.type?.name && !status.call_x8_preset) {
|
|
30204
30560
|
const return_struct = status.structs.find((s) => s.name === node.type.name && !s.is_simple_type && !s.is_class);
|
|
30205
30561
|
const return_enum_size = get_enum_sret_size(node.type?.name, status);
|
|
30206
|
-
const nullable_scalar_ret = is_nullable_scalar_type(node.type);
|
|
30562
|
+
const nullable_scalar_ret = is_nullable_scalar_type(node.type, status);
|
|
30207
30563
|
if (return_struct || return_enum_size !== void 0 || nullable_scalar_ret) {
|
|
30208
30564
|
const temp_name = `_call_ret_${temp_counter - 1}`;
|
|
30209
30565
|
emit_asm(status, `add x0, x29, #${status.stack_offsets.get(temp_name)}\n`);
|
|
@@ -31078,7 +31434,7 @@ function build_struct_body(node, status) {
|
|
|
31078
31434
|
status.code += `];\n`;
|
|
31079
31435
|
} else {
|
|
31080
31436
|
status.code += `${field_c_type(field.type, status)} ${field.name};\n`;
|
|
31081
|
-
if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
|
|
31437
|
+
if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) status.code += `unsigned char ${has_flag_name(field.name)};\n`;
|
|
31082
31438
|
}
|
|
31083
31439
|
for (let traitName of node.traits) {
|
|
31084
31440
|
const trait = status.traits.find((n) => n.name === traitName);
|
|
@@ -31210,8 +31566,8 @@ function build_operation_node$1(node, status) {
|
|
|
31210
31566
|
build_node$1(node.right_value, status);
|
|
31211
31567
|
status.code += `); })`;
|
|
31212
31568
|
ns_tmp_counter++;
|
|
31213
|
-
} else if (is_nullable_scalar_type(left_type)) {
|
|
31214
|
-
if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type)) {
|
|
31569
|
+
} else if (is_nullable_scalar_type(left_type, status)) {
|
|
31570
|
+
if (node.left_value.node_type === "func_call" && is_nullable_scalar_type(node.left_value.type, status)) {
|
|
31215
31571
|
const id = ns_tmp_counter++;
|
|
31216
31572
|
const flag = `_nsh_${id}`;
|
|
31217
31573
|
const val = `_nsv_${id}`;
|
|
@@ -31273,8 +31629,8 @@ function build_operation_node$1(node, status) {
|
|
|
31273
31629
|
else status.code += `((`;
|
|
31274
31630
|
build_node$1(nullable_side, status);
|
|
31275
31631
|
status.code += `.ptr != 0))`;
|
|
31276
|
-
} else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side)) && type?.is_nullable && type.name !== "string") {
|
|
31277
|
-
if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type)) {
|
|
31632
|
+
} else if (is_nullable_scalar_type(type_from_value_node$1(nullable_side), status) && type?.is_nullable && type.name !== "string") {
|
|
31633
|
+
if (nullable_side.node_type === "func_call" && is_nullable_scalar_type(nullable_side.type, status)) {
|
|
31278
31634
|
const id = ns_tmp_counter++;
|
|
31279
31635
|
const flag = `_nsh_${id}`;
|
|
31280
31636
|
const val = `_nsv_${id}`;
|
|
@@ -31438,7 +31794,7 @@ function is_null_comparison(node) {
|
|
|
31438
31794
|
}
|
|
31439
31795
|
function is_nullable_struct_side(node, status) {
|
|
31440
31796
|
const t = type_from_value_node$1(node);
|
|
31441
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
31797
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
31442
31798
|
}
|
|
31443
31799
|
/**
|
|
31444
31800
|
* Build the companion `_has` flag expression for a nullable-struct or
|
|
@@ -31456,8 +31812,8 @@ function is_nullable_struct_side(node, status) {
|
|
|
31456
31812
|
function build_nullable_has(node, status) {
|
|
31457
31813
|
if (node.node_type === "value") {
|
|
31458
31814
|
const name = node.value;
|
|
31459
|
-
if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type))) return has_flag_name(name);
|
|
31460
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) return has_flag_name(name);
|
|
31815
|
+
if (status.function_ref_params?.has(name) && (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status))) return has_flag_name(name);
|
|
31816
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) return has_flag_name(name);
|
|
31461
31817
|
}
|
|
31462
31818
|
const saved = begin_code_scratch(status);
|
|
31463
31819
|
build_node$1(node, status);
|
|
@@ -33122,7 +33478,7 @@ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
|
|
|
33122
33478
|
if (rhs_is_null) status.code += `${flag} = 0`;
|
|
33123
33479
|
else {
|
|
33124
33480
|
const call_ret = call_result_type(node.right_value);
|
|
33125
|
-
if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret))) {
|
|
33481
|
+
if (!!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status))) {
|
|
33126
33482
|
const old = status.current_nullable_call_flag;
|
|
33127
33483
|
status.current_nullable_call_flag = flag;
|
|
33128
33484
|
status.code += `${lhs_expr} = `;
|
|
@@ -33592,11 +33948,11 @@ function lhs_nullable_struct_type(node, status) {
|
|
|
33592
33948
|
if (node.left_value.node_type === "value") {
|
|
33593
33949
|
const name = node.left_value.value;
|
|
33594
33950
|
const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
33595
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
33951
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
33596
33952
|
}
|
|
33597
33953
|
if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
|
|
33598
33954
|
const field_type = node.left_value.access.type;
|
|
33599
|
-
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
|
|
33955
|
+
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
|
|
33600
33956
|
}
|
|
33601
33957
|
return false;
|
|
33602
33958
|
}
|
|
@@ -33609,7 +33965,7 @@ function lhs_nullable_struct_type(node, status) {
|
|
|
33609
33965
|
function nullable_value_flag_expr(value, status) {
|
|
33610
33966
|
const t = type_from_value_node$1(value);
|
|
33611
33967
|
if (!t?.is_nullable) return void 0;
|
|
33612
|
-
if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t)) return void 0;
|
|
33968
|
+
if (!is_nullable_struct_type(t, status) && !is_nullable_scalar_type(t, status)) return void 0;
|
|
33613
33969
|
if (value.node_type === "value") return has_flag_name(value.value);
|
|
33614
33970
|
if (value.node_type === "access" && value.access.node_type === "access_field") {
|
|
33615
33971
|
const saved = begin_code_scratch(status);
|
|
@@ -34340,32 +34696,37 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
|
|
|
34340
34696
|
status.stack_array_lengths.set(safe_name, len_text);
|
|
34341
34697
|
}
|
|
34342
34698
|
}
|
|
34343
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
|
|
34699
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
|
|
34344
34700
|
const flag = has_flag_name(safe_name);
|
|
34345
34701
|
const is_null = !node.value || node.value.node_type === "value" && node.value.value === "null";
|
|
34346
|
-
if (!status.current_function && is_nullable_scalar_type(node.type)) {
|
|
34347
|
-
|
|
34348
|
-
|
|
34349
|
-
|
|
34350
|
-
status.code += `;\nunsigned char ${flag} = 0`;
|
|
34351
|
-
if (node.value && !is_null) {
|
|
34352
|
-
const call_ret = call_result_type(node.value);
|
|
34353
|
-
const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret));
|
|
34354
|
-
status.code += `;\n${safe_name} = `;
|
|
34355
|
-
if (value_is_nullable_call) {
|
|
34356
|
-
const old = status.current_nullable_call_flag;
|
|
34357
|
-
status.current_nullable_call_flag = flag;
|
|
34358
|
-
emit_init_value(node.value, nir_init, status);
|
|
34359
|
-
status.current_nullable_call_flag = old;
|
|
34360
|
-
record_call_init_string_fields(node, status);
|
|
34361
|
-
} else {
|
|
34362
|
-
const rhs_flag = nullable_value_flag_expr(node.value, status);
|
|
34363
|
-
emit_init_value(node.value, nir_init, status);
|
|
34364
|
-
status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
|
|
34365
|
-
}
|
|
34702
|
+
if (!status.current_function && is_nullable_scalar_type(node.type, status)) {
|
|
34703
|
+
if (is_null) {
|
|
34704
|
+
status.code += `;\nunsigned char ${flag} = 0`;
|
|
34705
|
+
return;
|
|
34366
34706
|
}
|
|
34707
|
+
status.code += ` = `;
|
|
34708
|
+
emit_init_value(node.value, nir_init, status);
|
|
34709
|
+
status.code += `;\nunsigned char ${flag} = 1`;
|
|
34367
34710
|
return;
|
|
34368
34711
|
}
|
|
34712
|
+
status.code += `;\nunsigned char ${flag} = 0`;
|
|
34713
|
+
if (node.value && !is_null) {
|
|
34714
|
+
const call_ret = call_result_type(node.value);
|
|
34715
|
+
const value_is_nullable_call = !!call_ret && (is_nullable_struct_type(call_ret, status) || is_nullable_scalar_type(call_ret, status));
|
|
34716
|
+
status.code += `;\n${safe_name} = `;
|
|
34717
|
+
if (value_is_nullable_call) {
|
|
34718
|
+
const old = status.current_nullable_call_flag;
|
|
34719
|
+
status.current_nullable_call_flag = flag;
|
|
34720
|
+
emit_init_value(node.value, nir_init, status);
|
|
34721
|
+
status.current_nullable_call_flag = old;
|
|
34722
|
+
record_call_init_string_fields(node, status);
|
|
34723
|
+
} else {
|
|
34724
|
+
const rhs_flag = nullable_value_flag_expr(node.value, status);
|
|
34725
|
+
emit_init_value(node.value, nir_init, status);
|
|
34726
|
+
status.code += `;\n${flag} = ${rhs_flag ?? "1"}`;
|
|
34727
|
+
}
|
|
34728
|
+
}
|
|
34729
|
+
return;
|
|
34369
34730
|
}
|
|
34370
34731
|
if (node.value) {
|
|
34371
34732
|
const is_heap_literal_init = node.is_heap_array_literal || node.type.storage_kind === "heap_array" && (node.value.node_type === "array" || node.value.node_type === "range");
|
|
@@ -34790,6 +35151,7 @@ function build_if_else_node(node, status, nir) {
|
|
|
34790
35151
|
status.code += `) {\n`;
|
|
34791
35152
|
let then_fields;
|
|
34792
35153
|
if (node.if_branch) {
|
|
35154
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
34793
35155
|
build_block_with_cursor(node.if_branch, nir?.then_branch, status);
|
|
34794
35156
|
build_auto_free(status);
|
|
34795
35157
|
}
|
|
@@ -34798,7 +35160,7 @@ function build_if_else_node(node, status, nir) {
|
|
|
34798
35160
|
if (node.else_branch) {
|
|
34799
35161
|
status.scoped_declarations = enter_c_scope(status);
|
|
34800
35162
|
status.deferred_frees = [];
|
|
34801
|
-
status.heap_string_fields = pre_heap_string_fields;
|
|
35163
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
34802
35164
|
status.code += `} else {\n`;
|
|
34803
35165
|
build_block_with_cursor(node.else_branch, nir?.else_branch, status);
|
|
34804
35166
|
build_auto_free(status);
|
|
@@ -34982,7 +35344,7 @@ function build_return_node(node, status, nir_value) {
|
|
|
34982
35344
|
emit_nursery_joins_on_return_c(status);
|
|
34983
35345
|
reclaim_all_c_scopes(status);
|
|
34984
35346
|
status.code += `*${ret_has} = 0;\n`;
|
|
34985
|
-
if (status.function_return_type && is_nullable_scalar_type(status.function_return_type)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
|
|
35347
|
+
if (status.function_return_type && is_nullable_scalar_type(status.function_return_type, status)) status.code += `return (${c_type(status.function_return_type.name)})0;\n`;
|
|
34986
35348
|
else status.code += `return (struct ${status.function_return_type.name}){0};\n`;
|
|
34987
35349
|
return;
|
|
34988
35350
|
}
|
|
@@ -35215,7 +35577,7 @@ function nullable_flag_expr_of_value(value, status) {
|
|
|
35215
35577
|
if (!value) return void 0;
|
|
35216
35578
|
const t = type_from_value_node$1(value);
|
|
35217
35579
|
if (!t?.is_nullable) return void 0;
|
|
35218
|
-
if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t))) return void 0;
|
|
35580
|
+
if (!(is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status))) return void 0;
|
|
35219
35581
|
if (value.node_type === "value") return has_flag_name(value.value);
|
|
35220
35582
|
if (value.node_type === "access") {
|
|
35221
35583
|
const saved = begin_code_scratch(status);
|
|
@@ -35232,7 +35594,7 @@ function build_switch_node(node, status, nir) {
|
|
|
35232
35594
|
for (let i = 0; i < node.cases.length; i++) {
|
|
35233
35595
|
const c = node.cases[i];
|
|
35234
35596
|
status.scoped_declarations = enter_c_scope(status);
|
|
35235
|
-
status.heap_string_fields = pre_heap_string_fields;
|
|
35597
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
35236
35598
|
const saved_code = begin_code_scratch(status);
|
|
35237
35599
|
build_node$1(c.condition, status);
|
|
35238
35600
|
let cond_code = end_code_scratch(status, saved_code);
|
|
@@ -35278,7 +35640,7 @@ function build_switch_node(node, status, nir) {
|
|
|
35278
35640
|
if (node.else_branch) {
|
|
35279
35641
|
status.code += "} else ";
|
|
35280
35642
|
status.scoped_declarations = enter_c_scope(status);
|
|
35281
|
-
status.heap_string_fields = pre_heap_string_fields;
|
|
35643
|
+
status.heap_string_fields = new Set(pre_heap_string_fields ?? []);
|
|
35282
35644
|
status.code += `{\n`;
|
|
35283
35645
|
build_block_with_cursor(node.else_branch, nir?.otherwise ?? void 0, status);
|
|
35284
35646
|
build_auto_free(status);
|
|
@@ -35657,20 +36019,80 @@ function has_owning_fields(node, status) {
|
|
|
35657
36019
|
}
|
|
35658
36020
|
/**
|
|
35659
36021
|
* The names of Buffer methods that get specialized for owning value structs.
|
|
35660
|
-
* load_T
|
|
35661
|
-
*
|
|
35662
|
-
*
|
|
35663
|
-
*
|
|
36022
|
+
* load_T IS specialized when the element has owning container fields (a
|
|
36023
|
+
* `List<T>` member): the returned copy must own independent containers, or
|
|
36024
|
+
* the caller's scope-exit destroy would free the slot's buffer (double-free
|
|
36025
|
+
* at container teardown). String fields stay BORROWED on load (struct-local
|
|
36026
|
+
* teardown only frees RECORDED heap strings, so strdup'ing them would leak);
|
|
36027
|
+
* container fields have no such borrow convention — a struct local's
|
|
36028
|
+
* container fields are ALWAYS destroyed — so they must be deep-copied.
|
|
36029
|
+
* move_T is NOT specialized: it transfers ownership (the slot is zeroed, the
|
|
36030
|
+
* caller takes the pointers).
|
|
35664
36031
|
*/
|
|
35665
36032
|
const OWNING_BUFFER_METHODS = /* @__PURE__ */ new Set([
|
|
35666
36033
|
"store",
|
|
35667
36034
|
"replace",
|
|
36035
|
+
"load",
|
|
35668
36036
|
"modify",
|
|
35669
36037
|
"shift",
|
|
35670
36038
|
"destroy",
|
|
35671
36039
|
"#destroy"
|
|
35672
36040
|
]);
|
|
35673
36041
|
/**
|
|
36042
|
+
* Emit the transfer half of owning store_T/replace_T: the slot bytes were
|
|
36043
|
+
* already memcpy'd from the source, so zeroing each owned CONTAINER field
|
|
36044
|
+
* of the SOURCE completes the move — the slot is the sole owner, and the
|
|
36045
|
+
* source's own teardown (when it still runs: an unstamped generic-body
|
|
36046
|
+
* local) finds zeroed fields and reclaims nothing. Nested value struct
|
|
36047
|
+
* fields recurse so their container leaves are zeroed at their own paths;
|
|
36048
|
+
* their string fields are untouched (the slot aliases them — the pre-existing
|
|
36049
|
+
* nested-string convention). Class/enum payload fields are left alone
|
|
36050
|
+
* (recorded in FOLLOWUP.md).
|
|
36051
|
+
*/
|
|
36052
|
+
function emit_transfer_owning_fields(elem, src, status, prefix = "") {
|
|
36053
|
+
for (const field of elem.fields) {
|
|
36054
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
36055
|
+
if (field.type.is_array || field.type.is_nullable) continue;
|
|
36056
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
36057
|
+
if (!field_struct || field_struct.is_class) continue;
|
|
36058
|
+
const src_field = `${prefix}${src}${arrow(src)}${field.name}`;
|
|
36059
|
+
if (has_destroy(field_struct)) status.code += `memset((void*)&${src_field}, 0, sizeof(${src_field}));\n`;
|
|
36060
|
+
else if (struct_needs_destroy(field_struct, status)) emit_transfer_owning_fields(field_struct, src_field, status, "");
|
|
36061
|
+
}
|
|
36062
|
+
}
|
|
36063
|
+
/**
|
|
36064
|
+
* Emit the deep-copy half of owning load_T: for each field whose type has a
|
|
36065
|
+
* monomorphized `copy` helper that yields an independent value (a `List<T>`
|
|
36066
|
+
* /`Buffer<T>`/`Map`/`Set` field — `<Mono>_copy` deep-copies the slots or
|
|
36067
|
+
* member buffers — or an enum-with-data field whose string payloads
|
|
36068
|
+
* `<E>_copy` duplicates), overwrite the shallow-copied bytes with an owned
|
|
36069
|
+
* copy. A struct local's CONTAINER fields are reclaimed unconditionally at
|
|
36070
|
+
* scope exit, so a shallow return would double-free the slot's slab. Fields
|
|
36071
|
+
* without a copyable shape (class fields, nested owning user structs without
|
|
36072
|
+
* a synthesized copy) stay shallow — recorded in FOLLOWUP.md.
|
|
36073
|
+
*/
|
|
36074
|
+
function emit_load_deep_copy_fields(elem, dst, status) {
|
|
36075
|
+
for (const field of elem.fields) {
|
|
36076
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
36077
|
+
if (field.type.is_nullable) continue;
|
|
36078
|
+
if (field.type.name === "string") continue;
|
|
36079
|
+
const field_struct = resolve_struct_type(field.type, status);
|
|
36080
|
+
if (field_struct && !field_struct.is_class && is_load_deep_copy_container(field_struct.name) && struct_needs_destroy(field_struct, status) && field_struct.functions.find((f) => f.name === "copy")) {
|
|
36081
|
+
status.code += `${dst}.${field.name} = ${field_struct.name}_copy(&${dst}.${field.name});\n`;
|
|
36082
|
+
continue;
|
|
36083
|
+
}
|
|
36084
|
+
const field_enum = status.enums.find((e) => e.name === field.type.name && e.has_associated_data);
|
|
36085
|
+
if (field_enum && enum_copy_is_deep(field_enum, status)) status.code += `${dst}.${field.name} = ${field_enum.name}_copy(${dst}.${field.name});\n`;
|
|
36086
|
+
}
|
|
36087
|
+
}
|
|
36088
|
+
/** Whether `<E>_copy` yields a fully independent value: no case payload is a
|
|
36089
|
+
* class/trait reference (those are shared pointers the copy cannot
|
|
36090
|
+
* duplicate — mirroring owning_buffer_enum_element's gate). */
|
|
36091
|
+
function enum_copy_is_deep(elem, status) {
|
|
36092
|
+
for (const c of elem.cases) for (const p of c.params) if (status.structs.find((s) => s.name === p.type.name)?.is_class || status.traits.find((t) => t.name === p.type.name)) return false;
|
|
36093
|
+
return true;
|
|
36094
|
+
}
|
|
36095
|
+
/**
|
|
35674
36096
|
* A `Buffer<string>` owns an independent heap copy of each slot (strdup on
|
|
35675
36097
|
* store_T, free+strdup on replace_T, per-slot free on #destroy). The element
|
|
35676
36098
|
* is a primitive, so this is a separate path from the value-struct
|
|
@@ -35833,6 +36255,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35833
36255
|
status.code += `struct ${elem.name} _old = _slots[i];\n`;
|
|
35834
36256
|
status.code += `_slots[i] = (*val);\n`;
|
|
35835
36257
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status, "_old");
|
|
36258
|
+
emit_transfer_owning_fields(elem, "val", status);
|
|
36259
|
+
return true;
|
|
36260
|
+
}
|
|
36261
|
+
if (func_name === "load") {
|
|
36262
|
+
status.code += `struct ${elem.name} _loaded = ((struct ${elem.name} *)(unsigned long long)self->data)[i];\n`;
|
|
36263
|
+
emit_load_deep_copy_fields(elem, "_loaded", status);
|
|
36264
|
+
status.code += `return _loaded;\n`;
|
|
35836
36265
|
return true;
|
|
35837
36266
|
}
|
|
35838
36267
|
if (func_name === "modify") {
|
|
@@ -35847,6 +36276,7 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35847
36276
|
status.code += `${elem.name}_destroy(&_slots[i]);\n`;
|
|
35848
36277
|
status.code += `_slots[i] = (*val);\n`;
|
|
35849
36278
|
emit_deep_copy_fields(elem, "_slots[i]", "val", status);
|
|
36279
|
+
emit_transfer_owning_fields(elem, "val", status);
|
|
35850
36280
|
return true;
|
|
35851
36281
|
}
|
|
35852
36282
|
if (func_name === "shift") {
|
|
@@ -35873,12 +36303,13 @@ function emit_owning_buffer_body(func_name, elem, status) {
|
|
|
35873
36303
|
return false;
|
|
35874
36304
|
}
|
|
35875
36305
|
/**
|
|
35876
|
-
* For each string field on the element struct, overwrite the
|
|
35877
|
-
* pointer in `dst` with a fresh strdup of `src`'s field. This
|
|
35878
|
-
* pointer aliasing between the source and the slot so each can be
|
|
35879
|
-
* independently.
|
|
35880
|
-
*
|
|
35881
|
-
*
|
|
36306
|
+
* For each DIRECT string field on the element struct, overwrite the
|
|
36307
|
+
* shallow-copied pointer in `dst` with a fresh strdup of `src`'s field. This
|
|
36308
|
+
* breaks the pointer aliasing between the source and the slot so each can be
|
|
36309
|
+
* destroyed independently. Nested value struct fields are NOT walked: their
|
|
36310
|
+
* string fields stay aliased (the pre-existing convention — a spliced
|
|
36311
|
+
* `move` donor has no teardown, so the slot is the sole owner), and their
|
|
36312
|
+
* container leaves are transferred by `emit_transfer_owning_fields`.
|
|
35882
36313
|
*
|
|
35883
36314
|
* `old_expr` (optional) is the slot's PRE-copy value expression. When the
|
|
35884
36315
|
* source field pointer equals the old slot field pointer, the slot already
|
|
@@ -35896,12 +36327,6 @@ function emit_deep_copy_fields(elem, dst, src, status, old_expr) {
|
|
|
35896
36327
|
const src_field = `${src}${arrow(src)}${field.name}`;
|
|
35897
36328
|
if (old_expr) status.code += `${dst}.${field.name} = (${src_field}.ptr && ${src_field}.ptr != ${old_expr}.${field.name}.ptr) ? nomen_str_dup(${src_field}) : ${src_field};\n`;
|
|
35898
36329
|
else status.code += `${dst}.${field.name} = ${src_field}.ptr ? nomen_str_dup(${src_field}) : ${src_field};\n`;
|
|
35899
|
-
} else if (field.type.name && !field.type.is_array) {
|
|
35900
|
-
const field_struct = status.structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_generic);
|
|
35901
|
-
if (field_struct && !field_struct.is_class && struct_needs_destroy(field_struct, status)) {
|
|
35902
|
-
status.code += `${field_struct.name}_destroy(&${dst}.${field.name});\n`;
|
|
35903
|
-
emit_deep_copy_fields(field_struct, `${dst}.${field.name}`, `${src}${arrow(src)}${field.name}`, status);
|
|
35904
|
-
}
|
|
35905
36330
|
}
|
|
35906
36331
|
}
|
|
35907
36332
|
}
|
|
@@ -36049,7 +36474,7 @@ function build_struct_node(node, status) {
|
|
|
36049
36474
|
const ctor_return = is_class ? `struct ${node.name}*` : `struct ${node.name}`;
|
|
36050
36475
|
const field_params = node.fields.filter((f) => f.value == null).map((f) => {
|
|
36051
36476
|
let decl = c_param_decl(f.type, f.name, status);
|
|
36052
|
-
if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type)) decl += `, unsigned char ${has_flag_name(f.name)}`;
|
|
36477
|
+
if (is_nullable_struct_type(f.type, status) || is_nullable_scalar_type(f.type, status)) decl += `, unsigned char ${has_flag_name(f.name)}`;
|
|
36053
36478
|
return decl;
|
|
36054
36479
|
}).join(", ");
|
|
36055
36480
|
const ctor_sig = (init) => {
|
|
@@ -36060,7 +36485,7 @@ function build_struct_node(node, status) {
|
|
|
36060
36485
|
is_ref: p.is_ref || p.type.is_ref,
|
|
36061
36486
|
declaration: p.declaration
|
|
36062
36487
|
});
|
|
36063
|
-
if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type))) decl += `, unsigned char ${has_flag_name(p.name)}`;
|
|
36488
|
+
if (!p.is_variadic && (is_nullable_struct_type(p.type, status) || is_nullable_scalar_type(p.type, status))) decl += `, unsigned char ${has_flag_name(p.name)}`;
|
|
36064
36489
|
return decl;
|
|
36065
36490
|
}).join(", ");
|
|
36066
36491
|
const label = is_overloaded(node, "#init") ? mangled_label(init, node.name) : `${node.name}_init`;
|
|
@@ -36088,7 +36513,7 @@ function build_struct_node(node, status) {
|
|
|
36088
36513
|
build_node$1(field.value, status);
|
|
36089
36514
|
status.code += `; self${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
|
|
36090
36515
|
} else status.code += `self${accessor}${field.name} = (nomen_view){0};\n`;
|
|
36091
|
-
} else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) {
|
|
36516
|
+
} else if (is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) {
|
|
36092
36517
|
if (field.value.node_type === "value" && field.value.value === "null") status.code += `self${accessor}${has_flag_name(field.name)} = 0;\n`;
|
|
36093
36518
|
else {
|
|
36094
36519
|
status.code += `self${accessor}${field.name} = `;
|
|
@@ -36166,7 +36591,7 @@ function build_struct_node(node, status) {
|
|
|
36166
36591
|
status.code += `; ${object_name}${accessor}${field.name} = (nomen_view){ _p.ptr, _p.len }; }\n`;
|
|
36167
36592
|
} else status.code += `${object_name}${accessor}${field.name} = (nomen_view){0};\n`;
|
|
36168
36593
|
} else if (field.type.storage_kind === "stack_array" && field.type.length) status.code += `memcpy(${object_name}${accessor}${field.name}, ${field.name}, sizeof(${object_name}${accessor}${field.name}));\n`;
|
|
36169
|
-
else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type)) && field.value) {
|
|
36594
|
+
else if ((is_nullable_struct_type(field.type, status) || is_nullable_scalar_type(field.type, status)) && field.value) {
|
|
36170
36595
|
if (field.value.node_type === "value" && field.value.value === "null") status.code += `${object_name}${accessor}${has_flag_name(field.name)} = 0;\n`;
|
|
36171
36596
|
else {
|
|
36172
36597
|
status.code += `${object_name}${accessor}${field.name} = `;
|
|
@@ -36176,7 +36601,7 @@ function build_struct_node(node, status) {
|
|
|
36176
36601
|
} else if (is_nullable_struct_type(field.type, status)) {
|
|
36177
36602
|
status.code += `${object_name}${accessor}${field.name} = *${field.name};\n`;
|
|
36178
36603
|
status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
|
|
36179
|
-
} else if (is_nullable_scalar_type(field.type)) {
|
|
36604
|
+
} else if (is_nullable_scalar_type(field.type, status)) {
|
|
36180
36605
|
status.code += `${object_name}${accessor}${field.name} = ${field.name};\n`;
|
|
36181
36606
|
status.code += `${object_name}${accessor}${has_flag_name(field.name)} = ${has_flag_name(field.name)};\n`;
|
|
36182
36607
|
} else if (!field.type.is_ref && !field.type.is_view && status.enums.find((e) => e.name === field.type.name && e.has_associated_data)) {
|
|
@@ -36314,7 +36739,7 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
36314
36739
|
status.force_heap_strings = scan_force_heap_strings(func.statements ?? [], status.structs);
|
|
36315
36740
|
status.function_return_type = func.return_type;
|
|
36316
36741
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
36317
|
-
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) status.nullable_ret_has_param = "_ret_has";
|
|
36742
|
+
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) status.nullable_ret_has_param = "_ret_has";
|
|
36318
36743
|
else status.nullable_ret_has_param = void 0;
|
|
36319
36744
|
const self_param = func.params[0]?.is_self_param ? func.params[0] : null;
|
|
36320
36745
|
status.self_is_ref = !!self_param?.is_ref || self_param?.declaration === "var";
|
|
@@ -36371,9 +36796,9 @@ function build_struct_functions(node, status, skip_init = false) {
|
|
|
36371
36796
|
for (let i = 0; i < func.params.length; i++) {
|
|
36372
36797
|
if (i > 0) status.code += ", ";
|
|
36373
36798
|
build_parameter_node(func.params[i], status);
|
|
36374
|
-
if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
|
|
36799
|
+
if (!func.params[i].is_self_param && (is_nullable_struct_type(func.params[i].type, status) || is_nullable_scalar_type(func.params[i].type, status))) status.code += `, unsigned char ${has_flag_name(c_function_name(func.params[i].name))}`;
|
|
36375
36800
|
}
|
|
36376
|
-
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type)) {
|
|
36801
|
+
if (is_nullable_struct_type(func.return_type, status) || is_nullable_scalar_type(func.return_type, status)) {
|
|
36377
36802
|
if (func.params.length > 0) status.code += `, `;
|
|
36378
36803
|
status.code += `unsigned char *_ret_has`;
|
|
36379
36804
|
}
|
|
@@ -37134,9 +37559,9 @@ function build_function_node(node, status) {
|
|
|
37134
37559
|
first_param = false;
|
|
37135
37560
|
if (node.params[i].is_variadic) status.code += `long _${node.params[i].name}_len, `;
|
|
37136
37561
|
build_parameter_node(node.params[i], status);
|
|
37137
|
-
if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_type(node.params[i].type)) && !node.params[i].is_self_param) status.code += `, unsigned char ${has_flag_name(c_function_name(node.params[i].name))}`;
|
|
37562
|
+
if ((is_nullable_struct_type(node.params[i].type, status) || is_nullable_scalar_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))}`;
|
|
37138
37563
|
}
|
|
37139
|
-
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) {
|
|
37564
|
+
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) {
|
|
37140
37565
|
if (!first_param) status.code += ", ";
|
|
37141
37566
|
status.code += `unsigned char *_ret_has`;
|
|
37142
37567
|
}
|
|
@@ -37189,7 +37614,7 @@ function build_function_node(node, status) {
|
|
|
37189
37614
|
const old_variable_types = status.variable_types;
|
|
37190
37615
|
status.variable_types = /* @__PURE__ */ new Map();
|
|
37191
37616
|
const old_nullable_ret_has = status.nullable_ret_has_param;
|
|
37192
|
-
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type)) status.nullable_ret_has_param = "_ret_has";
|
|
37617
|
+
if (is_nullable_struct_type(node.return_type, status) || is_nullable_scalar_type(node.return_type, status)) status.nullable_ret_has_param = "_ret_has";
|
|
37193
37618
|
else status.nullable_ret_has_param = void 0;
|
|
37194
37619
|
const old_function_name = status.current_function_name;
|
|
37195
37620
|
status.current_function_name = emission_label(node);
|
|
@@ -37246,6 +37671,7 @@ function build_function_node(node, status) {
|
|
|
37246
37671
|
stmts: nir.body,
|
|
37247
37672
|
ast: node.statements
|
|
37248
37673
|
} : void 0;
|
|
37674
|
+
emit_ref_param_entry_dups(node, status);
|
|
37249
37675
|
build_block_node(node, status, false);
|
|
37250
37676
|
status.nir_emit_ctx = old_nir_ctx;
|
|
37251
37677
|
status.function_ref_params = old_ref_params;
|
|
@@ -37291,6 +37717,37 @@ function build_function_node(node, status) {
|
|
|
37291
37717
|
status.stack_array_lengths = old_stack_array_lengths;
|
|
37292
37718
|
status.moved = old_moved;
|
|
37293
37719
|
}
|
|
37720
|
+
/**
|
|
37721
|
+
* Emit the entry-dup for every `ref` value-struct param string field the
|
|
37722
|
+
* body stores through CONDITIONALLY (see ref_param_entry_dup_fields):
|
|
37723
|
+
* `{ field = nomen_str_dup(field) }` at function entry, recorded in
|
|
37724
|
+
* heap_string_fields, so the field is heap-owned on every path out of the
|
|
37725
|
+
* function and the call-site record transfer is sound. Nullable string
|
|
37726
|
+
* fields guard the dup (a null pair stores as null — free(NULL) is a
|
|
37727
|
+
* no-op on every reclaim path).
|
|
37728
|
+
*/
|
|
37729
|
+
function emit_ref_param_entry_dups(node, status) {
|
|
37730
|
+
const dups = ref_param_entry_dup_fields(node, status.structs);
|
|
37731
|
+
if (!dups.size) return;
|
|
37732
|
+
for (const param of node.params ?? []) {
|
|
37733
|
+
if (param.is_self_param) continue;
|
|
37734
|
+
if (!(param.is_ref || param.type.is_ref)) continue;
|
|
37735
|
+
const fields = dups.get(param.name);
|
|
37736
|
+
if (!fields?.size) continue;
|
|
37737
|
+
const struct = status.structs.find((s) => s.name === param.type.name && !s.is_simple_type && !s.is_class);
|
|
37738
|
+
const base = c_function_name(param.name);
|
|
37739
|
+
for (const field of fields) {
|
|
37740
|
+
const nullable = !!struct?.fields.find((f) => f.name === field)?.type.is_nullable;
|
|
37741
|
+
if (nullable) status.code += `if (${base}->${field}.ptr) {\n`;
|
|
37742
|
+
const temp = `_dup_t_${status.label_counter = (status.label_counter ?? 0) + 1}`;
|
|
37743
|
+
status.code += `nomen_string ${temp} = nomen_str_dup(${base}->${field});\n`;
|
|
37744
|
+
status.code += `${base}->${field} = ${temp};\n`;
|
|
37745
|
+
if (nullable) status.code += `}\n`;
|
|
37746
|
+
if (!status.heap_string_fields) status.heap_string_fields = /* @__PURE__ */ new Set();
|
|
37747
|
+
status.heap_string_fields.add(`${param.name}.${field}`);
|
|
37748
|
+
}
|
|
37749
|
+
}
|
|
37750
|
+
}
|
|
37294
37751
|
function emit_nested_declarations(node, status) {
|
|
37295
37752
|
const block = node;
|
|
37296
37753
|
for (let child of block.statements) switch (child.node_type) {
|
|
@@ -37576,11 +38033,22 @@ function build_function_call_node(node, status) {
|
|
|
37576
38033
|
const lambda_ret_c = lambda_arg_temps.size > 0 ? c_return_type(node.type, status) : void 0;
|
|
37577
38034
|
const lambda_ret_tmp = lambda_ret_c && lambda_ret_c !== "void" ? next_lambda_ret_temp() : void 0;
|
|
37578
38035
|
if (lambda_ret_tmp) status.code += `${lambda_ret_c} ${lambda_ret_tmp} = `;
|
|
37579
|
-
const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
|
|
38036
|
+
const saved_call = (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) && !status.current_nullable_call_flag ? begin_code_scratch(status) : void 0;
|
|
37580
38037
|
let wrap_tmp;
|
|
37581
38038
|
status.code += `${func_name}(${is_closure_callee ? "NULL" : ""}`;
|
|
37582
38039
|
if (is_closure_callee && node.params.length > 0) status.code += ", ";
|
|
37583
38040
|
const variadic_idx = node.variadic_param_name ? node.params.findIndex((p) => p.node_type === "array") : -1;
|
|
38041
|
+
if (!node.variadic_param_name) {
|
|
38042
|
+
const resolved = node.resolved_function ?? (is_struct ? status.structs.find((s) => s.name === node.name)?.functions.find((f) => f.name === "#init") : void 0);
|
|
38043
|
+
const variadic_param = resolved?.params?.find((p) => p.is_variadic);
|
|
38044
|
+
if (resolved && variadic_param) {
|
|
38045
|
+
if (node.params.length === 1 && node.params[0].node_type === "array") node.variadic_param_name = variadic_param.name;
|
|
38046
|
+
else if (node.params.length === 0 && resolved.params.filter((p) => !p.is_self_param && !p.is_variadic).length === 0) {
|
|
38047
|
+
const elem_c = c_type(variadic_param.type.name);
|
|
38048
|
+
status.code += `0, (${elem_c}[]){ }`;
|
|
38049
|
+
}
|
|
38050
|
+
}
|
|
38051
|
+
}
|
|
37584
38052
|
for (let i = 0; i < node.params.length; i++) {
|
|
37585
38053
|
if (i > 0) status.code += ", ";
|
|
37586
38054
|
if (i === variadic_idx && node.params[i].node_type === "array") {
|
|
@@ -37617,7 +38085,7 @@ function build_function_call_node(node, status) {
|
|
|
37617
38085
|
}
|
|
37618
38086
|
if (node.nullable_param_indices?.includes(i) && node.params[i].node_type === "value" && node.params[i].value === "null") {
|
|
37619
38087
|
const callee_null_param_type = callee_params?.[i]?.type ?? param_type;
|
|
37620
|
-
if (is_nullable_scalar_type(callee_null_param_type)) status.code += `0, 0`;
|
|
38088
|
+
if (is_nullable_scalar_type(callee_null_param_type, status)) status.code += `0, 0`;
|
|
37621
38089
|
else status.code += `(void *)&(struct ${mono_type_name(callee_null_param_type)}){0}, 0`;
|
|
37622
38090
|
continue;
|
|
37623
38091
|
}
|
|
@@ -37703,7 +38171,7 @@ function build_function_call_node(node, status) {
|
|
|
37703
38171
|
emit_nullable_arg_flag(node.params[i], status);
|
|
37704
38172
|
}
|
|
37705
38173
|
}
|
|
37706
|
-
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type)) {
|
|
38174
|
+
if (is_nullable_struct_type(node.type, status) || is_nullable_scalar_type(node.type, status)) {
|
|
37707
38175
|
const flag_name = status.current_nullable_call_flag;
|
|
37708
38176
|
const arg_list_empty = node.params.length === 0 && !is_closure_callee;
|
|
37709
38177
|
if (flag_name) status.code += `${arg_list_empty ? "" : ", "}&${flag_name}`;
|
|
@@ -37812,13 +38280,13 @@ function emit_nullable_arg_flag(arg, status) {
|
|
|
37812
38280
|
}
|
|
37813
38281
|
if (arg.node_type === "value") {
|
|
37814
38282
|
const vn = arg;
|
|
37815
|
-
if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type)) status.code += `${has_flag_name(vn.value)}`;
|
|
38283
|
+
if (is_nullable_struct_type(vn.type, status) || is_nullable_scalar_type(vn.type, status)) status.code += `${has_flag_name(vn.value)}`;
|
|
37816
38284
|
else status.code += `1`;
|
|
37817
38285
|
return;
|
|
37818
38286
|
}
|
|
37819
38287
|
if (arg.node_type === "access" && arg.access.node_type === "access_field") {
|
|
37820
38288
|
const t = type_from_value_node$1(arg);
|
|
37821
|
-
if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t)) {
|
|
38289
|
+
if (is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status)) {
|
|
37822
38290
|
const saved = begin_code_scratch(status);
|
|
37823
38291
|
build_node$1(arg, status);
|
|
37824
38292
|
const expr = end_code_scratch(status, saved);
|
|
@@ -38620,13 +39088,13 @@ function build_access_node$1(node, status) {
|
|
|
38620
39088
|
build_node$1(access_func.params[i], status);
|
|
38621
39089
|
status.suppress_dereference = false;
|
|
38622
39090
|
}
|
|
38623
|
-
if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type))) {
|
|
39091
|
+
if (target_param && !target_param.is_self_param && (is_nullable_struct_type(target_param.type, status) || is_nullable_scalar_type(target_param.type, status))) {
|
|
38624
39092
|
status.code += `, `;
|
|
38625
39093
|
emit_nullable_arg_flag(access_func.params[i], status);
|
|
38626
39094
|
}
|
|
38627
39095
|
}
|
|
38628
39096
|
const method_ret_type = (target_method ?? trait_default_func)?.return_type ?? access_func.type;
|
|
38629
|
-
if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type)) {
|
|
39097
|
+
if (is_nullable_struct_type(method_ret_type, status) || is_nullable_scalar_type(method_ret_type, status)) {
|
|
38630
39098
|
const flag_name = status.current_nullable_call_flag;
|
|
38631
39099
|
status.code += `, `;
|
|
38632
39100
|
status.code += flag_name ? `&${flag_name}` : `(unsigned char *)&(unsigned char){0}`;
|
|
@@ -40371,11 +40839,11 @@ function is_nullable_struct_assignment(node, status) {
|
|
|
40371
40839
|
if (node.left_value.node_type === "value") {
|
|
40372
40840
|
const name = node.left_value.value;
|
|
40373
40841
|
const t = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
40374
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
40842
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
40375
40843
|
}
|
|
40376
40844
|
if (node.left_value.node_type === "access" && node.left_value.access.node_type === "access_field") {
|
|
40377
40845
|
const field_type = node.left_value.access.type;
|
|
40378
|
-
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type);
|
|
40846
|
+
return is_nullable_struct_type(field_type, status) || is_nullable_scalar_type(field_type, status);
|
|
40379
40847
|
}
|
|
40380
40848
|
return false;
|
|
40381
40849
|
}
|
|
@@ -40430,7 +40898,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40430
40898
|
const flag_name = has_flag_name(name);
|
|
40431
40899
|
const decl_type = status.scoped_declarations.find((d) => d.name === name)?.type || status.variable_types?.get(name);
|
|
40432
40900
|
const type_name = decl_type?.name || "";
|
|
40433
|
-
const is_scalar = is_nullable_scalar_type(decl_type);
|
|
40901
|
+
const is_scalar = is_nullable_scalar_type(decl_type, status);
|
|
40434
40902
|
const scalar_size = is_scalar ? aarch64_size(type_name) : 0;
|
|
40435
40903
|
const reclaimable = !(node.right_value.node_type === "value" && node.right_value.value === name) && !!status.heap_string_fields?.size && Array.from(status.heap_string_fields).some((k) => k.startsWith(`${name}.`));
|
|
40436
40904
|
if (rhs_is_null) {
|
|
@@ -40449,9 +40917,11 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40449
40917
|
}
|
|
40450
40918
|
}
|
|
40451
40919
|
};
|
|
40452
|
-
const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type));
|
|
40920
|
+
const value_is_nullable_call = node.right_value.node_type === "func_call" && (is_nullable_struct_type(node.right_value.type, status) || is_nullable_scalar_type(node.right_value.type, status));
|
|
40453
40921
|
const rhs_value_type = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
|
|
40454
|
-
|
|
40922
|
+
const value_is_nullable_access = node.right_value.node_type === "access" && rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
|
|
40923
|
+
const rhs_is_field_read = value_is_nullable_access && is_nullable_scalar_type(rhs_value_type, status) && node.right_value.access.node_type === "access_field";
|
|
40924
|
+
if (value_is_nullable_access && !rhs_is_field_read) {
|
|
40455
40925
|
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
40456
40926
|
retarget_records();
|
|
40457
40927
|
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
@@ -40468,6 +40938,17 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40468
40938
|
emit_var_store(status, "x9", flag_name, 8);
|
|
40469
40939
|
return;
|
|
40470
40940
|
}
|
|
40941
|
+
if (rhs_is_field_read) {
|
|
40942
|
+
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
40943
|
+
retarget_records();
|
|
40944
|
+
emit_rhs_value(node.right_value, nir_rhs, status);
|
|
40945
|
+
ensure_newline(status);
|
|
40946
|
+
emit_var_store(status, "x0", name, scalar_size);
|
|
40947
|
+
load_nullable_has(node.right_value, "x9", status);
|
|
40948
|
+
ensure_newline(status);
|
|
40949
|
+
emit_var_store(status, "x9", flag_name, 8);
|
|
40950
|
+
return;
|
|
40951
|
+
}
|
|
40471
40952
|
if (value_is_nullable_call) {
|
|
40472
40953
|
if (reclaimable) emit_recorded_field_reclaim_for_slot(status, name, type_name);
|
|
40473
40954
|
retarget_records();
|
|
@@ -40484,7 +40965,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40484
40965
|
status.call_x8_preset = old_preset;
|
|
40485
40966
|
return;
|
|
40486
40967
|
}
|
|
40487
|
-
const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type));
|
|
40968
|
+
const rhs_is_nullable_value = !!rhs_value_type?.is_nullable && (is_nullable_struct_type(rhs_value_type, status) || is_nullable_scalar_type(rhs_value_type, status));
|
|
40488
40969
|
let rhs_flag_slot;
|
|
40489
40970
|
if (rhs_is_nullable_value) {
|
|
40490
40971
|
load_nullable_has(node.right_value, "x9", status);
|
|
@@ -40521,7 +41002,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40521
41002
|
const field_offset = get_field_offset(target_type.name, field_name, status);
|
|
40522
41003
|
const has_offset = get_field_has_offset(target_type.name, field_name, status);
|
|
40523
41004
|
const struct_size = get_struct_size(type_name, status);
|
|
40524
|
-
const field_is_scalar = is_nullable_scalar_type(field_access.type);
|
|
41005
|
+
const field_is_scalar = is_nullable_scalar_type(field_access.type, status);
|
|
40525
41006
|
get_source_address(access.target, status);
|
|
40526
41007
|
ensure_newline(status);
|
|
40527
41008
|
emit_asm(status, `str x0, [sp, #-16]!\n`);
|
|
@@ -40532,7 +41013,7 @@ function build_nullable_struct_assignment(node, status, nir_rhs) {
|
|
|
40532
41013
|
return;
|
|
40533
41014
|
}
|
|
40534
41015
|
const rhs_t = call_result_type(node.right_value) ?? type_from_value_node$1(node.right_value);
|
|
40535
|
-
const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t));
|
|
41016
|
+
const rhs_is_nullable_value = !!rhs_t?.is_nullable && (is_nullable_struct_type(rhs_t, status) || is_nullable_scalar_type(rhs_t, status));
|
|
40536
41017
|
let rhs_flag_slot;
|
|
40537
41018
|
if (rhs_is_nullable_value) {
|
|
40538
41019
|
load_nullable_has(node.right_value, "x10", status);
|
|
@@ -41175,7 +41656,11 @@ function build_assignment_node(node, status, nir_rhs, nir_swap) {
|
|
|
41175
41656
|
if (name === "self" && status.current_struct) target_type = new Type(status.current_struct.name);
|
|
41176
41657
|
else if (status.variable_types?.has(name)) target_type = status.variable_types.get(name);
|
|
41177
41658
|
}
|
|
41178
|
-
|
|
41659
|
+
let field_type = access.access.type;
|
|
41660
|
+
if (!field_type?.name && target_type?.name) {
|
|
41661
|
+
const tf = status.structs.find((s) => s.name === target_type.name)?.fields.find((f) => f.name === field_name);
|
|
41662
|
+
if (tf) field_type = tf.type;
|
|
41663
|
+
}
|
|
41179
41664
|
const field_is_struct = is_struct_type$2(field_type, status);
|
|
41180
41665
|
if (access.target.node_type === "value") {
|
|
41181
41666
|
const name = access.target.value;
|
|
@@ -42357,7 +42842,7 @@ function build_operation_node(node, status) {
|
|
|
42357
42842
|
if (node.op === "??") {
|
|
42358
42843
|
const left_t = type_from_value_node$1(node.left_value);
|
|
42359
42844
|
const left_is_nullable_struct = is_nullable_struct_type(left_t, status);
|
|
42360
|
-
const left_is_nullable_scalar = is_nullable_scalar_type(left_t);
|
|
42845
|
+
const left_is_nullable_scalar = is_nullable_scalar_type(left_t, status);
|
|
42361
42846
|
if (left_is_nullable_scalar && node.left_value.node_type === "func_call") {
|
|
42362
42847
|
build_operand(node.left_value, "x0", status);
|
|
42363
42848
|
ensure_newline(status);
|
|
@@ -42409,11 +42894,11 @@ function build_operation_node(node, status) {
|
|
|
42409
42894
|
if ((node.op === "==" || node.op === "!=") && is_null_literal$1(node.left_value) !== is_null_literal$1(node.right_value)) {
|
|
42410
42895
|
const nullable_flagged_side = (n) => {
|
|
42411
42896
|
const t = type_from_value_node$1(n);
|
|
42412
|
-
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t);
|
|
42897
|
+
return is_nullable_struct_type(t, status) || is_nullable_scalar_type(t, status);
|
|
42413
42898
|
};
|
|
42414
42899
|
const nullable_side = nullable_flagged_side(node.left_value) ? node.left_value : node.right_value;
|
|
42415
42900
|
const side_type = type_from_value_node$1(nullable_side);
|
|
42416
|
-
if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type)) {
|
|
42901
|
+
if (is_nullable_struct_type(side_type, status) || is_nullable_scalar_type(side_type, status)) {
|
|
42417
42902
|
load_nullable_has(nullable_side, "x1", status);
|
|
42418
42903
|
ensure_newline(status);
|
|
42419
42904
|
emit_asm(status, `cmp x1, #0\n`);
|
|
@@ -42931,7 +43416,7 @@ function load_nullable_has(node, target_reg, status) {
|
|
|
42931
43416
|
get_source_address(access.target, status);
|
|
42932
43417
|
ensure_newline(status);
|
|
42933
43418
|
if (target_reg !== "x0") emit_asm(status, `mov ${target_reg}, x0\n`);
|
|
42934
|
-
const scalar_field = is_nullable_scalar_type(access.access.type);
|
|
43419
|
+
const scalar_field = is_nullable_scalar_type(access.access.type, status);
|
|
42935
43420
|
const wreg = target_reg.replace("x", "w");
|
|
42936
43421
|
const load_op = scalar_field ? `ldrb ${wreg}` : `ldr ${target_reg}`;
|
|
42937
43422
|
if (has_off === 0) emit_asm(status, `${load_op}, [${target_reg}]\n`);
|
|
@@ -45133,7 +45618,7 @@ function build_access_method(node, access_func, status) {
|
|
|
45133
45618
|
const access_callee_params = access_callee_method?.params?.filter((p) => !p.is_self_param);
|
|
45134
45619
|
const return_struct = !access_func.type.is_view && !access_func.type.is_array && (!!status.structs.find((s) => s.name === access_func.type.name && !s.is_simple_type && !s.is_class) || get_enum_sret_size(access_func.type.name, status) !== void 0);
|
|
45135
45620
|
const method_ret_type = access_callee_method?.return_type ?? access_func.type;
|
|
45136
|
-
const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type) && !method_ret_type.is_ref;
|
|
45621
|
+
const nullable_scalar_ret = is_nullable_scalar_type(method_ret_type, status) && !method_ret_type.is_ref;
|
|
45137
45622
|
let temp_addr = "";
|
|
45138
45623
|
let temp_offset = 0;
|
|
45139
45624
|
if (return_struct || nullable_scalar_ret) {
|
|
@@ -45253,7 +45738,7 @@ function build_access_method(node, access_func, status) {
|
|
|
45253
45738
|
for (let i = 0; i < access_func.params.length; i++) {
|
|
45254
45739
|
if ((access_func.ref_param_indices ?? []).includes(i)) continue;
|
|
45255
45740
|
const cp = access_callee_params?.[i];
|
|
45256
|
-
if (cp && is_nullable_scalar_type(cp.type) || is_nullable_scalar_type(access_func.params[i].type)) nullable_scalar_arg_set.add(i);
|
|
45741
|
+
if (cp && is_nullable_scalar_type(cp.type, status) || is_nullable_scalar_type(access_func.params[i].type, status)) nullable_scalar_arg_set.add(i);
|
|
45257
45742
|
}
|
|
45258
45743
|
const arg_slot = [];
|
|
45259
45744
|
let total_arg_slots = 0;
|
|
@@ -45308,7 +45793,7 @@ function build_access_method(node, access_func, status) {
|
|
|
45308
45793
|
if (arg.node_type === "value" && arg.value === "null") {
|
|
45309
45794
|
emit_asm(status, `mov x0, #0\n`);
|
|
45310
45795
|
emit_asm(status, `mov x1, #0\n`);
|
|
45311
|
-
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type)) {
|
|
45796
|
+
} else if (arg.node_type === "value" && is_nullable_scalar_type(arg.type, status)) {
|
|
45312
45797
|
const vname = arg.value;
|
|
45313
45798
|
emit_var_load(status, "x0", vname, aarch64_size(arg.type.name));
|
|
45314
45799
|
emit_var_load(status, "x1", has_flag_name(vname), 8);
|
|
@@ -49572,13 +50057,33 @@ function can_class_int_coerce(target, value, status) {
|
|
|
49572
50057
|
}
|
|
49573
50058
|
//#endregion
|
|
49574
50059
|
//#region ../src/check/utils/check_type_exists.ts
|
|
50060
|
+
/**
|
|
50061
|
+
* Containers whose elements live in raw T-sized slots (the `#arch` generic
|
|
50062
|
+
* bodies size each slot from the type name alone). A flagged-nullable
|
|
50063
|
+
* element type (`List<int?>`) has no per-slot null flag there, so `0`/`false`
|
|
50064
|
+
* would read back as null — rejected instead of silently misbehaving.
|
|
50065
|
+
* Element types with an in-band null (`string?`, class/trait `?`) stay legal.
|
|
50066
|
+
*/
|
|
50067
|
+
const RAW_ELEMENT_CONTAINERS = /* @__PURE__ */ new Set([
|
|
50068
|
+
"Array",
|
|
50069
|
+
"Buffer",
|
|
50070
|
+
"List",
|
|
50071
|
+
"Set",
|
|
50072
|
+
"Map",
|
|
50073
|
+
"Graph",
|
|
50074
|
+
"LinkedList",
|
|
50075
|
+
"Tree",
|
|
50076
|
+
"Arena"
|
|
50077
|
+
]);
|
|
49575
50078
|
function check_type_exists(type, status, start) {
|
|
50079
|
+
if (type.is_nullable && type.is_array && !type.is_ref && !type.is_pointer && is_nullable_scalar_name(type.name, status)) add_error(status, `Nullable element type '${type.name}?' is not supported in an array: element storage has no per-element null flag`, start);
|
|
49576
50080
|
if (type.name === "tuple" && type.tuple_types?.length) {
|
|
49577
50081
|
let ok = true;
|
|
49578
50082
|
for (const elem of type.tuple_types) if (!check_type_exists(elem, status, start)) ok = false;
|
|
49579
50083
|
return ok;
|
|
49580
50084
|
}
|
|
49581
50085
|
if (type.name === "anon_enum" && type.enum_cases?.length) {
|
|
50086
|
+
if (type.is_nullable && type.enum_cases.some((c) => c.types.length > 0)) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
|
|
49582
50087
|
let ok = true;
|
|
49583
50088
|
for (const c of type.enum_cases) for (const t of c.types) if (!check_type_exists(t, status, start)) ok = false;
|
|
49584
50089
|
return ok;
|
|
@@ -49595,19 +50100,26 @@ function check_type_exists(type, status, start) {
|
|
|
49595
50100
|
}
|
|
49596
50101
|
}
|
|
49597
50102
|
type.name = declared.name;
|
|
50103
|
+
if (type.is_nullable) {
|
|
50104
|
+
if (status.enums.findLast((e) => (e.name === declared.name || e.source_name === declared.name) && !e.is_generic)?.has_associated_data) add_error(status, `Nullable type '${type_name(type)}' is not supported: enums with associated data have no separate null flag`, start);
|
|
50105
|
+
}
|
|
49598
50106
|
} else if (!status.types.includes(type.name)) {
|
|
49599
50107
|
add_error(status, `Unknown type: ${type_name(type)}`, start);
|
|
49600
50108
|
return false;
|
|
49601
50109
|
}
|
|
49602
50110
|
if (type.tuple_types) for (const elem of type.tuple_types) check_type_exists(elem, status, start);
|
|
49603
|
-
if (type.type_args)
|
|
49604
|
-
if (
|
|
49605
|
-
add_error(status, `
|
|
49606
|
-
continue;
|
|
50111
|
+
if (type.type_args) {
|
|
50112
|
+
if (RAW_ELEMENT_CONTAINERS.has(type.name)) {
|
|
50113
|
+
for (const arg of type.type_args) if (is_nullable_flagged_type(arg, status)) add_error(status, `Nullable element type '${type_name(arg)}' is not supported in '${type.name}<...>': container element storage has no per-element null flag`, start);
|
|
49607
50114
|
}
|
|
49608
|
-
|
|
49609
|
-
|
|
49610
|
-
|
|
50115
|
+
for (const arg of type.type_args) {
|
|
50116
|
+
if (arg.name === "func") {
|
|
50117
|
+
add_error(status, `func types cannot be used as type arguments ('${type.name}<...>')`, start);
|
|
50118
|
+
continue;
|
|
50119
|
+
}
|
|
50120
|
+
check_type_exists(arg, status, start);
|
|
50121
|
+
}
|
|
50122
|
+
} else {
|
|
49611
50123
|
const struct = status.structs.findLast((s) => s.name === type.name);
|
|
49612
50124
|
if (struct?.is_generic) {
|
|
49613
50125
|
if (!struct.type_params.every((tp) => status.type_params.includes(tp))) add_error(status, `Generic type '${type.name}' requires type arguments (expected <${struct.type_params.join(", ")}>)`, start);
|
|
@@ -52486,20 +52998,57 @@ function build_hash(struct, fields) {
|
|
|
52486
52998
|
}
|
|
52487
52999
|
/**
|
|
52488
53000
|
* Whether `struct` is eligible for a synthesized `copy` method: a value
|
|
52489
|
-
* struct whose
|
|
52490
|
-
*
|
|
52491
|
-
*
|
|
52492
|
-
*
|
|
52493
|
-
*
|
|
52494
|
-
*
|
|
52495
|
-
*
|
|
53001
|
+
* struct whose every field is independently deep-copyable — direct string
|
|
53002
|
+
* fields (strdup'd), scalars/arrays (byte copies), `List<T>` fields whose
|
|
53003
|
+
* element is itself deep-copyable (the library `List.copy` deep-copies slots
|
|
53004
|
+
* via the owning store contract), and nested copyable structs (recursing
|
|
53005
|
+
* through their own `copy`). Class fields, enum-with-data fields (their
|
|
53006
|
+
* stores transfer payload ownership), class/trait-carrying `List` elements,
|
|
53007
|
+
* and resource-releasing `#destroy`s cannot be deep-copied by synthesis —
|
|
53008
|
+
* the copy stays unwritten (move/swap remains the escape hatch) — and a
|
|
53009
|
+
* struct with nothing to deep-copy (no string, `List`, or nested-copyable
|
|
53010
|
+
* field) is already soundly byte-copyable, so it needs no method.
|
|
52496
53011
|
*/
|
|
52497
53012
|
function struct_is_copyable(struct, status) {
|
|
52498
53013
|
if (struct.is_class || struct.is_generic || struct.is_simple_type) return false;
|
|
52499
|
-
if (
|
|
52500
|
-
if (
|
|
53014
|
+
if (struct_owns_resource(struct, status, /* @__PURE__ */ new Set())) return false;
|
|
53015
|
+
if (!struct_needs_copy(struct, status)) return false;
|
|
52501
53016
|
return struct_copy_ctor_args(struct) !== null;
|
|
52502
53017
|
}
|
|
53018
|
+
/** Whether any field of `struct` actually needs the synthesized deep copy:
|
|
53019
|
+
* a direct string field, a deep-copyable `List<T>` field, or a nested
|
|
53020
|
+
* copyable struct field. A struct of flat values byte-copies soundly
|
|
53021
|
+
* without a method. */
|
|
53022
|
+
function struct_needs_copy(struct, status) {
|
|
53023
|
+
if (direct_string_fields(struct).length > 0) return true;
|
|
53024
|
+
for (const field of struct.fields) {
|
|
53025
|
+
if (field.type.is_ref || field.type.is_view || field.type.is_array) continue;
|
|
53026
|
+
if (field_copy_call(field.type, status)) return true;
|
|
53027
|
+
}
|
|
53028
|
+
return false;
|
|
53029
|
+
}
|
|
53030
|
+
/**
|
|
53031
|
+
* Whether `struct` owns a resource a synthesized copy CANNOT duplicate: a
|
|
53032
|
+
* `#destroy` that releases something (a raw block / extern free), or a field
|
|
53033
|
+
* whose type has no deep-copy story (a class instance, a `List` of
|
|
53034
|
+
* class/trait elements, a nested struct with such a field). Fields that DO
|
|
53035
|
+
* have a deep-copy story (strings, nested copyables, copyable `List`s,
|
|
53036
|
+
* payload-copyable enums) don't block synthesis. Mirrors the old
|
|
53037
|
+
* `struct_owns_non_string_heap` gate but understands container deep copies.
|
|
53038
|
+
*/
|
|
53039
|
+
function struct_owns_resource(struct, status, visited) {
|
|
53040
|
+
if (visited.has(struct.name)) return false;
|
|
53041
|
+
visited.add(struct.name);
|
|
53042
|
+
const destroy = struct.functions.find((f) => f.name === "#destroy");
|
|
53043
|
+
if (destroy && destroy_releases(destroy)) return true;
|
|
53044
|
+
for (const field of struct.fields) {
|
|
53045
|
+
if (field.type.is_ref || field.type.is_view) continue;
|
|
53046
|
+
if (field.type.name === "string" && !field.type.is_array) continue;
|
|
53047
|
+
if (field.type.is_nullable) return true;
|
|
53048
|
+
if (!field_copyable(field.type, status)) return true;
|
|
53049
|
+
}
|
|
53050
|
+
return false;
|
|
53051
|
+
}
|
|
52503
53052
|
/**
|
|
52504
53053
|
* How a synthesized `copy` constructs the fresh value: the zero-argument
|
|
52505
53054
|
* `#init` overload when one exists (auto or user), else the bodyless AUTO
|
|
@@ -52515,14 +53064,49 @@ function struct_copy_ctor_args(struct) {
|
|
|
52515
53064
|
if (!auto) return null;
|
|
52516
53065
|
return auto.params.filter((p) => !p.is_self_param).map((p) => p.name);
|
|
52517
53066
|
}
|
|
52518
|
-
/** Whether `type`
|
|
52519
|
-
*
|
|
52520
|
-
*
|
|
52521
|
-
*
|
|
52522
|
-
|
|
53067
|
+
/** Whether `type` has (or will get) a deep-copy story: a struct with a
|
|
53068
|
+
* synthesized `copy`, a `List<T>` whose element is deep-copyable (the
|
|
53069
|
+
* library `List.copy` deep-copies slots via the owning store contract), or
|
|
53070
|
+
* a primitive / plain (payload-free) enum. Order-independent: it
|
|
53071
|
+
* re-evaluates the eligibility predicate instead of looking for an
|
|
53072
|
+
* already-synthesized method, so an outer struct nested in the same
|
|
53073
|
+
* pre-pass resolves a later-declared field struct. */
|
|
53074
|
+
function field_copyable(type, status) {
|
|
52523
53075
|
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
53076
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
53077
|
+
const nested_struct = status.structs.find((s) => s.name === type.name);
|
|
53078
|
+
if (nested_struct) {
|
|
53079
|
+
if (nested_struct.is_simple_type) return true;
|
|
53080
|
+
return struct_is_copyable(nested_struct, status);
|
|
53081
|
+
}
|
|
53082
|
+
const en = status.enums.find((e) => e.name === type.name);
|
|
53083
|
+
if (en) return !en.has_associated_data;
|
|
53084
|
+
return !status.traits.some((t) => t.name === type.name);
|
|
53085
|
+
}
|
|
53086
|
+
/** Whether a `List<T>` element type is deep-copyable (see
|
|
53087
|
+
* `field_copyable`). */
|
|
53088
|
+
function element_copyable(type, status) {
|
|
53089
|
+
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
53090
|
+
if (type.name === "string") return true;
|
|
53091
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
52524
53092
|
const nested = status.structs.find((s) => s.name === type.name);
|
|
52525
|
-
|
|
53093
|
+
if (nested) {
|
|
53094
|
+
if (nested.is_simple_type) return true;
|
|
53095
|
+
return !nested.is_class && struct_is_copyable(nested, status);
|
|
53096
|
+
}
|
|
53097
|
+
const en = status.enums.find((e) => e.name === type.name);
|
|
53098
|
+
if (en) return !en.has_associated_data;
|
|
53099
|
+
return !status.traits.some((t) => t.name === type.name);
|
|
53100
|
+
}
|
|
53101
|
+
/** Whether `build_copy` must route the field through a `.copy()` call (a
|
|
53102
|
+
* nested copyable struct, or a `List<T>` field): everything else byte
|
|
53103
|
+
* copies through plain field assignment (strings strdup via the field-write
|
|
53104
|
+
* path, primitives/plain enums are flat values). */
|
|
53105
|
+
function field_copy_call(type, status) {
|
|
53106
|
+
if (!type.name || type.is_array || type.is_ref || type.is_view || type.is_nullable) return false;
|
|
53107
|
+
if (type.name === "List" && type.type_args?.length === 1) return element_copyable(type.type_args[0], status);
|
|
53108
|
+
const nested_struct = status.structs.find((s) => s.name === type.name);
|
|
53109
|
+
return !!nested_struct && !nested_struct.is_simple_type && struct_is_copyable(nested_struct, status);
|
|
52526
53110
|
}
|
|
52527
53111
|
/**
|
|
52528
53112
|
* Synthesize `pub func copy(self) -> Struct` for owning value structs: a
|
|
@@ -52538,8 +53122,8 @@ function build_copy(struct, status) {
|
|
|
52538
53122
|
const ctor = new FunctionCallNode(-1, struct.name, new Type(struct.name), ctor_args.map((name) => field_access("self", name)));
|
|
52539
53123
|
const statements = [new DeclarationNode(-1, "private", "var", "c", new Type(struct.name), ctor)];
|
|
52540
53124
|
for (const field of struct.fields) {
|
|
52541
|
-
const right =
|
|
52542
|
-
statements.push(new AssignmentNode(-1, field_access("c", field.name), right
|
|
53125
|
+
const right = field_copy_call(field.type, status) ? method_call(field_access("self", field.name), "copy") : field_access("self", field.name);
|
|
53126
|
+
statements.push(new AssignmentNode(-1, field_access("c", field.name), right));
|
|
52543
53127
|
}
|
|
52544
53128
|
statements.push(new ReturnNode(-1, new ValueNode(-1, "c")));
|
|
52545
53129
|
return new FunctionNode(-1, "pub", "copy", new Type(struct.name), [self_param(struct)], statements);
|
|
@@ -53618,7 +54202,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53618
54202
|
mono_field.type = constructed;
|
|
53619
54203
|
}
|
|
53620
54204
|
}
|
|
53621
|
-
substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits,
|
|
54205
|
+
substitute_raw_in_node(mono_field.value, substitution, status.structs, void 0, status.traits, {
|
|
54206
|
+
enums: status.enums,
|
|
54207
|
+
bitsets: status.bitsets
|
|
54208
|
+
});
|
|
53622
54209
|
}
|
|
53623
54210
|
mono_field.is_library = field.is_library;
|
|
53624
54211
|
mono_field.is_readonly = field.is_readonly;
|
|
@@ -53653,7 +54240,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53653
54240
|
for (const func of generic_struct.functions) {
|
|
53654
54241
|
if (func.name === "#init") continue;
|
|
53655
54242
|
const cloned = clone_node(func);
|
|
53656
|
-
substitute_raw_types(cloned, substitution, status.structs, status.traits,
|
|
54243
|
+
substitute_raw_types(cloned, substitution, status.structs, status.traits, {
|
|
54244
|
+
enums: status.enums,
|
|
54245
|
+
bitsets: status.bitsets
|
|
54246
|
+
});
|
|
53657
54247
|
fold_substituted_constant_ifs(cloned.statements);
|
|
53658
54248
|
rename_local_labels(cloned, mono_name);
|
|
53659
54249
|
const retype_roots = [...cloned.statements, ...cloned.allocations ?? []];
|
|
@@ -53666,7 +54256,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53666
54256
|
param.type = substitute_type(param.type, substitution);
|
|
53667
54257
|
substitute_param_signature(param, substitution);
|
|
53668
54258
|
if (param.type.type_args?.length) param.type = materialize_generic_enum_type(param.type, status);
|
|
53669
|
-
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits,
|
|
54259
|
+
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
|
|
54260
|
+
enums: status.enums,
|
|
54261
|
+
bitsets: status.bitsets
|
|
54262
|
+
});
|
|
53670
54263
|
}
|
|
53671
54264
|
retype_param_references(retype_roots, cloned.params);
|
|
53672
54265
|
retype_local_references(retype_roots);
|
|
@@ -53694,7 +54287,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53694
54287
|
if (variadic_init_unsupported(generic_init)) continue;
|
|
53695
54288
|
if (!generic_init.statements.some((s) => s.node_type !== "raw")) continue;
|
|
53696
54289
|
const cloned = clone_node(generic_init);
|
|
53697
|
-
substitute_raw_types(cloned, substitution, status.structs, status.traits,
|
|
54290
|
+
substitute_raw_types(cloned, substitution, status.structs, status.traits, {
|
|
54291
|
+
enums: status.enums,
|
|
54292
|
+
bitsets: status.bitsets
|
|
54293
|
+
});
|
|
53698
54294
|
fold_substituted_constant_ifs(cloned.statements);
|
|
53699
54295
|
rename_local_labels(cloned, mono_name);
|
|
53700
54296
|
cloned.return_type = new Type(mono_name);
|
|
@@ -53702,7 +54298,10 @@ function monomorphize(generic_struct, type_args, status) {
|
|
|
53702
54298
|
for (const param of cloned.params) {
|
|
53703
54299
|
param.type = substitute_type(param.type, substitution);
|
|
53704
54300
|
substitute_param_signature(param, substitution);
|
|
53705
|
-
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits,
|
|
54301
|
+
if (param.constraint) substitute_raw_in_node(param.constraint, substitution, status.structs, void 0, status.traits, {
|
|
54302
|
+
enums: status.enums,
|
|
54303
|
+
bitsets: status.bitsets
|
|
54304
|
+
});
|
|
53706
54305
|
}
|
|
53707
54306
|
const cloned_self = cloned.params.find((p) => p.is_self_param);
|
|
53708
54307
|
if (cloned_self) cloned_self.type = new Type(mono_name);
|
|
@@ -54207,7 +54806,7 @@ function derive_annotations_for_access_func(access_node, fc, status) {
|
|
|
54207
54806
|
const p = func.params[i];
|
|
54208
54807
|
if (p.is_self_param) continue;
|
|
54209
54808
|
if (!p.type?.is_nullable) continue;
|
|
54210
|
-
if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
|
|
54809
|
+
if (status.structs.find((s) => s.name === p.type.name && !s.is_class) || is_nullable_scalar_type(p.type, status)) nullable_indices.push(i - (self_offset >= 0 ? self_offset + 1 : 0));
|
|
54211
54810
|
}
|
|
54212
54811
|
if (nullable_indices.length > 0) fc.nullable_param_indices = nullable_indices;
|
|
54213
54812
|
if (func.return_constraint && !fc.return_bounds) {
|
|
@@ -54586,7 +55185,7 @@ function fold_substituted_constant_ifs(statements) {
|
|
|
54586
55185
|
statements.splice(i, 1, ...kept);
|
|
54587
55186
|
}
|
|
54588
55187
|
}
|
|
54589
|
-
function substitute_raw_types(func, substitution, structs, traits,
|
|
55188
|
+
function substitute_raw_types(func, substitution, structs, traits, tables) {
|
|
54590
55189
|
const deref_params = /* @__PURE__ */ new Set();
|
|
54591
55190
|
for (const param of func.params) {
|
|
54592
55191
|
const resolved = substitution.get(param.type.name);
|
|
@@ -54595,7 +55194,7 @@ function substitute_raw_types(func, substitution, structs, traits, enums) {
|
|
|
54595
55194
|
const s = structs.find((x) => x.name === resolved);
|
|
54596
55195
|
if (s && !s.is_simple_type && !s.is_class) deref_params.add(param.name);
|
|
54597
55196
|
}
|
|
54598
|
-
for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits,
|
|
55197
|
+
for (const stmt of func.statements) substitute_raw_in_node(stmt, substitution, structs, deref_params, traits, tables);
|
|
54599
55198
|
}
|
|
54600
55199
|
function rename_local_labels(node, prefix) {
|
|
54601
55200
|
if (node.node_type === "raw") {
|
|
@@ -54628,7 +55227,7 @@ function raw_type_alignment(name) {
|
|
|
54628
55227
|
function raw_align_to(offset, alignment) {
|
|
54629
55228
|
return Math.ceil(offset / alignment) * alignment;
|
|
54630
55229
|
}
|
|
54631
|
-
function raw_type_size(name, structs,
|
|
55230
|
+
function raw_type_size(name, structs, tables) {
|
|
54632
55231
|
switch (name) {
|
|
54633
55232
|
case "bool":
|
|
54634
55233
|
case "int8":
|
|
@@ -54639,13 +55238,13 @@ function raw_type_size(name, structs, enums) {
|
|
|
54639
55238
|
case "int32":
|
|
54640
55239
|
case "uint32": return 4;
|
|
54641
55240
|
}
|
|
54642
|
-
const enum_node = enums
|
|
55241
|
+
const enum_node = tables.enums.find((e) => e.name === name && !e.is_generic);
|
|
54643
55242
|
if (enum_node) {
|
|
54644
55243
|
if (!enum_node.has_associated_data) return 8;
|
|
54645
55244
|
let max_payload = 0;
|
|
54646
55245
|
for (const c of enum_node.cases) {
|
|
54647
55246
|
let case_size = 0;
|
|
54648
|
-
for (const p of c.params) case_size += raw_type_size(p.type.name, structs,
|
|
55247
|
+
for (const p of c.params) case_size += raw_type_size(p.type.name, structs, tables);
|
|
54649
55248
|
max_payload = Math.max(max_payload, case_size);
|
|
54650
55249
|
}
|
|
54651
55250
|
return 8 + Math.ceil(max_payload / 8) * 8;
|
|
@@ -54660,9 +55259,9 @@ function raw_type_size(name, structs, enums) {
|
|
|
54660
55259
|
size += 16;
|
|
54661
55260
|
continue;
|
|
54662
55261
|
}
|
|
54663
|
-
size += raw_type_size(field.type.name, structs,
|
|
55262
|
+
size += raw_type_size(field.type.name, structs, tables);
|
|
54664
55263
|
if (field.type.is_nullable && structs.find((s) => s.name === field.type.name && !s.is_simple_type && !s.is_class)) size += 8;
|
|
54665
|
-
else if (is_nullable_scalar_type(field.type)) size += 1;
|
|
55264
|
+
else if (is_nullable_scalar_type(field.type, tables)) size += 1;
|
|
54666
55265
|
}
|
|
54667
55266
|
return raw_align_to(size, 8);
|
|
54668
55267
|
}
|
|
@@ -54707,12 +55306,15 @@ function raw_block_is_pure_asm(value) {
|
|
|
54707
55306
|
function is_reference_type_arg(t, structs, traits) {
|
|
54708
55307
|
return !!traits.some((tr) => tr.name === t.name) || !!structs.find((s) => s.name === t.name && s.is_class);
|
|
54709
55308
|
}
|
|
54710
|
-
function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [],
|
|
55309
|
+
function substitute_raw_in_node(node, substitution, structs, deref_params = /* @__PURE__ */ new Set(), traits = [], tables = {
|
|
55310
|
+
enums: [],
|
|
55311
|
+
bitsets: []
|
|
55312
|
+
}) {
|
|
54711
55313
|
if (node.node_type === "value") {
|
|
54712
55314
|
const vn = node;
|
|
54713
55315
|
for (const [param, type] of substitution) {
|
|
54714
55316
|
if (vn.value === `${param}_SIZE`) {
|
|
54715
|
-
vn.value = String(raw_type_size(type, structs,
|
|
55317
|
+
vn.value = String(raw_type_size(type, structs, tables));
|
|
54716
55318
|
vn.type = new Type("int", true);
|
|
54717
55319
|
return;
|
|
54718
55320
|
}
|
|
@@ -54737,7 +55339,7 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
|
|
|
54737
55339
|
else if (struct_node) c_type_name = `struct ${type}`;
|
|
54738
55340
|
else c_type_name = raw_c_type_name(type);
|
|
54739
55341
|
value = value.replace(new RegExp(`\\b${param}\\b`, "g"), c_type_name);
|
|
54740
|
-
const size = raw_type_size(type, structs,
|
|
55342
|
+
const size = raw_type_size(type, structs, tables);
|
|
54741
55343
|
const size_expr = raw_block_is_pure_asm(value) ? String(size) : `sizeof(${c_type_name})`;
|
|
54742
55344
|
value = value.replace(new RegExp(`\\b${param}_SIZE\\b`, "g"), size_expr);
|
|
54743
55345
|
value = value.replace(new RegExp(`\\b${param}_destroy\\b`, "g"), `${type}_destroy`);
|
|
@@ -54773,22 +55375,22 @@ function substitute_raw_in_node(node, substitution, structs, deref_params = /* @
|
|
|
54773
55375
|
if (any_node.func_return_type?.name) any_node.func_return_type = substitute_type(any_node.func_return_type, substitution);
|
|
54774
55376
|
}
|
|
54775
55377
|
if (any_node.allocations && Array.isArray(any_node.allocations)) {
|
|
54776
|
-
for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55378
|
+
for (const child of any_node.allocations) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54777
55379
|
}
|
|
54778
55380
|
if (any_node.statements && Array.isArray(any_node.statements)) {
|
|
54779
|
-
for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55381
|
+
for (const child of any_node.statements) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54780
55382
|
}
|
|
54781
55383
|
if (any_node.params && Array.isArray(any_node.params)) {
|
|
54782
|
-
for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits,
|
|
55384
|
+
for (const child of any_node.params) if (child && typeof child === "object" && "node_type" in child) substitute_raw_in_node(child, substitution, structs, deref_params, traits, tables);
|
|
54783
55385
|
}
|
|
54784
|
-
if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits,
|
|
54785
|
-
if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits,
|
|
54786
|
-
if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits,
|
|
54787
|
-
if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits,
|
|
54788
|
-
if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits,
|
|
54789
|
-
if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits,
|
|
54790
|
-
if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits,
|
|
54791
|
-
if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits,
|
|
55386
|
+
if (any_node.value && any_node.value.node_type) substitute_raw_in_node(any_node.value, substitution, structs, deref_params, traits, tables);
|
|
55387
|
+
if (any_node.left_value?.node_type) substitute_raw_in_node(any_node.left_value, substitution, structs, deref_params, traits, tables);
|
|
55388
|
+
if (any_node.right_value?.node_type) substitute_raw_in_node(any_node.right_value, substitution, structs, deref_params, traits, tables);
|
|
55389
|
+
if (any_node.target?.node_type) substitute_raw_in_node(any_node.target, substitution, structs, deref_params, traits, tables);
|
|
55390
|
+
if (any_node.access?.node_type) substitute_raw_in_node(any_node.access, substitution, structs, deref_params, traits, tables);
|
|
55391
|
+
if (any_node.swap?.node_type) substitute_raw_in_node(any_node.swap, substitution, structs, deref_params, traits, tables);
|
|
55392
|
+
if (any_node.condition?.node_type) substitute_raw_in_node(any_node.condition, substitution, structs, deref_params, traits, tables);
|
|
55393
|
+
if (any_node.index?.node_type) substitute_raw_in_node(any_node.index, substitution, structs, deref_params, traits, tables);
|
|
54792
55394
|
}
|
|
54793
55395
|
function specialize_function(generic_func, call_node, status) {
|
|
54794
55396
|
const substitution = /* @__PURE__ */ new Map();
|
|
@@ -55760,6 +56362,14 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55760
56362
|
}
|
|
55761
56363
|
node.type = func.return_type;
|
|
55762
56364
|
node.is_static = func.is_static;
|
|
56365
|
+
if (target_type?.type_args?.length && node.type?.type_args?.length) {
|
|
56366
|
+
const owner = func.scope;
|
|
56367
|
+
if (owner && owner.node_type === "struct" && owner.is_generic && owner.type_params.length === target_type.type_args.length) {
|
|
56368
|
+
const rebind = /* @__PURE__ */ new Map();
|
|
56369
|
+
owner.type_params.forEach((tp, i) => rebind.set(tp, target_type.type_args[i].name));
|
|
56370
|
+
node.type = substitute_type(node.type, rebind);
|
|
56371
|
+
}
|
|
56372
|
+
}
|
|
55763
56373
|
if (node.type?.type_args?.length) instantiate_generic_type(node.type, status);
|
|
55764
56374
|
if (node.type && node.type.name && self_value && is_class_type$1(node.type.name, status) && receiver_is_const(target_type, self_value, status)) {
|
|
55765
56375
|
node.type = clone_type$2(node.type);
|
|
@@ -55807,7 +56417,7 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55807
56417
|
for (let [i, param] of node.params.entries()) {
|
|
55808
56418
|
const func_param = func.params[i + self_offset];
|
|
55809
56419
|
if (func_param && func_param.type.is_nullable && !func_param.is_self_param && !func_param.type.is_ref && !func_param.type.is_array) {
|
|
55810
|
-
if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type)) {
|
|
56420
|
+
if (status.structs.find((st) => st.name === func_param.type.name && !st.is_class && !st.is_simple_type) || is_nullable_scalar_type(func_param.type, status)) {
|
|
55811
56421
|
if (!node.nullable_param_indices) node.nullable_param_indices = [];
|
|
55812
56422
|
if (!node.nullable_param_indices.includes(i)) node.nullable_param_indices.push(i);
|
|
55813
56423
|
}
|
|
@@ -55943,7 +56553,10 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
55943
56553
|
else {
|
|
55944
56554
|
const stamped = String(func_param.type.length.value);
|
|
55945
56555
|
const incoming = String(param_type.length.value);
|
|
55946
|
-
if (stamped !== incoming)
|
|
56556
|
+
if (stamped !== incoming) {
|
|
56557
|
+
if (is_heap_array_type(func_param.type)) func_param.type.length = void 0;
|
|
56558
|
+
else add_error(status, `Call passes ${incoming} elements to array parameter '${func_param.name}' whose compile-time length is ${stamped} — the raw array ABI carries no runtime length, so one parameter serves one compile-time length; use Array<${func_param.type.name}> for varying lengths`, param.start);
|
|
56559
|
+
}
|
|
55947
56560
|
}
|
|
55948
56561
|
}
|
|
55949
56562
|
let arg_value;
|
|
@@ -56174,6 +56787,13 @@ function check_function_call(node, status, func, target_type, self_value, self_p
|
|
|
56174
56787
|
if (typeof size_val === "number" && size_val > 0) record_buffer_cap(self_path, size_val, status);
|
|
56175
56788
|
}
|
|
56176
56789
|
}
|
|
56790
|
+
if (self_path && self_path !== "?" && is_bufferish && func.name === "copy") {
|
|
56791
|
+
const source_cap = lookup_buffer_cap(self_path, status);
|
|
56792
|
+
if (source_cap !== void 0) {
|
|
56793
|
+
const lhs_name = find_lhs_var_name(status);
|
|
56794
|
+
if (lhs_name) record_buffer_cap(lhs_name, source_cap, status);
|
|
56795
|
+
}
|
|
56796
|
+
}
|
|
56177
56797
|
if (func.return_constraint) {
|
|
56178
56798
|
const param_to_arg = /* @__PURE__ */ new Map();
|
|
56179
56799
|
if (self_path && self_path !== "?") param_to_arg.set("self", path_to_node(self_path));
|
|
@@ -60227,6 +60847,68 @@ function parse_case_branch(status) {
|
|
|
60227
60847
|
return null;
|
|
60228
60848
|
}
|
|
60229
60849
|
//#endregion
|
|
60850
|
+
//#region ../src/parse/utils/parse_embed_file.ts
|
|
60851
|
+
/**
|
|
60852
|
+
* `#embed_file("relative/path")` — a compile-time string literal whose value
|
|
60853
|
+
* is the contents of the file at parse time. The path resolves against the
|
|
60854
|
+
* package root (the directory containing package.jsonc, found by climbing
|
|
60855
|
+
* from the entry file), falling back to the entry file's folder; with no
|
|
60856
|
+
* known base it resolves against the process CWD.
|
|
60857
|
+
*
|
|
60858
|
+
* The parsed form is a plain string-literal ValueNode: escapes are encoded
|
|
60859
|
+
* with the source-level escape set (`\\`, `\"`, `\n`, `\r`, `\t`, and
|
|
60860
|
+
* three-digit octal for other control bytes — self-terminating, matching the
|
|
60861
|
+
* escape scanner), so every downstream consumer (length counting, C/asm
|
|
60862
|
+
* emission) treats it exactly like a hand-written literal.
|
|
60863
|
+
*/
|
|
60864
|
+
function parse_embed_file(status) {
|
|
60865
|
+
const start = get_index(status);
|
|
60866
|
+
consume(status);
|
|
60867
|
+
const name = consume(status);
|
|
60868
|
+
if (name !== "embed_file") {
|
|
60869
|
+
add_error(status, `Unknown compiler directive '#${name}' — did you mean '#embed_file'?`, start);
|
|
60870
|
+
return new ValueNode(start, "\"\"");
|
|
60871
|
+
}
|
|
60872
|
+
if (!expect("(", status)) return new ValueNode(start, "\"\"");
|
|
60873
|
+
const path_token = status.tokens[status.i];
|
|
60874
|
+
if (!path_token || !path_token.value.startsWith("\"")) {
|
|
60875
|
+
add_error(status, "Expected a string literal path in #embed_file", start);
|
|
60876
|
+
return new ValueNode(start, "\"\"");
|
|
60877
|
+
}
|
|
60878
|
+
status.i += 1;
|
|
60879
|
+
const rel_path = path_token.value.slice(1, -1);
|
|
60880
|
+
if (!expect(")", status)) return new ValueNode(start, "\"\"");
|
|
60881
|
+
if (!rel_path) {
|
|
60882
|
+
add_error(status, "#embed_file path is empty", start);
|
|
60883
|
+
return new ValueNode(start, "\"\"");
|
|
60884
|
+
}
|
|
60885
|
+
const base = status.embed_root ?? process.cwd();
|
|
60886
|
+
const full = path.isAbsolute(rel_path) ? rel_path : path.join(base, rel_path);
|
|
60887
|
+
let content;
|
|
60888
|
+
try {
|
|
60889
|
+
content = fs.readFileSync(full, "utf8");
|
|
60890
|
+
} catch {
|
|
60891
|
+
add_error(status, `#embed_file could not read '${rel_path}' (resolved '${full}')`, start);
|
|
60892
|
+
return new ValueNode(start, "\"\"");
|
|
60893
|
+
}
|
|
60894
|
+
return new ValueNode(start, `"${escape_embedded(content)}"`);
|
|
60895
|
+
}
|
|
60896
|
+
/** Encode file contents as a source-level string literal body. */
|
|
60897
|
+
function escape_embedded(content) {
|
|
60898
|
+
let out = "";
|
|
60899
|
+
for (const ch of content) {
|
|
60900
|
+
const code = ch.charCodeAt(0);
|
|
60901
|
+
if (ch === "\\") out += "\\\\";
|
|
60902
|
+
else if (ch === "\"") out += "\\\"";
|
|
60903
|
+
else if (ch === "\n") out += "\\n";
|
|
60904
|
+
else if (ch === "\r") out += "\\r";
|
|
60905
|
+
else if (ch === " ") out += "\\t";
|
|
60906
|
+
else if (code < 32 || code === 127) out += `\\${code.toString(8).padStart(3, "0")}`;
|
|
60907
|
+
else out += ch;
|
|
60908
|
+
}
|
|
60909
|
+
return out;
|
|
60910
|
+
}
|
|
60911
|
+
//#endregion
|
|
60230
60912
|
//#region ../src/parse/parse_expression.ts
|
|
60231
60913
|
function restructure_op(start, current_op, current_prec, current_node, expression) {
|
|
60232
60914
|
if (current_prec <= operator_precedence(expression.op)) {
|
|
@@ -60366,6 +61048,7 @@ function parse_primary(status, value) {
|
|
|
60366
61048
|
expect(")", status);
|
|
60367
61049
|
return node;
|
|
60368
61050
|
}
|
|
61051
|
+
case "#": return parse_embed_file(status);
|
|
60369
61052
|
case "if": return parse_if_else(status);
|
|
60370
61053
|
case "match": return parse_match(status);
|
|
60371
61054
|
case "switch": return parse_switch(status);
|
|
@@ -61508,7 +62191,8 @@ function parse(source, library, file_path, options) {
|
|
|
61508
62191
|
qualified_paths: [],
|
|
61509
62192
|
library,
|
|
61510
62193
|
errors: [],
|
|
61511
|
-
allow_user_raw: options?.allow_user_raw
|
|
62194
|
+
allow_user_raw: options?.allow_user_raw,
|
|
62195
|
+
embed_root: embed_root_for(file_path)
|
|
61512
62196
|
};
|
|
61513
62197
|
if (library) status.unsafe_boundary = user_source_length;
|
|
61514
62198
|
parse_statement(status);
|
|
@@ -61585,6 +62269,23 @@ function mark_library_nodes(root, boundary) {
|
|
|
61585
62269
|
walk(root);
|
|
61586
62270
|
}
|
|
61587
62271
|
/**
|
|
62272
|
+
* The base directory for `#embed_file` paths: the package root (the nearest
|
|
62273
|
+
* enclosing directory with a package.jsonc), falling back to the entry
|
|
62274
|
+
* file's folder. No file path — undefined, so `#embed_file` resolves
|
|
62275
|
+
* against the process CWD.
|
|
62276
|
+
*/
|
|
62277
|
+
function embed_root_for(file_path) {
|
|
62278
|
+
if (!file_path) return void 0;
|
|
62279
|
+
let dir = path.dirname(path.resolve(file_path));
|
|
62280
|
+
for (let i = 0; i < 32; i++) {
|
|
62281
|
+
if (fs.existsSync(path.join(dir, "package.jsonc"))) return dir;
|
|
62282
|
+
const parent = path.dirname(dir);
|
|
62283
|
+
if (parent === dir) break;
|
|
62284
|
+
dir = parent;
|
|
62285
|
+
}
|
|
62286
|
+
return path.dirname(path.resolve(file_path));
|
|
62287
|
+
}
|
|
62288
|
+
/**
|
|
61588
62289
|
* Append the library source that `source` needs, exactly as `parse` does.
|
|
61589
62290
|
* Exported so tooling (e.g. the editor extension) can reproduce the combined
|
|
61590
62291
|
* source that node offsets refer to.
|
|
@@ -61838,7 +62539,7 @@ function print_help() {
|
|
|
61838
62539
|
" nomen check --in [file/folder] Parse and check only",
|
|
61839
62540
|
" nomen format [--in folder] Reformat every .nm file",
|
|
61840
62541
|
" nomen docs [--in file] Generate markdown documentation",
|
|
61841
|
-
" nomen test [--in folder] Discover and run *.test.nm files",
|
|
62542
|
+
" nomen test [--in folder] Discover and run *.test.nm files (binary CWD = --in folder)",
|
|
61842
62543
|
" nomen init <name> Scaffold a new project in ./<name>",
|
|
61843
62544
|
" nomen lib-path Print the bundled System library path",
|
|
61844
62545
|
"",
|
|
@@ -62428,7 +63129,7 @@ function extract_leaks(stdout) {
|
|
|
62428
63129
|
* Compile one `*.test.nm` file (with the generated harness) and run it,
|
|
62429
63130
|
* returning its parsed records and any crash info.
|
|
62430
63131
|
*/
|
|
62431
|
-
function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime) {
|
|
63132
|
+
function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime, release = false, cwd) {
|
|
62432
63133
|
const start = performance.now();
|
|
62433
63134
|
const source_text = fs.readFileSync(entry_path, "utf8");
|
|
62434
63135
|
const harness = generate_harness(extract_test_functions(source_text), extract_bench_functions(source_text));
|
|
@@ -62455,7 +63156,8 @@ function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime)
|
|
|
62455
63156
|
}
|
|
62456
63157
|
const buildResult = build(parsed.root, {
|
|
62457
63158
|
arch,
|
|
62458
|
-
audit
|
|
63159
|
+
audit,
|
|
63160
|
+
optimize: release
|
|
62459
63161
|
});
|
|
62460
63162
|
if (buildResult.errors && buildResult.errors.length) {
|
|
62461
63163
|
result.ok = false;
|
|
@@ -62490,11 +63192,19 @@ function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime)
|
|
|
62490
63192
|
const outfile = path.join(buildDir, path.basename(entry_path, ".nm"));
|
|
62491
63193
|
fs.writeFileSync(path.join(buildDir, "main.h"), buildResult.headers ?? "");
|
|
62492
63194
|
fs.writeFileSync(codefile, buildResult.code);
|
|
63195
|
+
let companionfile;
|
|
63196
|
+
if (buildResult.companion) {
|
|
63197
|
+
const comp_ext = platform === "macos" || platform === "ios" ? ".m" : ".c";
|
|
63198
|
+
companionfile = path.join(buildDir, path.basename(entry_path, ".nm") + "_companion" + comp_ext);
|
|
63199
|
+
fs.writeFileSync(companionfile, buildResult.companion);
|
|
63200
|
+
}
|
|
62493
63201
|
const link_args = [
|
|
62494
63202
|
"-o",
|
|
62495
63203
|
outfile,
|
|
62496
63204
|
codefile
|
|
62497
63205
|
];
|
|
63206
|
+
if (companionfile) link_args.push(companionfile);
|
|
63207
|
+
if (release) link_args.unshift("-O2");
|
|
62498
63208
|
if (audit_obj) link_args.push(audit_obj);
|
|
62499
63209
|
if (platform === "macos" || platform === "ios") link_args.push("-framework", "CoreGraphics", "-framework", "Foundation", "-framework", "AppKit", "-lobjc");
|
|
62500
63210
|
try {
|
|
@@ -62525,7 +63235,8 @@ function run_test_file(entry_path, lib_path, arch, audit = false, audit_runtime)
|
|
|
62525
63235
|
"pipe"
|
|
62526
63236
|
],
|
|
62527
63237
|
timeout: 3e4,
|
|
62528
|
-
maxBuffer: 16777216
|
|
63238
|
+
maxBuffer: 16777216,
|
|
63239
|
+
cwd: cwd ?? process.cwd()
|
|
62529
63240
|
});
|
|
62530
63241
|
} catch (err) {
|
|
62531
63242
|
runStdout = err.stdout ? err.stdout.toString() : "";
|
|
@@ -62607,9 +63318,10 @@ function runTests(root, options = {}) {
|
|
|
62607
63318
|
const files = collect_test_files(root).filter((f) => !options.filter || options.filter.test(f));
|
|
62608
63319
|
console.log(`Found ${files.length} test file(s)\n`);
|
|
62609
63320
|
const startTime = performance.now();
|
|
63321
|
+
const binary_cwd = path.resolve(root);
|
|
62610
63322
|
const results = [];
|
|
62611
63323
|
for (const file of files) {
|
|
62612
|
-
const result = run_test_file(file, lib, arch, options.audit, options.audit_runtime);
|
|
63324
|
+
const result = run_test_file(file, lib, arch, options.audit, options.audit_runtime, options.release, binary_cwd);
|
|
62613
63325
|
report_file(result);
|
|
62614
63326
|
results.push(result);
|
|
62615
63327
|
}
|
|
@@ -62717,7 +63429,8 @@ try {
|
|
|
62717
63429
|
arch: args.arch ?? "aarch64",
|
|
62718
63430
|
filter,
|
|
62719
63431
|
audit: args.audit,
|
|
62720
|
-
audit_runtime: args.audit_runtime
|
|
63432
|
+
audit_runtime: args.audit_runtime,
|
|
63433
|
+
release: args.release
|
|
62721
63434
|
});
|
|
62722
63435
|
process.exit(ok ? 0 : 1);
|
|
62723
63436
|
}
|