Skip to content

Commit 3e2519d

Browse files
Fixed the duplicate compiler issue
1 parent 45350ad commit 3e2519d

5 files changed

Lines changed: 449960 additions & 335 deletions

File tree

playground/compile.js

Lines changed: 345648 additions & 208 deletions
Large diffs are not rendered by default.

playground/internal/cmd/compile/internal/compiler.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -164,33 +164,40 @@ func (r *compilerImp) collectAllSources(root *sources.Sources) (map[string]*sour
164164
allSources := map[string]*sources.Sources{
165165
root.ImportPath: root,
166166
}
167+
if err := r.addSourcesToSourcesMap(allSources, root); err != nil {
168+
return nil, err
169+
}
170+
171+
// Ensure `runtime` is added even if it was not explicitly imported.
172+
if err := r.addPathToSourcesMap(allSources, `runtime`); err != nil {
173+
return nil, err
174+
}
175+
return allSources, nil
176+
}
167177

168-
var collectDeps func(srcs *sources.Sources) error
169-
collectDeps = func(srcs *sources.Sources) error {
170-
for _, path := range srcs.UnresolvedImports() {
171-
if _, has := allSources[path]; has {
172-
continue // Already collected.
173-
}
174-
175-
// Run load synchronously to await for the package to be available.
176-
srcs, _, err := r.cache.Load(path)
177-
if err != nil {
178-
// Failed to load an import.
179-
return fmt.Errorf(`failed to load package %q: %w`, path, err)
180-
}
181-
allSources[srcs.ImportPath] = srcs
182-
183-
if err := collectDeps(srcs); err != nil {
184-
return err
185-
}
178+
func (r *compilerImp) addSourcesToSourcesMap(allSources map[string]*sources.Sources, srcs *sources.Sources) error {
179+
for _, path := range srcs.UnresolvedImports() {
180+
if err := r.addPathToSourcesMap(allSources, path); err != nil {
181+
return err
186182
}
187-
return nil
188183
}
189-
if err := collectDeps(root); err != nil {
190-
return nil, err
184+
return nil
185+
}
186+
187+
func (r *compilerImp) addPathToSourcesMap(allSources map[string]*sources.Sources, path string) error {
188+
if _, has := allSources[path]; has {
189+
return nil // Already collected.
191190
}
192191

193-
return allSources, nil
192+
// Run load synchronously to await for the package to be available.
193+
srcs, _, err := r.cache.Load(path)
194+
if err != nil {
195+
// Failed to load an import.
196+
return fmt.Errorf(`failed to load package %q: %w`, path, err)
197+
}
198+
199+
allSources[srcs.ImportPath] = srcs
200+
return r.addSourcesToSourcesMap(allSources, srcs)
194201
}
195202

196203
func (r *compilerImp) prepareAndCompilePackages(rootPath string, allSources map[string]*sources.Sources) ([]*compiler.Archive, error) {

playground/internal/cmd/compile/internal/packageCache.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,35 @@ func newPackageCache(fetcher Fetcher) *packageCache {
3535
// This will block until the sources are available.
3636
// If the sources are cached, then this will be fast,
3737
// otherwise it will await a network request before returning.
38+
//
39+
// Since GET http requests do not deduplicate requests for the same
40+
// package file and we could get duplicate requests because of diamond
41+
// dependencies (more than one package imports the same package),
42+
// this load will deduplicate requests. Multiple requests to the same
43+
// import path will create only one network request but will block
44+
// all the `Load` calls for that import path until the package has been loaded.
3845
func (pc *packageCache) Load(importPath string) (*sources.Sources, loadResult, error) {
39-
load := pc.getLoadStratagy(importPath)
46+
load := pc.getLoadStrategy(importPath)
4047

4148
srcs := &sources.Sources{}
4249
result, err := load(srcs)
4350
return srcs, result, err
4451
}
4552

46-
// loadStrategy is the stratagy for performing the load based
53+
// loadStrategy is the strategy for performing the load based
4754
// on the current state of any other load of the same package.
4855
//
4956
// If this returns loadFailed, the error will be non-nil.
5057
// and if the error is non-nil, the loadResult will be loadFailed.
5158
type loadStrategy func(srcs *sources.Sources) (loadResult, error)
5259

53-
// getLoadStratagy returns the stratagy to complete loading the package.
54-
// When the returned stratagy is run, it may either return the cached
60+
// getLoadStrategy returns the strategy to complete loading the package.
61+
// When the returned strategy is run, it may either return the cached
5562
// package immediately, wait for an in-progress load to complete,
5663
// or perform the load itself.
5764
//
58-
// getLoadStratagy will not block, but the returned function may block.
59-
func (pc *packageCache) getLoadStratagy(importPath string) loadStrategy {
65+
// getLoadStrategy will not block, but the returned function may block.
66+
func (pc *packageCache) getLoadStrategy(importPath string) loadStrategy {
6067
pc.lock.Lock()
6168
defer pc.lock.Unlock()
6269

@@ -75,7 +82,7 @@ func (pc *packageCache) getLoadStratagy(importPath string) loadStrategy {
7582
return pc.startLoading(importPath, ch)
7683
}
7784

78-
// alreadyLoaded returns a load stratagy that returns the cached sources.
85+
// alreadyLoaded returns a load strategy that returns the cached sources.
7986
// The given cached sources must be the sources that are already loaded in the cache.
8087
func (pc *packageCache) alreadyLoaded(cached *sources.Sources) loadStrategy {
8188
return func(srcs *sources.Sources) (loadResult, error) {
@@ -84,7 +91,7 @@ func (pc *packageCache) alreadyLoaded(cached *sources.Sources) loadStrategy {
8491
}
8592
}
8693

87-
// alreadyInprogress returns a load stratagy that waits for an in-progress load to complete.
94+
// alreadyInprogress returns a load strategy that waits for an in-progress load to complete.
8895
// The given channel will be waited on until it is closed, indicating the load is complete
8996
// and the sources should now be in the cache.
9097
func (pc *packageCache) alreadyInprogress(importPath string, ch chan struct{}) loadStrategy {
@@ -105,26 +112,25 @@ func (pc *packageCache) alreadyInprogress(importPath string, ch chan struct{}) l
105112
}
106113
}
107114

108-
// startLoading returns a load stratagy that performs the package load via a network request.
115+
// startLoading returns a load strategy that performs the package load via a network request.
109116
// This occurs when the package is not cached and not currently in progress.
110117
// The given channel will be closed when the load is complete to indicate to any
111118
// other processed waiting on the load that it is done.
112119
func (pc *packageCache) startLoading(importPath string, ch chan struct{}) loadStrategy {
113120
return func(srcs *sources.Sources) (loadResult, error) {
114121
fetched, err := pc.fetcher.FetchPackage(importPath)
115-
if err != nil {
116-
delete(pc.inprogress, importPath)
117-
close(ch)
118-
return loadFailed, err
119-
}
120122

121123
pc.lock.Lock()
122124
defer pc.lock.Unlock()
125+
defer close(ch)
126+
defer delete(pc.inprogress, importPath)
127+
128+
if err != nil {
129+
return loadFailed, err
130+
}
123131

124132
pc.cached[importPath] = fetched
125133
*srcs = *fetched // Shallow copy the fetched sources into passed in sources.
126-
delete(pc.inprogress, importPath)
127-
close(ch)
128134
return loadFetched, nil
129135
}
130136
}

playground/internal/page/playground.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package page
22

33
import (
44
"go/format"
5+
"sync"
56

67
"github.com/gopherjs/gopherjs.github.io/playground/internal/bindings/react"
78
"github.com/gopherjs/gopherjs.github.io/playground/internal/bindings/url"
@@ -14,15 +15,39 @@ func Playground() *react.Element {
1415
return react.CreateElement(playgroundComponent, nil)
1516
}
1617

18+
// Since React can create Refs twice when in "Strict Mode" but we do not
19+
// want any of these resources created twice so they are singletons.
20+
var (
21+
compiler = OnceValue(workers.NewCompiler)
22+
runner = OnceValue(workers.NewRunner)
23+
snippetStore = OnceValue(snippets.NewStore)
24+
)
25+
26+
// TODO(grantnelson-wf): Remove when we've reached go1.21
27+
func OnceValue[T any](f func() T) func() T {
28+
var (
29+
once sync.Once
30+
result T
31+
)
32+
return func() T {
33+
once.Do(func() {
34+
temp := f
35+
f = nil
36+
result = temp()
37+
})
38+
return result
39+
}
40+
}
41+
1742
func playgroundComponent(props react.Props) *react.Element {
1843
var (
1944
bannerRef = react.UseRefLazy(NoopBannerHandle)
2045
outputRef = react.UseRefLazy(NoopOutput)
2146
codeBoxRef = react.UseRefLazy(NoopCodeBoxHandle)
2247

23-
compilerRef = react.UseRefLazy(workers.NewCompiler)
24-
runnerRef = react.UseRefLazy(workers.NewRunner)
25-
snippetsStoreRef = react.UseRefLazy(snippets.NewStore)
48+
compilerRef = react.UseRefLazy(compiler)
49+
runnerRef = react.UseRefLazy(runner)
50+
snippetsStoreRef = react.UseRefLazy(snippetStore)
2651
)
2752

2853
// Get version number from compiler webworker.

playground/playground.js

Lines changed: 104233 additions & 86 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)