Skip to content

Commit 36dc4ed

Browse files
authored
Merge pull request #1100 from gopherjs/import_order
Orders source files before compilation to ensure reproducible output
2 parents ee1fd17 + 05ff22d commit 36dc4ed

File tree

5 files changed

+156
-4
lines changed

5 files changed

+156
-4
lines changed

build/build.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,10 @@ func parseAndAugment(xctx XContext, pkg *PackageData, isTest bool, fileSet *toke
225225
if err != nil {
226226
panic(err)
227227
}
228-
file, err := parser.ParseFile(fileSet, fullPath, r, parser.ParseComments)
228+
// Files should be uniquely named and in the original package directory in order to be
229+
// ordered correctly
230+
newPath := path.Join(pkg.Dir, "gopherjs__"+name)
231+
file, err := parser.ParseFile(fileSet, newPath, r, parser.ParseComments)
229232
if err != nil {
230233
panic(err)
231234
}

compiler/compiler_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package compiler
2+
3+
import (
4+
"bytes"
5+
"go/ast"
6+
"go/build"
7+
"go/parser"
8+
"go/token"
9+
"go/types"
10+
"testing"
11+
12+
"github.com/google/go-cmp/cmp"
13+
"golang.org/x/tools/go/loader"
14+
)
15+
16+
func TestOrder(t *testing.T) {
17+
fileA := `
18+
package foo
19+
20+
var Avar = "a"
21+
22+
type Atype struct{}
23+
24+
func Afunc() int {
25+
var varA = 1
26+
var varB = 2
27+
return varA+varB
28+
}
29+
`
30+
31+
fileB := `
32+
package foo
33+
34+
var Bvar = "b"
35+
36+
type Btype struct{}
37+
38+
func Bfunc() int {
39+
var varA = 1
40+
var varB = 2
41+
return varA+varB
42+
}
43+
`
44+
files := []source{{"fileA.go", []byte(fileA)}, {"fileB.go", []byte(fileB)}}
45+
46+
compare(t, "foo", files, false)
47+
compare(t, "foo", files, true)
48+
}
49+
50+
func compare(t *testing.T, path string, sourceFiles []source, minify bool) {
51+
outputNormal, err := compile(path, sourceFiles, minify)
52+
if err != nil {
53+
t.Fatal(err)
54+
}
55+
56+
// reverse the array
57+
for i, j := 0, len(sourceFiles)-1; i < j; i, j = i+1, j-1 {
58+
sourceFiles[i], sourceFiles[j] = sourceFiles[j], sourceFiles[i]
59+
}
60+
61+
outputReversed, err := compile(path, sourceFiles, minify)
62+
if err != nil {
63+
t.Fatal(err)
64+
}
65+
66+
if diff := cmp.Diff(string(outputNormal), string(outputReversed)); diff != "" {
67+
t.Errorf("files in different order produce different JS:\n%s", diff)
68+
}
69+
}
70+
71+
type source struct {
72+
name string
73+
contents []byte
74+
}
75+
76+
func compile(path string, sourceFiles []source, minify bool) ([]byte, error) {
77+
conf := loader.Config{}
78+
conf.Fset = token.NewFileSet()
79+
conf.ParserMode = parser.ParseComments
80+
81+
context := build.Default // make a copy of build.Default
82+
conf.Build = &context
83+
conf.Build.BuildTags = []string{"js"}
84+
85+
var astFiles []*ast.File
86+
for _, sourceFile := range sourceFiles {
87+
astFile, err := parser.ParseFile(conf.Fset, sourceFile.name, sourceFile.contents, parser.ParseComments)
88+
if err != nil {
89+
return nil, err
90+
}
91+
astFiles = append(astFiles, astFile)
92+
}
93+
conf.CreateFromFiles(path, astFiles...)
94+
prog, err := conf.Load()
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
archiveCache := map[string]*Archive{}
100+
var importContext *ImportContext
101+
importContext = &ImportContext{
102+
Packages: make(map[string]*types.Package),
103+
Import: func(path string) (*Archive, error) {
104+
// find in local cache
105+
if a, ok := archiveCache[path]; ok {
106+
return a, nil
107+
}
108+
109+
pi := prog.Package(path)
110+
importContext.Packages[path] = pi.Pkg
111+
112+
// compile package
113+
a, err := Compile(path, pi.Files, prog.Fset, importContext, minify)
114+
if err != nil {
115+
return nil, err
116+
}
117+
archiveCache[path] = a
118+
return a, nil
119+
},
120+
}
121+
122+
a, err := importContext.Import(path)
123+
if err != nil {
124+
return nil, err
125+
}
126+
b, err := renderPackage(a)
127+
if err != nil {
128+
return nil, err
129+
}
130+
return b, nil
131+
}
132+
133+
func renderPackage(archive *Archive) ([]byte, error) {
134+
selection := make(map[*Decl]struct{})
135+
for _, d := range archive.Declarations {
136+
selection[d] = struct{}{}
137+
}
138+
139+
buf := &bytes.Buffer{}
140+
141+
if err := WritePkgCode(archive, selection, goLinknameSet{}, false, &SourceMapFilter{Writer: buf}); err != nil {
142+
return nil, err
143+
}
144+
145+
return buf.Bytes(), nil
146+
}

compiler/package.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor
135135
// Some other unexpected panic, catch the stack trace and return as an error.
136136
err = bailout(e)
137137
}()
138+
139+
// Files must be in the same order to get reproducible JS
140+
sort.Slice(files, func(i, j int) bool {
141+
return fileSet.File(files[i].Pos()).Name() > fileSet.File(files[j].Pos()).Name()
142+
})
143+
138144
typesInfo := &types.Info{
139145
Types: make(map[ast.Expr]types.TypeAndValue),
140146
Defs: make(map[*ast.Ident]types.Object),

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ require (
1919

2020
require (
2121
github.com/inconshreveable/mousetrap v1.0.0 // indirect
22-
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
2322
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
2423
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
2524
)

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,6 @@ github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxr
209209
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
210210
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
211211
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
212-
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
213-
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
214212
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
215213
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
216214
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=

0 commit comments

Comments
 (0)