Files
gasm-sdk/verify/sigparse.go
T
petrbalvin f58a4fe51d
Test / vet (push) Successful in 45s
Test / test (push) Successful in 1m58s
Test / build (push) Successful in 42s
feat(debug): add named buffer allocation with pattern filling
2026-08-04 23:55:25 +02:00

137 lines
3.6 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package verify
import (
"strings"
)
// FuncSig is a parsed // func signature from the assembly source.
type FuncSig struct {
Name string
Params []Param
Results []Param
}
// Param is a single function parameter.
type Param struct {
Name string
Typ string // "[]byte", "[]int32", "int", "*[32]uint16", etc.
}
// IsPointer reports whether the parameter type is a pointer or slice
// (i.e., it needs a buffer address in the argument block).
func (p Param) IsPointer() bool {
return strings.HasPrefix(p.Typ, "[]") || strings.HasPrefix(p.Typ, "*")
}
// ParseFuncSig extracts the function signature from a "// func ..." comment.
func ParseFuncSig(comment string) (FuncSig, bool) {
m := funcSigRe.FindStringSubmatch(strings.TrimSpace(comment))
if m == nil {
return FuncSig{}, false
}
sig := FuncSig{Name: m[1]}
sig.Params = parseParamsExported(m[2])
// Results may be "(a int, b int)" or "int" or "(int, error)".
res := strings.TrimSpace(m[3])
res = strings.TrimPrefix(res, "(")
res = strings.TrimSuffix(res, ")")
if res != "" {
sig.Results = parseParamsExported(res)
}
return sig, true
}
// parseParamsExported splits "a []byte, b []int32" into typed parameters.
func parseParamsExported(s string) []Param {
s = strings.TrimSpace(s)
if s == "" {
return nil
}
var out []Param
for _, field := range strings.Split(s, ",") {
field = strings.TrimSpace(field)
if field == "" {
continue
}
parts := strings.Fields(field)
if len(parts) == 1 {
// Unnamed: "int" or "[]byte".
out = append(out, Param{Typ: parts[0]})
} else {
// Named: "a []byte".
out = append(out, Param{Name: parts[0], Typ: parts[1]})
}
}
return out
}
// ExtractFuncSig extracts the // func signature for the named function
// from the assembly source.
func ExtractFuncSig(src, funcName string) (FuncSig, bool) {
lines := strings.Split(src, "\n")
for i, line := range lines {
if sig, ok := ParseFuncSig(line); ok && sig.Name == funcName {
// Verify the next non-comment line is the TEXT directive.
for j := i + 1; j < len(lines); j++ {
trimmed := strings.TrimSpace(lines[j])
if trimmed == "" || strings.HasPrefix(trimmed, "//") {
continue
}
if strings.HasPrefix(trimmed, "TEXT") {
return sig, true
}
break
}
}
}
return FuncSig{}, false
}
// ArgLayout computes the ABI0 argument layout for a function signature.
// Returns the offset of each parameter in the argument block.
func ArgLayout(sig FuncSig) []ArgOffset {
var offsets []ArgOffset
off := 0
for _, p := range sig.Params {
size := paramSize(p.Typ)
offsets = append(offsets, ArgOffset{Name: p.Name, Typ: p.Typ, Offset: off, Size: size, IsPtr: p.IsPointer()})
off += size
}
return offsets
}
// ArgOffset describes one parameter's position in the argument block.
type ArgOffset struct {
Name string
Typ string
Offset int
Size int
IsPtr bool
}
// paramSize returns the size in bytes of a parameter type in the ABI0 layout.
func paramSize(typ string) int {
switch {
case strings.HasPrefix(typ, "[]"):
// Slice: pointer + length + capacity = 24 bytes.
return 24
case strings.HasPrefix(typ, "*"):
// Pointer: 8 bytes.
return 8
case typ == "int", typ == "int64", typ == "uint64", typ == "uintptr":
return 8
case typ == "int32", typ == "uint32", typ == "float32":
return 4
case typ == "int16", typ == "uint16":
return 2
case typ == "int8", typ == "uint8", typ == "byte", typ == "bool":
return 1
default:
// Default to 8 bytes for unknown types.
return 8
}
}