@@ -139,18 +139,16 @@ type overrideInfo struct {
139139}
140140
141141// pkgOverrideInfo is the collection of overrides still needed for a package.
142- //
143- // Even after the overrides are applied to the files of a package and the
144- // overrides map is empty, this will be kept to indicate that a package has
145- // already been augmented so that the additional native information is not
146- // re-applied when there is a file from this package parsed after all the
147- // overrides are empty.
148142type pkgOverrideInfo struct {
149143 // overrides is a map of identifier to overrideInfo to override
150144 // individual named structs, interfaces, functions, and methods.
151- // As identifiers are used, they will be removed from this map.
152145 overrides map [string ]overrideInfo
153146
147+ // overlayFiles are the files from the natives that still haven't been
148+ // appended to a file from the package, typically the first file.
149+ overlayFiles []* ast.File
150+
151+ // jsFiles are the additional JS files that are part of the natives.
154152 jsFiles []JSFile
155153}
156154
@@ -165,40 +163,61 @@ type pkgOverrideInfo struct {
165163//
166164// The first file from a package will have any additional methods and
167165// information from the natives injected into the AST. All files from a package
168- // will be augmented by the overrides. After augmentation of a whole package,
169- // the overrides should be empty, but might not be if the natives contain
170- // overrides for methods that no longer exist.
166+ // will be augmented by the overrides.
171167type Augmentor struct {
172- xctx XContext
173-
174168 // packages is a map of package import path to the package's override.
175169 // This is used to keep track of the overrides for a package and indicate
176170 // that additional files from the natives have already been applied.
177171 packages map [string ]* pkgOverrideInfo
178172}
179173
180- func (aug * Augmentor ) Augment (fileSet * token.FileSet , filename string , src * ast.File ) error {
181- pkgName := src .Name .Name
182- importPath := pkgName // TODO: Determine unique import path for the package.
174+ func (aug * Augmentor ) getPackageOverrides (xctx XContext , pkg * PackageData , fileSet * token.FileSet ) * pkgOverrideInfo {
175+ importPath := pkg .ImportPath
176+ if pkgAug , ok := aug .packages [importPath ]; ok {
177+ return pkgAug
178+ }
183179
184- pkgAug , ok := aug .packages [importPath ]
185- if ! ok {
186- if aug .packages == nil {
187- aug .packages = map [string ]* pkgOverrideInfo {}
188- }
189- jsFiles , overlayFiles := parseOverlayFiles (xctx , pkg , fileSet )
180+ jsFiles , overlayFiles := parseOverlayFiles (xctx , pkg , fileSet )
190181
191- overrides := make (map [string ]overrideInfo )
192- for _ , file := range overlayFiles {
193- augmentOverlayFile (file , overrides )
194- }
195- delete (overrides , `init` )
182+ overrides := make (map [string ]overrideInfo )
183+ for _ , file := range overlayFiles {
184+ augmentOverlayFile (file , overrides )
185+ }
186+ delete (overrides , `init` )
187+
188+ pkgAug := & pkgOverrideInfo {
189+ overrides : overrides ,
190+ overlayFiles : overlayFiles ,
191+ jsFiles : jsFiles ,
192+ }
193+
194+ if aug .packages == nil {
195+ aug .packages = map [string ]* pkgOverrideInfo {}
196+ }
197+ aug .packages [importPath ] = pkgAug
198+ return pkgAug
199+ }
200+
201+ func (aug * Augmentor ) Augment (xctx XContext , pkg * PackageData , fileSet * token.FileSet , file * ast.File ) error {
202+ pkgAug := aug .getPackageOverrides (xctx , pkg , fileSet )
203+
204+ augmentOriginalImports (pkg .ImportPath , file )
196205
197- pkgAug = & pkgOverrideInfo {
198- overrides : overrides ,
199- jsFiles : jsFiles ,
206+ if len (pkgAug .overrides ) > 0 {
207+ augmentOriginalFile (file , pkgAug .overrides )
208+ }
209+
210+ if len (pkgAug .overlayFiles ) > 0 {
211+ // Append the overlay files to the first file of the package.
212+ // This is to ensure that the package is augmented with all the
213+ // additional methods and information from the natives.
214+ err := astutil .ConcatenateFiles (file , pkgAug .overlayFiles ... )
215+ if err != nil {
216+ panic (fmt .Errorf ("failed to concatenate overlay files onto %q: %w" , fileSet .Position (file .Package ).Filename , err ))
200217 }
201- aug .packages [importPath ] = pkgAug
218+ pkgAug .overlayFiles = nil
219+
220+ // TODO: Finish
202221 }
203222
204223 return nil
@@ -384,8 +403,8 @@ func augmentOverlayFile(file *ast.File, overrides map[string]overrideInfo) {
384403 }
385404 }
386405 if anyChange {
387- finalizeRemovals (file )
388- pruneImports (file )
406+ astutil . FinalizeRemovals (file )
407+ astutil . PruneImports (file )
389408 }
390409}
391410
@@ -496,167 +515,9 @@ func augmentOriginalFile(file *ast.File, overrides map[string]overrideInfo) {
496515 }
497516 }
498517 if anyChange {
499- finalizeRemovals (file )
500- pruneImports (file )
501- }
502- }
503-
504- // isOnlyImports determines if this file is empty except for imports.
505- func isOnlyImports (file * ast.File ) bool {
506- for _ , decl := range file .Decls {
507- if gen , ok := decl .(* ast.GenDecl ); ok && gen .Tok == token .IMPORT {
508- continue
509- }
510-
511- // The decl was either a FuncDecl or a non-import GenDecl.
512- return false
518+ astutil .FinalizeRemovals (file )
519+ astutil .PruneImports (file )
513520 }
514- return true
515- }
516-
517- // pruneImports will remove any unused imports from the file.
518- //
519- // This will not remove any dot (`.`) or blank (`_`) imports, unless
520- // there are no declarations or directives meaning that all the imports
521- // should be cleared.
522- // If the removal of code causes an import to be removed, the init's from that
523- // import may not be run anymore. If we still need to run an init for an import
524- // which is no longer used, add it to the overlay as a blank (`_`) import.
525- //
526- // This uses the given name or guesses at the name using the import path,
527- // meaning this doesn't work for packages which have a different package name
528- // from the path, including those paths which are versioned
529- // (e.g. `github.com/foo/bar/v2` where the package name is `bar`)
530- // or if the import is defined using a relative path (e.g. `./..`).
531- // Those cases don't exist in the native for Go, so we should only run
532- // this pruning when we have native overlays, but not for unknown packages.
533- func pruneImports (file * ast.File ) {
534- if isOnlyImports (file ) && ! astutil .HasDirectivePrefix (file , `//go:linkname ` ) {
535- // The file is empty, remove all imports including any `.` or `_` imports.
536- file .Imports = nil
537- file .Decls = nil
538- return
539- }
540-
541- unused := make (map [string ]int , len (file .Imports ))
542- for i , in := range file .Imports {
543- if name := astutil .ImportName (in ); len (name ) > 0 {
544- unused [name ] = i
545- }
546- }
547-
548- // Remove "unused imports" for any import which is used.
549- ast .Inspect (file , func (n ast.Node ) bool {
550- if sel , ok := n .(* ast.SelectorExpr ); ok {
551- if id , ok := sel .X .(* ast.Ident ); ok && id .Obj == nil {
552- delete (unused , id .Name )
553- }
554- }
555- return len (unused ) > 0
556- })
557- if len (unused ) == 0 {
558- return
559- }
560-
561- // Remove "unused imports" for any import used for a directive.
562- directiveImports := map [string ]string {
563- `unsafe` : `//go:linkname ` ,
564- `embed` : `//go:embed ` ,
565- }
566- for name , index := range unused {
567- in := file .Imports [index ]
568- path , _ := strconv .Unquote (in .Path .Value )
569- directivePrefix , hasPath := directiveImports [path ]
570- if hasPath && astutil .HasDirectivePrefix (file , directivePrefix ) {
571- // since the import is otherwise unused set the name to blank.
572- in .Name = ast .NewIdent (`_` )
573- delete (unused , name )
574- }
575- }
576- if len (unused ) == 0 {
577- return
578- }
579-
580- // Remove all unused import specifications
581- isUnusedSpec := map [* ast.ImportSpec ]bool {}
582- for _ , index := range unused {
583- isUnusedSpec [file.Imports [index ]] = true
584- }
585- for _ , decl := range file .Decls {
586- if d , ok := decl .(* ast.GenDecl ); ok {
587- for i , spec := range d .Specs {
588- if other , ok := spec .(* ast.ImportSpec ); ok && isUnusedSpec [other ] {
589- d .Specs [i ] = nil
590- }
591- }
592- }
593- }
594-
595- // Remove the unused import copies in the file
596- for _ , index := range unused {
597- file .Imports [index ] = nil
598- }
599-
600- finalizeRemovals (file )
601- }
602-
603- // finalizeRemovals fully removes any declaration, specification, imports
604- // that have been set to nil. This will also remove any unassociated comment
605- // groups, including the comments from removed code.
606- func finalizeRemovals (file * ast.File ) {
607- fileChanged := false
608- for i , decl := range file .Decls {
609- switch d := decl .(type ) {
610- case nil :
611- fileChanged = true
612- case * ast.GenDecl :
613- declChanged := false
614- for j , spec := range d .Specs {
615- switch s := spec .(type ) {
616- case nil :
617- declChanged = true
618- case * ast.ValueSpec :
619- specChanged := false
620- for _ , name := range s .Names {
621- if name == nil {
622- specChanged = true
623- break
624- }
625- }
626- if specChanged {
627- s .Names = astutil .Squeeze (s .Names )
628- s .Values = astutil .Squeeze (s .Values )
629- if len (s .Names ) == 0 {
630- declChanged = true
631- d .Specs [j ] = nil
632- }
633- }
634- }
635- }
636- if declChanged {
637- d .Specs = astutil .Squeeze (d .Specs )
638- if len (d .Specs ) == 0 {
639- fileChanged = true
640- file .Decls [i ] = nil
641- }
642- }
643- }
644- }
645- if fileChanged {
646- file .Decls = astutil .Squeeze (file .Decls )
647- }
648-
649- file .Imports = astutil .Squeeze (file .Imports )
650-
651- file .Comments = nil // clear this first so ast.Inspect doesn't walk it.
652- remComments := []* ast.CommentGroup {}
653- ast .Inspect (file , func (n ast.Node ) bool {
654- if cg , ok := n .(* ast.CommentGroup ); ok {
655- remComments = append (remComments , cg )
656- }
657- return true
658- })
659- file .Comments = remComments
660521}
661522
662523// Options controls build process behavior.
0 commit comments