rewrite: address review feedback
This commit is contained in:
@@ -130,12 +130,14 @@ type context struct {
|
||||
rewrites map[string]string
|
||||
}
|
||||
|
||||
const maxStringTypeDepth = 64
|
||||
|
||||
func (p *context) rewriteValue(name string) (string, bool) {
|
||||
if p.rewrites == nil {
|
||||
return "", false
|
||||
}
|
||||
dot := strings.LastIndex(name, ".")
|
||||
if dot < 0 {
|
||||
if dot < 0 || dot == len(name)-1 {
|
||||
return "", false
|
||||
}
|
||||
varName := name[dot+1:]
|
||||
@@ -144,7 +146,9 @@ func (p *context) rewriteValue(name string) (string, bool) {
|
||||
}
|
||||
|
||||
func (p *context) isStringType(typ types.Type) bool {
|
||||
for typ != nil {
|
||||
depth := 0
|
||||
for typ != nil && depth < maxStringTypeDepth {
|
||||
depth++
|
||||
switch t := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return t.Kind() == types.String
|
||||
@@ -164,15 +168,21 @@ func (p *context) globalFullName(g *ssa.Global) string {
|
||||
}
|
||||
|
||||
func (p *context) rewriteInitStore(store *ssa.Store, g *ssa.Global) (string, bool) {
|
||||
if p.rewrites == nil {
|
||||
return "", false
|
||||
}
|
||||
fn := store.Block().Parent()
|
||||
if fn == nil || fn.Synthetic != "package initializer" {
|
||||
return "", false
|
||||
}
|
||||
value, ok := p.rewriteValue(p.globalFullName(g))
|
||||
if !ok || !p.isStringType(g.Type()) {
|
||||
if _, ok := store.Val.(*ssa.Const); !ok {
|
||||
return "", false
|
||||
}
|
||||
if _, ok := store.Val.(*ssa.Const); !ok {
|
||||
if !p.isStringType(g.Type()) {
|
||||
return "", false
|
||||
}
|
||||
value, ok := p.rewriteValue(p.globalFullName(g))
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return value, true
|
||||
@@ -867,9 +877,11 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if g, ok := va.(*ssa.Global); ok {
|
||||
if _, ok := p.rewriteInitStore(v, g); ok {
|
||||
return
|
||||
if p.rewrites != nil {
|
||||
if g, ok := va.(*ssa.Global); ok {
|
||||
if _, ok := p.rewriteInitStore(v, g); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr := p.compileValue(b, va)
|
||||
@@ -1041,6 +1053,16 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
|
||||
}
|
||||
|
||||
// NewPackageEx compiles a Go package to LLVM IR package.
|
||||
//
|
||||
// Parameters:
|
||||
// - prog: target LLVM SSA program context
|
||||
// - patches: optional package patches applied during compilation
|
||||
// - rewrites: per-package string initializers rewritten at compile time
|
||||
// - pkg: SSA package to compile
|
||||
// - files: parsed AST files that belong to the package
|
||||
//
|
||||
// The rewrites map uses short variable names (without package qualifier) and
|
||||
// only affects string-typed globals defined in the current package.
|
||||
func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) {
|
||||
pkgProg := pkg.Prog
|
||||
pkgTypes := pkg.Pkg
|
||||
|
||||
@@ -90,6 +90,9 @@ func TestRewriteValueNoDot(t *testing.T) {
|
||||
if _, ok := ctx.rewriteValue("VarInit"); ok {
|
||||
t.Fatalf("rewriteValue should skip names without package prefix")
|
||||
}
|
||||
if _, ok := ctx.rewriteValue("pkg."); ok {
|
||||
t.Fatalf("rewriteValue should skip trailing dot names")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsStringTypeDefault(t *testing.T) {
|
||||
|
||||
@@ -111,28 +111,33 @@ type OutFmtDetails struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Goos string
|
||||
Goarch string
|
||||
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
|
||||
BinPath string
|
||||
AppExt string // ".exe" on Windows, empty on Unix
|
||||
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
||||
OutFmts OutFmts // Output format specifications (only for Target != "")
|
||||
Emulator bool // run in emulator mode
|
||||
Port string // target port for flashing
|
||||
BaudRate int // baudrate for serial communication
|
||||
RunArgs []string
|
||||
Mode Mode
|
||||
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
|
||||
AbiMode AbiMode
|
||||
GenExpect bool // only valid for ModeCmpTest
|
||||
Verbose bool
|
||||
GenLL bool // generate pkg .ll files
|
||||
CheckLLFiles bool // check .ll files valid
|
||||
CheckLinkArgs bool // check linkargs valid
|
||||
ForceEspClang bool // force to use esp-clang
|
||||
Tags string
|
||||
GlobalRewrites map[string]Rewrites // pkg => var => value
|
||||
Goos string
|
||||
Goarch string
|
||||
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
|
||||
BinPath string
|
||||
AppExt string // ".exe" on Windows, empty on Unix
|
||||
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
||||
OutFmts OutFmts // Output format specifications (only for Target != "")
|
||||
Emulator bool // run in emulator mode
|
||||
Port string // target port for flashing
|
||||
BaudRate int // baudrate for serial communication
|
||||
RunArgs []string
|
||||
Mode Mode
|
||||
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
|
||||
AbiMode AbiMode
|
||||
GenExpect bool // only valid for ModeCmpTest
|
||||
Verbose bool
|
||||
GenLL bool // generate pkg .ll files
|
||||
CheckLLFiles bool // check .ll files valid
|
||||
CheckLinkArgs bool // check linkargs valid
|
||||
ForceEspClang bool // force to use esp-clang
|
||||
Tags string
|
||||
// GlobalRewrites specifies compile-time overrides for global string variables.
|
||||
// Keys are fully qualified package paths (e.g. "main" or "github.com/user/pkg").
|
||||
// Each Rewrites entry maps variable names to replacement string values. Only
|
||||
// string-typed globals are supported and "main" applies to all root main
|
||||
// packages in the current build.
|
||||
GlobalRewrites map[string]Rewrites
|
||||
}
|
||||
|
||||
type Rewrites map[string]string
|
||||
@@ -626,6 +631,8 @@ var (
|
||||
errXflags = errors.New("-X flag requires argument of the form importpath.name=value")
|
||||
)
|
||||
|
||||
const maxRewriteValueLength = 1 << 20 // 1 MiB cap per rewrite value
|
||||
|
||||
func addGlobalString(conf *Config, arg string, mainPkgs []string) {
|
||||
addGlobalStringWith(conf, arg, mainPkgs, true)
|
||||
}
|
||||
@@ -637,6 +644,9 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
|
||||
panic(errXflags)
|
||||
}
|
||||
pkg := arg[:dot]
|
||||
varName := arg[dot+1 : eq]
|
||||
value := arg[eq+1:]
|
||||
validateRewriteInput(pkg, varName, value)
|
||||
pkgs := []string{pkg}
|
||||
if pkg == "main" {
|
||||
pkgs = mainPkgs
|
||||
@@ -647,8 +657,6 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
|
||||
if conf.GlobalRewrites == nil {
|
||||
conf.GlobalRewrites = make(map[string]Rewrites)
|
||||
}
|
||||
varName := arg[dot+1 : eq]
|
||||
value := arg[eq+1:]
|
||||
for _, realPkg := range pkgs {
|
||||
vars := conf.GlobalRewrites[realPkg]
|
||||
if vars == nil {
|
||||
@@ -664,6 +672,18 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
|
||||
}
|
||||
}
|
||||
|
||||
func validateRewriteInput(pkg, varName, value string) {
|
||||
if pkg == "" || strings.ContainsAny(pkg, " \t\r\n") {
|
||||
panic(fmt.Errorf("invalid package path for rewrite: %q", pkg))
|
||||
}
|
||||
if !token.IsIdentifier(varName) {
|
||||
panic(fmt.Errorf("invalid variable name for rewrite: %q", varName))
|
||||
}
|
||||
if len(value) > maxRewriteValueLength {
|
||||
panic(fmt.Errorf("rewrite value too large: %d bytes", len(value)))
|
||||
}
|
||||
}
|
||||
|
||||
// compileExtraFiles compiles extra files (.s/.c) from target configuration and returns object files
|
||||
func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
|
||||
if len(ctx.crossCompile.ExtraFiles) == 0 {
|
||||
@@ -1182,23 +1202,29 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
|
||||
}
|
||||
|
||||
func collectRewriteVars(ctx *context, pkgPath string) map[string]string {
|
||||
var rewrites map[string]string
|
||||
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
|
||||
if data := ctx.buildConf.GlobalRewrites; len(data) != 0 {
|
||||
for pkg, vars := range data {
|
||||
trimmed := strings.TrimPrefix(pkg, altPkgPathPrefix)
|
||||
if trimmed != basePath {
|
||||
continue
|
||||
}
|
||||
for name, value := range vars {
|
||||
if rewrites == nil {
|
||||
rewrites = make(map[string]string)
|
||||
}
|
||||
rewrites[name] = value
|
||||
}
|
||||
}
|
||||
data := ctx.buildConf.GlobalRewrites
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
return rewrites
|
||||
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
|
||||
if vars := data[basePath]; vars != nil {
|
||||
return cloneRewrites(vars)
|
||||
}
|
||||
if vars := data[pkgPath]; vars != nil {
|
||||
return cloneRewrites(vars)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloneRewrites(src Rewrites) map[string]string {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
}
|
||||
dup := make(map[string]string, len(src))
|
||||
for k, v := range src {
|
||||
dup[k] = v
|
||||
}
|
||||
return dup
|
||||
}
|
||||
|
||||
func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Package {
|
||||
|
||||
@@ -31,7 +31,10 @@ func (pkg Package) AddGlobalString(name string, value string) {
|
||||
pkg.NewVarEx(name, prog.Pointer(styp)).Init(Expr{cv, styp})
|
||||
}
|
||||
|
||||
// ConstString returns an SSA string constant expression within this package.
|
||||
// ConstString creates an SSA expression representing a Go string literal. The
|
||||
// returned value is backed by an anonymous global constant and can be used to
|
||||
// initialize package-level variables or other constant contexts that expect a
|
||||
// Go string value.
|
||||
func (pkg Package) ConstString(value string) Expr {
|
||||
prog := pkg.Prog
|
||||
styp := prog.String()
|
||||
|
||||
Reference in New Issue
Block a user