Found while extending the element-shape loop clone (#10185). For a short-string (SSO) immediate, .length returns the byte count, not the UTF-16 code-unit count:
console.log(JSON.parse('{"a":"é"}').a.length); // node: 1 perry: 2
console.log("é".length); // check the literal path too
It is a Perry-wide convention rather than one site: string/char_ops.rs and property_get/generic_dispatch.rs both compute the SSO length from bytes, and #10185's clone reproduces it deliberately so the clone agrees with the path it clones. Heap strings carry utf16_len and are correct. Fixing it means the SSO decode has to count UTF-16 units (or SSO admission has to be restricted to ASCII), and every site above changes together; a gap test with a non-ASCII short string should pin it.
Found while extending the element-shape loop clone (#10185). For a short-string (SSO) immediate,
.lengthreturns the byte count, not the UTF-16 code-unit count:It is a Perry-wide convention rather than one site:
string/char_ops.rsandproperty_get/generic_dispatch.rsboth compute the SSO length from bytes, and #10185's clone reproduces it deliberately so the clone agrees with the path it clones. Heap strings carryutf16_lenand are correct. Fixing it means the SSO decode has to count UTF-16 units (or SSO admission has to be restricted to ASCII), and every site above changes together; a gap test with a non-ASCII short string should pin it.