feat(debug): add named buffer allocation with pattern filling
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user