codegen

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2026 License: CC0-1.0 Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AIDLToGoFileName

func AIDLToGoFileName(name string) string

AIDLToGoFileName converts an AIDL type name to a Go file name. "IServiceManager" -> "iservicemanager.go"

func AIDLToGoName

func AIDLToGoName(name string) string

AIDLToGoName converts an AIDL identifier to a Go-exported name. camelCase -> PascalCase (e.g., "getService" -> "GetService") SCREAMING_SNAKE -> PascalCase (e.g., "FIRST_CALL_TRANSACTION" -> "FirstCallTransaction") Dotted names (nested types) -> flattened PascalCase (e.g., "Foo.Bar" -> "FooBar") already PascalCase -> kept as-is

func AIDLToGoPackage

func AIDLToGoPackage(pkg string) string

AIDLToGoPackage converts an AIDL package name to a Go package path segment. "android.os" -> "android/os" "com.android.internal.foo" -> "com/android/internal_/foo"

The "internal" segment is renamed to "internal_" because Go restricts imports of packages under "internal/" directories to their parent tree.

func AIDLTypeToGo

func AIDLTypeToGo(ts *parser.TypeSpecifier) string

AIDLTypeToGo converts an AIDL TypeSpecifier to a Go type string. Handles primitives, String, IBinder, List<T>, Map<K,V>, arrays, @nullable.

func ComputeTransactionCodes

func ComputeTransactionCodes(
	methods []*parser.MethodDecl,
) map[string]binder.TransactionCode

ComputeTransactionCodes computes the binder transaction code for each method in an AIDL interface, following the Android AIDL transaction code assignment rules: sequential from FirstCallTransaction, with explicit TransactionID overrides resetting the counter.

func GenerateConstants

func GenerateConstants(
	constants []*parser.ConstantDecl,
	f *GoFile,
	prefix string,
) error

GenerateConstants writes const declarations to the given GoFile. prefix is prepended to each constant name to avoid collisions when multiple types in the same package define constants with the same name. Self-references within constant expressions are also prefixed so they resolve to the correct Go constant names.

func GenerateConstantsFile

func GenerateConstantsFile(
	constants []*parser.ConstantDecl,
	pkg string,
) ([]byte, error)

GenerateConstantsFile generates a standalone Go file containing only constant declarations. This is useful when constants are declared at package level.

func GenerateEnum

func GenerateEnum(
	decl *parser.EnumDecl,
	pkg string,
	options ...GenOption,
) ([]byte, error)

GenerateEnum generates Go source for an AIDL enum declaration. The output includes: - A Go type alias for the backing type - Typed constants for each enumerator

func GenerateInterface

func GenerateInterface(
	decl *parser.InterfaceDecl,
	pkg string,
	qualifiedName string,
	options ...GenOption,
) ([]byte, error)

GenerateInterface generates Go source for an AIDL interface declaration. The output includes: - A Go interface type with all methods - A client-side proxy struct - A constructor for the proxy - Proxy method implementations that serialize/deserialize via parcels - Transaction code constants - A descriptor constant

func GenerateParcelable

func GenerateParcelable(
	decl *parser.ParcelableDecl,
	pkg string,
	qualifiedName string,
	options ...GenOption,
) ([]byte, error)

GenerateParcelable generates Go source for an AIDL parcelable declaration. The output includes: - A Go struct with all fields - A MarshalParcel method - An UnmarshalParcel method - Any constants declared inside the parcelable

func GenerateSmokeTests

func GenerateSmokeTests(
	interfaces []*parser.InterfaceDecl,
	goPackage string,
) ([]byte, error)

GenerateSmokeTests generates a smoke_test.go file containing one test function per interface proxy. All interfaces in the same Go package are grouped into a single test file.

func GenerateUnion

func GenerateUnion(
	decl *parser.UnionDecl,
	pkg string,
	qualifiedName string,
	options ...GenOption,
) ([]byte, error)

GenerateUnion generates Go source for an AIDL union declaration. The output includes: - A Go struct with a Tag field and one field per variant - Tag constants for each variant (unless a separate Tag enum exists) - Accessor methods for each variant (Get and Set) - MarshalParcel and UnmarshalParcel methods

func ScreamingSnakeToPascal

func ScreamingSnakeToPascal(name string) string

ScreamingSnakeToPascal converts "FIRST_CALL_TRANSACTION" to "FirstCallTransaction".

Types

type GenOption

type GenOption func(*GenOptions)

GenOption configures code generation.

func WithCurrentDef

func WithCurrentDef(
	def string,
) GenOption

WithCurrentDef sets the fully qualified name of the definition being generated (e.g., "android.hardware.bluetooth.audio.CodecId"). This enables nested type references like "A2dp" to resolve to "CodecId.A2dp" first.

func WithCurrentPkg

func WithCurrentPkg(
	pkg string,
) GenOption

WithCurrentPkg sets the AIDL package of the type being generated. This enables cross-package type references to be qualified correctly.

func WithCycleTypeCallback

func WithCycleTypeCallback(
	cb func(qualifiedName, typesPkg string),
) GenOption

WithCycleTypeCallback sets a callback for types redirected to sub-packages.

func WithImportGraph

func WithImportGraph(
	graph *ImportGraph,
) GenOption

WithImportGraph sets the import graph for cycle detection. When set, cross-package type references that would create import cycles are replaced with any to break the cycle.

func WithRegistry

func WithRegistry(
	registry *resolver.TypeRegistry,
) GenOption

WithRegistry sets the type registry for looking up type definitions during code generation. This enables correct marshaling of enum types (as integers) and interface types (as IBinder).

type GenOptions

type GenOptions struct {
	Registry    *resolver.TypeRegistry
	CurrentPkg  string // AIDL package of the type being generated
	CurrentDef  string // fully qualified name of the definition being generated (for nested type resolution)
	ImportGraph *ImportGraph
	// CycleTypeCallback is called when a type is redirected to a "types"
	// sub-package to break an import cycle. The callback receives the
	// qualified AIDL name and the types sub-package AIDL name.
	CycleTypeCallback func(qualifiedName, typesPkg string)
}

GenOptions holds optional parameters for code generation functions.

type Generator

type Generator struct {
	Resolver   *resolver.Resolver
	OutputDir  string
	SkipErrors bool
	// CycleTypes collects empty parcelables that were redirected to "types"
	// sub-packages during codegen to break import cycles. Populated during
	// GenerateAll and used to generate the sub-package files afterward.
	// Maps qualified AIDL name → types sub-package AIDL name.
	CycleTypes map[string]string
}

Generator generates Go code from AIDL files.

func NewGenerator

func NewGenerator(
	r *resolver.Resolver,
	outputDir string,
) *Generator

NewGenerator creates a Generator that writes output to outputDir.

func (*Generator) GenerateAll

func (g *Generator) GenerateAll() (_err error)

GenerateAll generates Go code for all definitions registered in the resolver. If skipErrors is false (default), generation stops at the first error. Use SetSkipErrors(true) to skip definitions that fail codegen and continue.

func (*Generator) GenerateAllSmokeTests

func (g *Generator) GenerateAllSmokeTests() (_err error)

GenerateAllSmokeTests generates smoke_test.go files for all packages that contain interface definitions. Each file contains one test function per proxy type in the package. Only interfaces whose generated proxy file exists in the output directory are included.

func (*Generator) GenerateFile

func (g *Generator) GenerateFile(
	doc *parser.Document,
) (_err error)

GenerateFile generates Go code for all definitions in a single AIDL document. It runs semantic validation before code generation.

func (*Generator) SetSkipErrors

func (g *Generator) SetSkipErrors(
	skip bool,
)

SetSkipErrors configures whether the generator skips definitions that fail codegen and continues with the rest.

type GoFile

type GoFile struct {
	Pkg     string
	Imports map[string]string // import path -> alias (or "")
	Buf     bytes.Buffer
}

GoFile builds a Go source file with automatic import management.

func NewGoFile

func NewGoFile(pkg string) *GoFile

NewGoFile creates a new GoFile for the given package name.

func (*GoFile) AddImport

func (f *GoFile) AddImport(
	path string,
	alias string,
)

AddImport adds an import. alias can be empty for default import.

func (*GoFile) Bytes

func (f *GoFile) Bytes() ([]byte, error)

Bytes returns the formatted Go source code.

func (*GoFile) P

func (f *GoFile) P(
	fmtStr string,
	args ...any,
)

P writes a line (printf-style) to the file body.

type ImportGraph

type ImportGraph struct {
	// Edges maps each package to the set of packages it depends on.
	Edges map[string]map[string]bool
	// CyclePkgs is the set of packages that participate in import cycles.
	// Two packages in the same strongly connected component (SCC) of size > 1
	// would create a cycle if they imported each other.
	CyclePkgs map[string]int // package -> SCC index
	// BackEdges records the specific edges within SCCs that are back-edges
	// (their removal makes the subgraph acyclic). Only these edges need
	// to be broken to prevent Go import cycles.
	BackEdges map[string]map[string]bool
}

ImportGraph represents the directed dependency graph between AIDL packages. An edge from package A to package B means that package A references a type defined in package B.

func BuildImportGraph

func BuildImportGraph(registry *resolver.TypeRegistry) *ImportGraph

BuildImportGraph scans all definitions in the registry and builds a package dependency graph. It computes strongly connected components (SCCs) to find cycles, then identifies which specific edges within each SCC are back-edges that must be broken.

A single SCC pass that marks ALL intra-SCC edges as cycle-causing is too conservative: it prevents imports between packages that are only indirectly part of a cycle (via long chains through unrelated packages). Instead, after finding SCCs, a DFS within each SCC identifies back-edges (the minimum set of edges whose removal makes the subgraph acyclic). Only those back-edges are marked as cycle-causing.

func (*ImportGraph) AugmentFromGoFiles

func (g *ImportGraph) AugmentFromGoFiles(outputDir string)

AugmentFromGoFiles scans Go files in outputDir for import statements and adds edges to the import graph. This ensures the graph includes dependencies from existing generated code (from previous runs), not just spec-defined types.

func (*ImportGraph) SameSCC

func (g *ImportGraph) SameSCC(srcPkg, targetPkg string) bool

SameSCC returns true if srcPkg and targetPkg are in the same strongly connected component.

func (*ImportGraph) StrictForSCC

func (g *ImportGraph) StrictForSCC(pkg string) *ImportGraph

StrictForSCC creates a copy of the import graph that treats ALL intra-SCC edges as cycle-causing for the SCC containing the given package. This is used when generating types sub-packages, where any cross-package import within the SCC could create a cycle.

func (*ImportGraph) WouldCauseCycle

func (g *ImportGraph) WouldCauseCycle(srcPkg, targetPkg string) bool

WouldCauseCycle returns true if adding an import from srcPkg to targetPkg would create an import cycle. Only back-edges (identified by DFS within each SCC) are considered cycle-causing; forward and cross edges within the same SCC are safe.

func (*ImportGraph) WouldCreateCycle

func (g *ImportGraph) WouldCreateCycle(srcPkg, targetPkg string) bool

WouldCreateCycle returns true if adding an edge from srcPkg to targetPkg would create an import cycle, considering the current edge set. This checks whether there is a path from targetPkg back to srcPkg in the existing graph. Used for edges not captured in the import graph (e.g., typed_object field references from JavaWireFormat).

type MarshalInfo

type MarshalInfo struct {
	// WriteExpr is a format string where %s is the variable name.
	// e.g., "_data.WriteInt32(%s)" where %s is substituted with the var name.
	WriteExpr string

	// ReadExpr is the expression to read the value from a parcel.
	// e.g., "_reply.ReadInt32()" which returns (T, error).
	ReadExpr string

	// NeedsCast is true if the read returns a different type that needs casting.
	NeedsCast bool

	// IsIBinder is true for IBinder types that require constructing a
	// ProxyBinder from the raw handle returned by ReadStrongBinder.
	IsIBinder bool

	// IsInterface is true for user-defined AIDL interface types (not IBinder).
	// These require constructing a typed proxy from the binder handle.
	IsInterface bool

	// IsMap is true for Map<K,V> types. Map serialization requires inline
	// code generation (writing count + key/value pairs) and cannot be
	// expressed as a single WriteExpr/ReadExpr format string.
	IsMap bool
}

MarshalInfo contains the parcel read/write expressions for a type.

func MarshalForType

func MarshalForType(ts *parser.TypeSpecifier) MarshalInfo

MarshalForType returns the MarshalInfo for an AIDL type.

func MarshalForTypeWithRegistry

func MarshalForTypeWithRegistry(
	ts *parser.TypeSpecifier,
	registry *resolver.TypeRegistry,
	pkgAndDef ...string,
) MarshalInfo

MarshalForTypeWithRegistry returns the MarshalInfo for an AIDL type, using the registry to look up whether user-defined types are enums (which use integer marshaling) or interfaces (which use IBinder). currentPkg, if non-empty, enables same-package lookups by trying currentPkg + "." + typeName as a fully qualified name.

type TypeRefResolver

type TypeRefResolver struct {
	Registry   *resolver.TypeRegistry
	CurrentPkg string // current AIDL package (e.g., "android.hardware.audio.common")
	CurrentDef string // fully qualified name of the definition being generated (e.g., "android.hardware.bluetooth.audio.CodecId")
	GoFile     *GoFile
	// AliasMap caches the Go import alias assigned per AIDL package to avoid
	// collisions when two different packages share the same last segment.
	AliasMap map[string]string
	// UsedAliases tracks aliases already assigned to detect collisions.
	UsedAliases map[string]bool
	// ReservedNames holds identifiers that must not be used as import aliases
	// because they appear as parameter names in method signatures. An alias
	// matching a parameter would shadow the import within that method body.
	ReservedNames map[string]bool
	// ImportGraph is used to detect import cycles. When set, cross-package
	// type references that would create cycles are replaced with any.
	ImportGraph *ImportGraph
	// CycleBreaks tracks qualified names that were resolved to any
	// due to import cycles.
	CycleBreaks map[string]bool
	// CycleTypeCallback is called when a type is redirected to a "types"
	// sub-package to break an import cycle.
	CycleTypeCallback func(qualifiedName, typesPkg string)
	// ResolvedTypes caches the Go type string for each AIDL type name to
	// ensure consistent resolution across multiple calls (avoiding
	// non-determinism from map iteration in the type registry).
	ResolvedTypes map[string]string
}

TypeRefResolver resolves AIDL type references to qualified Go type strings, adding import statements to the GoFile when a type is from a different package.

func NewTypeRefResolver

func NewTypeRefResolver(
	registry *resolver.TypeRegistry,
	currentPkg string,
	goFile *GoFile,
) *TypeRefResolver

NewTypeRefResolver creates a resolver for type references in the given AIDL package.

func (*TypeRefResolver) GoTypeRef

func (r *TypeRefResolver) GoTypeRef(ts *parser.TypeSpecifier) string

GoTypeRef converts an AIDL TypeSpecifier to a Go type string, qualifying cross-package references and adding imports to the GoFile as needed.

func (*TypeRefResolver) IsCycleBroken

func (r *TypeRefResolver) IsCycleBroken(aidlName string) bool

IsCycleBroken returns true if the given AIDL type name was resolved to any due to an import cycle.

func (*TypeRefResolver) ReserveNames

func (r *TypeRefResolver) ReserveNames(names []string)

ReserveNames marks identifiers that must not be used as import aliases. Call this before resolving any types to prevent aliases from colliding with method parameter names in the generated file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL