@@ -117,6 +117,19 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
117
117
return pkg , nil
118
118
}
119
119
120
+ // overrideInfo is used by parseAndAugment methods to manage
121
+ // directives and how the overlay and original are merged.
122
+ type overrideInfo struct {
123
+ // KeepOriginal indicates that the original code should be kept
124
+ // but the identifier will be prefixed by `_gopherjs_original_foo`.
125
+ // If false the original code is removed.
126
+ keepOriginal bool
127
+
128
+ // pruneMethodBody indicates that the body of the methods should be
129
+ // removed because they contain something that is invalid to GopherJS.
130
+ pruneMethodBody bool
131
+ }
132
+
120
133
// parseAndAugment parses and returns all .go files of given pkg.
121
134
// Standard Go library packages are augmented with files in compiler/natives folder.
122
135
// If isTest is true and pkg.ImportPath has no _test suffix, package is built for running internal tests.
@@ -132,84 +145,86 @@ func ImportDir(dir string, mode build.ImportMode, installSuffix string, buildTag
132
145
// the original identifier gets replaced by `_`. New identifiers that don't exist in original
133
146
// package get added.
134
147
func parseAndAugment (xctx XContext , pkg * PackageData , isTest bool , fileSet * token.FileSet ) ([]* ast.File , []JSFile , error ) {
135
- var files []* ast.File
148
+ jsFiles , overlayFiles := parseOverlayFiles (xctx , pkg , isTest , fileSet )
149
+
150
+ originalFiles , err := parserOriginalFiles (pkg , fileSet )
151
+ if err != nil {
152
+ return nil , nil , err
153
+ }
154
+
155
+ overrides := make (map [string ]overrideInfo )
156
+ for _ , file := range overlayFiles {
157
+ augmentOverlayFile (file , overrides )
158
+ }
159
+ delete (overrides , "init" )
136
160
137
- type overrideInfo struct {
138
- keepOriginal bool
139
- pruneOriginal bool
161
+ for _ , file := range originalFiles {
162
+ augmentOriginalImports ( pkg . ImportPath , file )
163
+ augmentOriginalFile ( file , overrides )
140
164
}
141
- replacedDeclNames := make (map [string ]overrideInfo )
142
165
166
+ return append (overlayFiles , originalFiles ... ), jsFiles , nil
167
+ }
168
+
169
+ // parseOverlayFiles loads and parses overlay files
170
+ // to augment the original files with.
171
+ func parseOverlayFiles (xctx XContext , pkg * PackageData , isTest bool , fileSet * token.FileSet ) ([]JSFile , []* ast.File ) {
143
172
isXTest := strings .HasSuffix (pkg .ImportPath , "_test" )
144
173
importPath := pkg .ImportPath
145
174
if isXTest {
146
175
importPath = importPath [:len (importPath )- 5 ]
147
176
}
148
177
149
- jsFiles := []JSFile {}
150
-
151
178
nativesContext := overlayCtx (xctx .Env ())
179
+ nativesPkg , err := nativesContext .Import (importPath , "" , 0 )
180
+ if err != nil {
181
+ return nil , nil
182
+ }
152
183
153
- if nativesPkg , err := nativesContext .Import (importPath , "" , 0 ); err == nil {
154
- jsFiles = nativesPkg .JSFiles
155
- names := nativesPkg .GoFiles
156
- if isTest {
157
- names = append (names , nativesPkg .TestGoFiles ... )
158
- }
159
- if isXTest {
160
- names = nativesPkg .XTestGoFiles
184
+ jsFiles := nativesPkg .JSFiles
185
+ var files []* ast.File
186
+ names := nativesPkg .GoFiles
187
+ if isTest {
188
+ names = append (names , nativesPkg .TestGoFiles ... )
189
+ }
190
+ if isXTest {
191
+ names = nativesPkg .XTestGoFiles
192
+ }
193
+
194
+ for _ , name := range names {
195
+ fullPath := path .Join (nativesPkg .Dir , name )
196
+ r , err := nativesContext .bctx .OpenFile (fullPath )
197
+ if err != nil {
198
+ panic (err )
161
199
}
162
- for _ , name := range names {
163
- fullPath := path .Join (nativesPkg .Dir , name )
164
- r , err := nativesContext .bctx .OpenFile (fullPath )
165
- if err != nil {
166
- panic (err )
167
- }
168
- // Files should be uniquely named and in the original package directory in order to be
169
- // ordered correctly
170
- newPath := path .Join (pkg .Dir , "gopherjs__" + name )
171
- file , err := parser .ParseFile (fileSet , newPath , r , parser .ParseComments )
172
- if err != nil {
173
- panic (err )
174
- }
175
- r .Close ()
176
- for _ , decl := range file .Decls {
177
- switch d := decl .(type ) {
178
- case * ast.FuncDecl :
179
- k := astutil .FuncKey (d )
180
- replacedDeclNames [k ] = overrideInfo {
181
- keepOriginal : astutil .KeepOriginal (d ),
182
- pruneOriginal : astutil .PruneOriginal (d ),
183
- }
184
- case * ast.GenDecl :
185
- switch d .Tok {
186
- case token .TYPE :
187
- for _ , spec := range d .Specs {
188
- replacedDeclNames [spec .(* ast.TypeSpec ).Name .Name ] = overrideInfo {}
189
- }
190
- case token .VAR , token .CONST :
191
- for _ , spec := range d .Specs {
192
- for _ , name := range spec .(* ast.ValueSpec ).Names {
193
- replacedDeclNames [name .Name ] = overrideInfo {}
194
- }
195
- }
196
- }
197
- }
198
- }
199
- files = append (files , file )
200
+ // Files should be uniquely named and in the original package directory in order to be
201
+ // ordered correctly
202
+ newPath := path .Join (pkg .Dir , "gopherjs__" + name )
203
+ file , err := parser .ParseFile (fileSet , newPath , r , parser .ParseComments )
204
+ if err != nil {
205
+ panic (err )
200
206
}
207
+ r .Close ()
208
+
209
+ files = append (files , file )
201
210
}
202
- delete (replacedDeclNames , "init" )
211
+ return jsFiles , files
212
+ }
203
213
214
+ // parserOriginalFiles loads and parses the original files to augment.
215
+ func parserOriginalFiles (pkg * PackageData , fileSet * token.FileSet ) ([]* ast.File , error ) {
216
+ var files []* ast.File
204
217
var errList compiler.ErrorList
205
218
for _ , name := range pkg .GoFiles {
206
219
if ! filepath .IsAbs (name ) { // name might be absolute if specified directly. E.g., `gopherjs build /abs/file.go`.
207
220
name = filepath .Join (pkg .Dir , name )
208
221
}
222
+
209
223
r , err := buildutil .OpenFile (pkg .bctx , name )
210
224
if err != nil {
211
- return nil , nil , err
225
+ return nil , err
212
226
}
227
+
213
228
file , err := parser .ParseFile (fileSet , name , r , parser .ParseComments )
214
229
r .Close ()
215
230
if err != nil {
@@ -226,68 +241,102 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
226
241
continue
227
242
}
228
243
229
- switch pkg .ImportPath {
230
- case "crypto/rand" , "encoding/gob" , "encoding/json" , "expvar" , "go/token" , "log" , "math/big" , "math/rand" , "regexp" , "time" :
231
- for _ , spec := range file .Imports {
232
- path , _ := strconv .Unquote (spec .Path .Value )
233
- if path == "sync" {
234
- if spec .Name == nil {
235
- spec .Name = ast .NewIdent ("sync" )
244
+ files = append (files , file )
245
+ }
246
+
247
+ if errList != nil {
248
+ return nil , errList
249
+ }
250
+ return files , nil
251
+ }
252
+
253
+ // augmentOverlayFile is the part of parseAndAugment that processes
254
+ // an overlay file AST to collect information such as compiler directives
255
+ // and perform any initial augmentation needed to the overlay.
256
+ func augmentOverlayFile (file * ast.File , overrides map [string ]overrideInfo ) {
257
+ for _ , decl := range file .Decls {
258
+ switch d := decl .(type ) {
259
+ case * ast.FuncDecl :
260
+ k := astutil .FuncKey (d )
261
+ overrides [k ] = overrideInfo {
262
+ keepOriginal : astutil .KeepOriginal (d ),
263
+ pruneMethodBody : astutil .PruneOriginal (d ),
264
+ }
265
+ case * ast.GenDecl :
266
+ for _ , spec := range d .Specs {
267
+ switch s := spec .(type ) {
268
+ case * ast.TypeSpec :
269
+ overrides [s .Name .Name ] = overrideInfo {}
270
+ case * ast.ValueSpec :
271
+ for _ , name := range s .Names {
272
+ overrides [name .Name ] = overrideInfo {}
236
273
}
237
- spec .Path .Value = `"github.com/gopherjs/gopherjs/nosync"`
238
274
}
239
275
}
240
276
}
277
+ }
278
+ }
241
279
242
- for _ , decl := range file .Decls {
243
- switch d := decl .(type ) {
244
- case * ast.FuncDecl :
245
- k := astutil .FuncKey (d )
246
- if info , ok := replacedDeclNames [k ]; ok {
247
- if info .pruneOriginal {
248
- // Prune function bodies, since it may contain code invalid for
249
- // GopherJS and pin unwanted imports.
250
- d .Body = nil
251
- }
252
- if info .keepOriginal {
253
- // Allow overridden function calls
254
- // The standard library implementation of foo() becomes _gopherjs_original_foo()
255
- d .Name .Name = "_gopherjs_original_" + d .Name .Name
256
- } else {
257
- d .Name = ast .NewIdent ("_" )
258
- }
280
+ // augmentOriginalImports is the part of parseAndAugment that processes
281
+ // an original file AST to modify the imports for that file.
282
+ func augmentOriginalImports (importPath string , file * ast.File ) {
283
+ switch importPath {
284
+ case "crypto/rand" , "encoding/gob" , "encoding/json" , "expvar" , "go/token" , "log" , "math/big" , "math/rand" , "regexp" , "time" :
285
+ for _ , spec := range file .Imports {
286
+ path , _ := strconv .Unquote (spec .Path .Value )
287
+ if path == "sync" {
288
+ if spec .Name == nil {
289
+ spec .Name = ast .NewIdent ("sync" )
259
290
}
260
- case * ast.GenDecl :
261
- switch d .Tok {
262
- case token .TYPE :
263
- for _ , spec := range d .Specs {
264
- s := spec .(* ast.TypeSpec )
265
- if _ , ok := replacedDeclNames [s .Name .Name ]; ok {
266
- s .Name = ast .NewIdent ("_" )
267
- s .Type = & ast.StructType {Struct : s .Pos (), Fields : & ast.FieldList {}}
268
- s .TypeParams = nil
269
- }
291
+ spec .Path .Value = `"github.com/gopherjs/gopherjs/nosync"`
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ // augmentOriginalFile is the part of parseAndAugment that processes an
298
+ // original file AST to augment the source code using the overrides from
299
+ // the overlay files.
300
+ func augmentOriginalFile (file * ast.File , overrides map [string ]overrideInfo ) {
301
+ for _ , decl := range file .Decls {
302
+ switch d := decl .(type ) {
303
+ case * ast.FuncDecl :
304
+ if info , ok := overrides [astutil .FuncKey (d )]; ok {
305
+ if info .pruneMethodBody {
306
+ // Prune function bodies, since it may contain code invalid for
307
+ // GopherJS and pin unwanted imports.
308
+ d .Body = nil
309
+ }
310
+ if info .keepOriginal {
311
+ // Allow overridden function calls
312
+ // The standard library implementation of foo() becomes _gopherjs_original_foo()
313
+ d .Name .Name = "_gopherjs_original_" + d .Name .Name
314
+ } else {
315
+ // By setting the name to an underscore, the method will
316
+ // not be outputted. Doing this will keep the dependencies the same.
317
+ d .Name = ast .NewIdent ("_" )
318
+ }
319
+ }
320
+ case * ast.GenDecl :
321
+ for _ , spec := range d .Specs {
322
+ switch s := spec .(type ) {
323
+ case * ast.TypeSpec :
324
+ if _ , ok := overrides [s .Name .Name ]; ok {
325
+ s .Name = ast .NewIdent ("_" )
326
+ // Change to struct type with no type body and not type parameters.
327
+ s .Type = & ast.StructType {Struct : s .Pos (), Fields : & ast.FieldList {}}
328
+ s .TypeParams = nil
270
329
}
271
- case token .VAR , token .CONST :
272
- for _ , spec := range d .Specs {
273
- s := spec .(* ast.ValueSpec )
274
- for i , name := range s .Names {
275
- if _ , ok := replacedDeclNames [name .Name ]; ok {
276
- s .Names [i ] = ast .NewIdent ("_" )
277
- }
330
+ case * ast.ValueSpec :
331
+ for i , name := range s .Names {
332
+ if _ , ok := overrides [name .Name ]; ok {
333
+ s .Names [i ] = ast .NewIdent ("_" )
278
334
}
279
335
}
280
336
}
281
337
}
282
338
}
283
-
284
- files = append (files , file )
285
- }
286
-
287
- if errList != nil {
288
- return nil , nil , errList
289
339
}
290
- return files , jsFiles , nil
291
340
}
292
341
293
342
// Options controls build process behavior.
0 commit comments