compiler: lower polymorphic len() to wasm (string + array length)#583
Merged
Conversation
String-wall residual found while verifying the migration corpus is
unblocked: len() did not lower to wasm (Codegen.UnboundVariable "len"),
only string_length did. AffineScript arrays and strings share the same
[len: i32][payload...] header (the list-concat handler already relies on
this), so len() is the same single i32.load at offset 0 regardless of
whether its argument is a String or an array. Merged into the existing
string_length arm.
This unblocks the stdlib string layer on the wasm backend
(starts_with / ends_with / substring / split / join / pad_* all call
len() on strings, and join also on arrays), which the migration corpus's
String.length pattern (30 files) needs.
Verified: len("abcd")=4 and len([10,20,30])=3 lower and run; full
run_codegen_wasm_tests.sh green (no regressions).
Note (separate, not fixed here): string ==/!= still lower to pointer
comparison, not value comparison (string_sub(...) == "ab" is wrongly 0) —
a #555-class interp-vs-wasm divergence, tracked for a follow-up slice. It
only affects string-logic kernels; integer-brain extraction (strings kept
host-side) is unaffected.
https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s
🔍 Hypatia Security ScanFindings: 40 issues detected
View findings[
{
"reason": "Action denoland/setup-deno@v2 needs attention",
"type": "unpinned_action",
"file": "publish-jsr.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in scorecard-enforcer.yml",
"type": "scorecard_publish_with_run_step",
"file": "scorecard-enforcer.yml",
"action": "split_scorecard_publish_job",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in instant-sync.yml",
"type": "secret_action_without_presence_gate",
"file": "instant-sync.yml",
"action": "peter-evans/repository-dispatch",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Shell execution -- validate input before passing to shell (1 occurrences, CWE-78)",
"type": "js_exec_sync",
"file": "/home/runner/work/affinescript/affinescript/packages/affinescript-cli/mod.js",
"action": "flag",
"rule_module": "code_safety",
"severity": "high"
},
{
"reason": "Shell execution -- validate input before passing to shell (2 occurrences, CWE-78)",
"type": "js_exec_sync",
"file": "/home/runner/work/affinescript/affinescript/packages/affine-vscode/mod.js",
"action": "flag",
"rule_module": "code_safety",
"severity": "high"
},
{
"reason": "Shell execution -- validate input before passing to shell (1 occurrences, CWE-78)",
"type": "js_exec_sync",
"file": "/home/runner/work/affinescript/affinescript/affinescript-vite/src/affine-plugin-improved.js",
"action": "flag",
"rule_module": "code_safety",
"severity": "high"
},
{
"reason": "expect() in hot path (32 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/affinescript/affinescript/affinescriptiser/src/codegen/wasm_gen.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "medium"
},
{
"reason": "expect() in hot path (29 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/affinescript/affinescript/affinescriptiser/src/codegen/affine_gen.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "medium"
},
{
"reason": "unsafe block -- requires SAFETY comment (2 occurrences, CWE-676)",
"type": "unsafe_block",
"file": "/home/runner/work/affinescript/affinescript/runtime/src/panic.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "medium"
},
{
"reason": "unsafe block -- requires SAFETY comment (1 occurrences, CWE-676)",
"type": "unsafe_block",
"file": "/home/runner/work/affinescript/affinescript/runtime/src/alloc.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
added a commit
that referenced
this pull request
Jun 14, 2026
…584) ## Migration wave: 7 integer-brain kernels from string-gated idaptik modules First applied wave of the now-unblocked **string-gated corpus**. Phase B classified 71 string-gated files; closing the string wall (slices 1–8) plus the `len()` lowering (#583) opened the integer-brain extraction path. Seven kernels re-decomposed from idaptik `.res` modules into AffineScript brains under `proposals/idaptik/migrated/`, fanned out across 6 parallel agents and **re-verified by me before commit**. Each is a four-gate deliverable — G1 compile, G2 independent-oracle parity sweep, G4 assail. Strings / floats / promises / mutable state stay **host-side** per the C1–C12 recipe; only the pure-integer decision core crosses to wasm. | Kernel | Exports | Parity | Assail | |---|---|---|---| | PortScanner | 4 | 44/44 | clean | | PasswordCracker | 7 | 215/215 | clean | | FirewallDevice | 12 | 164/164 | clean | | Inventory | 9 | 2840/2840 | clean | | Drone | 32 | 1192/1192 | clean | | SecurityDog | 29 | 31533/31533 | clean | | GuardNPC | 19 | 359/359 | clean | **Re-decompositions, not transliterations** — e.g. PasswordCracker inverts the djb2 string-loop so the host walks the string and the brain does i32 math (`Math.imul`/`|0` modelled in the oracle); Inventory packs slot-state into a base-3 Int instead of a mutable array; FirewallDevice keeps CIDR/protocol *string* parsing host-side and decides over integer flags. Floats cross as floored milli-units; out-of-band inputs return guarded `-1` sentinels (assail-clean, no in-band collapse). Each oracle is an independent JS reimplementation from the `.res` semantics, not copied from the `.affine`. **Deduped:** `SecurityAI` dropped — already tracked as `migrated/securityai/` (with a boundary proof) from an earlier wave; `GlobalNetworkData` likewise pre-existed and was left untouched. **Two compiler quirks surfaced** (flagged for the playbook, not fixed here): `total` is a reserved keyword (parse error as an identifier); and an `if { … }` block immediately followed by a parenthesized expression parses as a function application. Both have trivial source-side workarounds. Builds on #583 (`len`, merged) and the string-wall slices (#574/#575/#578). https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s --- _Generated by [Claude Code](https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s)_ Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lower polymorphic
len()to wasm (string + array length)A string-wall residual found while verifying the migration corpus is actually unblocked:
len()did not lower to wasm (Codegen.UnboundVariable "len") — onlystring_lengthdid.AffineScript arrays and strings share the same
[len: i32][payload…]header (the list-concat handler already relies on this), solen()is the same singlei32.loadat offset 0 whether its argument is aStringor an array. Merged into the existingstring_lengtharm.This unblocks the stdlib string layer on the wasm backend (
starts_with/ends_with/substring/split/join/pad_*all calllen()on strings;joinalso on arrays), which the corpus'sString.lengthpattern (30 files) needs.Verified:
len("abcd")=4 andlen([10,20,30])=3 lower and run; fullrun_codegen_wasm_tests.shgreen (no regressions).Separately noted (not fixed here): string
==/!=still lower to pointer comparison, not value comparison (string_sub(...) == "ab"is wrongly0) — a #555-class interp-vs-wasm divergence, tracked for a follow-up slice. It only affects string-logic kernels; integer-brain extraction (strings kept host-side) is unaffected.https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s
Generated by Claude Code