mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
* gcasm: fix cross-chunk symbol gaps in the direct-call path Follow-ups to the direct cross-chunk asm calls change (#61): - The direct-asm resolvers (CalleeSymbol / MemHelperSymbol) trimmed calleeSig's leading "·" and let asmgen's goAsmSymbol re-derive the CALL operand. A spelled remote symbol ("github·com∕...∕p2·Fn0") has no ASCII "." for goAsmSymbol to split on, so it was re-wrapped as a current-package reference ("CALL ·github·com∕...(SB)") that can never link — any direct-asm-retained function making a resolved cross-chunk call produced an undefined symbol at consumer link time. The resolver contract now carries the exact plan 9 operand (without the "(SB)" suffix) and asmgen emits it verbatim. - plan9AsmPathSafe accepted digit-leading import paths ("4d63.com/...", "0xacab.org/..."), but the plan 9 asm lexer accepts digits only after the first identifier rune, so the spelled CALL would fail to assemble. The predicate now requires a letter or "_" at position 0. - That predicate existed as two byte-for-byte copies (codegen's isPlan9AsmSafe, gcasm's plan9AsmPathSafe) under a comment claiming an import cycle prevents sharing; no such cycle exists. Both packages now consult one exported asmgen.Plan9AsmPathSafe, so the linkname alias shape and the direct-CALL / gcasmABI0Keep anchor gates can never disagree. - buildPkg now verifies every captured fn leaves the emission loop either with a TEXT body or registered as a pure-Go fallback. A future degrade path that forgets fallbackNames would otherwise surface only as a link error in the consumer build, with no Build-time diagnostic; it is a Build error naming the fn now. The new cg_crosscall_directasm fixture keeps every op native (no rotl/rotr, which lower to helper CALLs the direct-asm emitter's ForbidCalls refuses) so the retained functions actually emit via asmgen; its regression test fails against the pre-fix resolvers with the mangled current-package spelling in the emitted bundle. Co-Authored-By: Claude Fable 5 <[email protected]> * asmgen: restructure the first-rune check as a switch to satisfy staticcheck golangci-lint's staticcheck (QF1001) flagged the negated compound condition in Plan9AsmPathSafe; the positive-switch form matches the per-byte loop below it. No behavior change. Co-Authored-By: Claude Fable 5 <[email protected]> --------- Co-authored-by: Claude Fable 5 <[email protected]>
54 lines
1.8 KiB
Go
54 lines
1.8 KiB
Go
package asmgen
|
|
|
|
// Plan9AsmPathSafe reports whether an import path can be embedded in
|
|
// a plan 9 asm symbol operand (via the U+00B7 / U+2215 spelling that
|
|
// goAsmSymbol applies — see its doc comment for the lexer mechanics).
|
|
//
|
|
// The asm lexer (src/cmd/asm/internal/lex/tokenizer.go: isIdentRune)
|
|
// accepts letters, "_", U+00B7 and U+2215 as identifier runes, plus
|
|
// digits AFTER the first rune, and nothing else. Two consequences:
|
|
//
|
|
// - "." and "/" are the only ASCII punctuation an operand can
|
|
// carry (through the Unicode substitution); anything else Go
|
|
// module paths permit ("-", "+", "~", " ", ...) has no plan 9
|
|
// counterpart and is unrepresentable — there is no escape
|
|
// syntax.
|
|
//
|
|
// - A spelled path lexes as ONE identifier starting at the path's
|
|
// first byte, so a digit-leading host ("4d63.com/...",
|
|
// "0xacab.org/...") is unrepresentable too: position 0 of the
|
|
// identifier would be a digit and the token lexes as a number.
|
|
//
|
|
// The empty path is rejected so callers treat it as unsafe.
|
|
//
|
|
// This single function is the contract both halves of a generated
|
|
// bundle consult: internal/codegen keys the decl-only vs
|
|
// wrapper-pair linkname alias shape off it, and internal/gcasm keys
|
|
// the direct-CALL vs gcasmFwd forwarding shape plus the
|
|
// gcasmABI0Keep anchor off it. Keeping one copy is what guarantees
|
|
// the two decisions can never disagree.
|
|
func Plan9AsmPathSafe(path string) bool {
|
|
if path == "" {
|
|
return false
|
|
}
|
|
switch c := path[0]; {
|
|
case c >= 'a' && c <= 'z':
|
|
case c >= 'A' && c <= 'Z':
|
|
case c == '_':
|
|
default:
|
|
return false
|
|
}
|
|
for i := 1; i < len(path); i++ {
|
|
c := path[i]
|
|
switch {
|
|
case c >= 'a' && c <= 'z':
|
|
case c >= 'A' && c <= 'Z':
|
|
case c >= '0' && c <= '9':
|
|
case c == '_' || c == '.' || c == '/':
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|