ttsc 0.28.4 → 0.28.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cmd/ttsc/api_compile.go +6 -4
- package/cmd/ttsc/api_transform.go +6 -4
- package/cmd/ttsc/build.go +7 -6
- package/cmd/ttscgraph/serve.go +70 -77
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +26 -0
- package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +1 -2
- package/driver/graph.go +67 -35
- package/driver/input_observation.go +305 -115
- package/driver/input_observation_test.go +203 -0
- package/driver/program.go +28 -4
- package/driver/resolution_candidates.go +247 -741
- package/driver/resolution_candidates_test.go +341 -0
- package/lib/compiler/internal/project/readProjectConfig.js +9 -7
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/runBuild.js +1 -0
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +4 -0
- package/lib/compiler/internal/sharedHostHelpers.js +11 -1
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +1 -0
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +165 -8
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.js +8 -7
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +57 -19
- package/package.json +8 -8
- package/shim/compiler/incremental.go +52 -0
- package/shim/compiler/resolution.go +444 -0
- package/shim/core/shim.go +15 -1
- package/src/compiler/internal/project/readProjectConfig.ts +9 -7
- package/src/compiler/internal/runBuild.ts +2 -0
- package/src/compiler/internal/sharedHostHelpers.ts +14 -0
- package/src/compiler/internal/startResidentTransform.ts +2 -0
- package/src/compiler/internal/transformProjectInMemory.ts +193 -8
- package/src/internal/projectInputPathIdentity.ts +8 -7
- package/src/structures/ITtscCompilerTransformation.ts +65 -19
- package/utility/host.go +8 -7
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"crypto/sha256"
|
|
5
5
|
"encoding/hex"
|
|
6
6
|
"path/filepath"
|
|
7
|
+
"slices"
|
|
7
8
|
"sync"
|
|
8
9
|
|
|
9
10
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
@@ -15,32 +16,62 @@ var observedDirectoryDigest = func() string {
|
|
|
15
16
|
return hex.EncodeToString(digest[:])
|
|
16
17
|
}()
|
|
17
18
|
|
|
18
|
-
type observedInputKind uint8
|
|
19
|
-
|
|
20
19
|
type inputProofFailure string
|
|
21
20
|
|
|
22
21
|
const (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
inputProofAccessibleEntriesChanged inputProofFailure = "accessible-entries-changed"
|
|
23
|
+
inputProofContentChanged inputProofFailure = "content-changed"
|
|
24
|
+
inputProofContentUnavailable inputProofFailure = "content-unavailable"
|
|
25
|
+
inputProofDirectoryExistsChanged inputProofFailure = "directory-exists-changed"
|
|
26
|
+
inputProofFileExistsChanged inputProofFailure = "file-exists-changed"
|
|
27
|
+
inputProofInvalidPath inputProofFailure = "invalid-path"
|
|
28
|
+
inputProofPredicateConflict inputProofFailure = "predicate-conflict"
|
|
29
|
+
inputProofResolutionChanged inputProofFailure = "resolution-changed"
|
|
30
|
+
inputProofRealpathChanged inputProofFailure = "realpath-changed"
|
|
31
|
+
inputProofRealpathUnavailable inputProofFailure = "realpath-unavailable"
|
|
32
|
+
inputProofStatChanged inputProofFailure = "stat-changed"
|
|
33
|
+
inputProofUnobserved inputProofFailure = "unobserved"
|
|
34
|
+
inputProofUnsupportedInputKind inputProofFailure = "unsupported-input-kind"
|
|
31
35
|
)
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
// TransformInputReadObservation is the exact result of one compiler ReadFile
|
|
38
|
+
// predicate. A failed read carries OK=false and no guessed filesystem kind.
|
|
39
|
+
type TransformInputReadObservation struct {
|
|
40
|
+
OK bool `json:"ok"`
|
|
41
|
+
Hash string `json:"hash,omitempty"`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// TransformInputRealpathObservation is the exact result of one compiler
|
|
45
|
+
// Realpath predicate or an identity read already performed beside a successful
|
|
46
|
+
// existence predicate.
|
|
47
|
+
type TransformInputRealpathObservation struct {
|
|
48
|
+
OK bool `json:"ok"`
|
|
49
|
+
Path string `json:"path,omitempty"`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// TransformInputEntriesObservation is the exact result of one compiler
|
|
53
|
+
// GetAccessibleEntries predicate. Both lists retain TypeScript-Go's sorted
|
|
54
|
+
// lexical child names, including followed directory links and junctions.
|
|
55
|
+
type TransformInputEntriesObservation struct {
|
|
56
|
+
Directories []string `json:"directories"`
|
|
57
|
+
Files []string `json:"files"`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// TransformInputObservation preserves independent compiler filesystem
|
|
61
|
+
// predicates for one lexical path. False FileExists and true DirectoryExists
|
|
62
|
+
// are compatible constraints, not a path-kind race.
|
|
63
|
+
type TransformInputObservation struct {
|
|
64
|
+
AccessibleEntries *TransformInputEntriesObservation `json:"accessibleEntries,omitempty"`
|
|
65
|
+
DirectoryExists *bool `json:"directoryExists,omitempty"`
|
|
66
|
+
FileExists *bool `json:"fileExists,omitempty"`
|
|
67
|
+
ReadFile *TransformInputReadObservation `json:"readFile,omitempty"`
|
|
68
|
+
Realpath *TransformInputRealpathObservation `json:"realpath,omitempty"`
|
|
69
|
+
Stat *string `json:"stat,omitempty"`
|
|
70
|
+
}
|
|
38
71
|
|
|
39
72
|
type observedInput struct {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
kind observedInputKind
|
|
43
|
-
realpath *string
|
|
73
|
+
failure inputProofFailure
|
|
74
|
+
proof TransformInputObservation
|
|
44
75
|
}
|
|
45
76
|
|
|
46
77
|
// inputObservationFS records the exact disk state returned through the
|
|
@@ -49,32 +80,32 @@ type observedInput struct {
|
|
|
49
80
|
// of attaching post-compile disk hashes to an earlier result.
|
|
50
81
|
type inputObservationFS struct {
|
|
51
82
|
vfs.FS
|
|
52
|
-
caseSensitive
|
|
53
|
-
mu
|
|
54
|
-
observations
|
|
83
|
+
caseSensitive bool
|
|
84
|
+
mu sync.Mutex
|
|
85
|
+
observations map[string]observedInput
|
|
86
|
+
observationOrder []string
|
|
87
|
+
observationSpellings map[string]string
|
|
55
88
|
}
|
|
56
89
|
|
|
57
90
|
func newInputObservationFS(inner vfs.FS) *inputObservationFS {
|
|
58
91
|
return &inputObservationFS{
|
|
59
|
-
FS:
|
|
60
|
-
caseSensitive:
|
|
61
|
-
observations:
|
|
92
|
+
FS: inner,
|
|
93
|
+
caseSensitive: inner.UseCaseSensitiveFileNames(),
|
|
94
|
+
observations: map[string]observedInput{},
|
|
95
|
+
observationSpellings: map[string]string{},
|
|
62
96
|
}
|
|
63
97
|
}
|
|
64
98
|
|
|
65
99
|
func (fs *inputObservationFS) FileExists(path string) bool {
|
|
66
100
|
exists := fs.FS.FileExists(path)
|
|
101
|
+
proof := TransformInputObservation{FileExists: boolPointer(exists)}
|
|
67
102
|
if exists {
|
|
68
103
|
// Existence participates in resolution, but only ReadFile returns bytes
|
|
69
104
|
// that can influence the resident Program. Do not duplicate every
|
|
70
105
|
// resolver probe with an eager file read.
|
|
71
|
-
fs.
|
|
72
|
-
kind: observedInputFile,
|
|
73
|
-
realpath: fs.currentRealpath(path),
|
|
74
|
-
})
|
|
75
|
-
} else {
|
|
76
|
-
fs.observe(path, observedInput{kind: observedInputMissing})
|
|
106
|
+
proof.Realpath = fs.currentRealpath(path)
|
|
77
107
|
}
|
|
108
|
+
fs.observe(path, observedInput{proof: proof})
|
|
78
109
|
return exists
|
|
79
110
|
}
|
|
80
111
|
|
|
@@ -84,49 +115,66 @@ func (fs *inputObservationFS) ReadFile(path string) (string, bool) {
|
|
|
84
115
|
digest := sha256.Sum256([]byte(contents))
|
|
85
116
|
hash := hex.EncodeToString(digest[:])
|
|
86
117
|
fs.observe(path, observedInput{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
118
|
+
proof: TransformInputObservation{
|
|
119
|
+
ReadFile: &TransformInputReadObservation{OK: true, Hash: hash},
|
|
120
|
+
Realpath: fs.currentRealpath(path),
|
|
121
|
+
},
|
|
90
122
|
})
|
|
91
123
|
} else {
|
|
92
|
-
fs.observe(path, observedInput{
|
|
124
|
+
fs.observe(path, observedInput{
|
|
125
|
+
proof: TransformInputObservation{
|
|
126
|
+
ReadFile: &TransformInputReadObservation{OK: false},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
93
129
|
}
|
|
94
130
|
return contents, ok
|
|
95
131
|
}
|
|
96
132
|
|
|
97
133
|
func (fs *inputObservationFS) DirectoryExists(path string) bool {
|
|
98
134
|
exists := fs.FS.DirectoryExists(path)
|
|
135
|
+
proof := TransformInputObservation{DirectoryExists: boolPointer(exists)}
|
|
99
136
|
if exists {
|
|
100
|
-
fs.
|
|
101
|
-
kind: observedInputDirectory,
|
|
102
|
-
realpath: fs.currentRealpath(path),
|
|
103
|
-
})
|
|
104
|
-
} else {
|
|
105
|
-
fs.observe(path, observedInput{kind: observedInputMissing})
|
|
137
|
+
proof.Realpath = fs.currentRealpath(path)
|
|
106
138
|
}
|
|
139
|
+
fs.observe(path, observedInput{proof: proof})
|
|
107
140
|
return exists
|
|
108
141
|
}
|
|
109
142
|
|
|
110
143
|
func (fs *inputObservationFS) GetAccessibleEntries(path string) vfs.Entries {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
144
|
+
entries := fs.FS.GetAccessibleEntries(path)
|
|
145
|
+
fs.observe(path, observedInput{
|
|
146
|
+
proof: TransformInputObservation{
|
|
147
|
+
AccessibleEntries: &TransformInputEntriesObservation{
|
|
148
|
+
Directories: append([]string{}, entries.Directories...),
|
|
149
|
+
Files: append([]string{}, entries.Files...),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
return entries
|
|
115
154
|
}
|
|
116
155
|
|
|
117
156
|
func (fs *inputObservationFS) Stat(path string) vfs.FileInfo {
|
|
118
157
|
info := fs.FS.Stat(path)
|
|
158
|
+
kind := "missing"
|
|
119
159
|
if info == nil {
|
|
120
|
-
fs.observe(path, observedInput{
|
|
160
|
+
fs.observe(path, observedInput{
|
|
161
|
+
proof: TransformInputObservation{Stat: &kind},
|
|
162
|
+
})
|
|
121
163
|
} else if info.IsDir() {
|
|
164
|
+
kind = "directory"
|
|
122
165
|
fs.observe(path, observedInput{
|
|
123
|
-
|
|
124
|
-
|
|
166
|
+
proof: TransformInputObservation{
|
|
167
|
+
Stat: &kind,
|
|
168
|
+
Realpath: fs.currentRealpath(path),
|
|
169
|
+
},
|
|
125
170
|
})
|
|
126
171
|
} else {
|
|
172
|
+
kind = "file"
|
|
127
173
|
fs.observe(path, observedInput{
|
|
128
|
-
|
|
129
|
-
|
|
174
|
+
proof: TransformInputObservation{
|
|
175
|
+
Stat: &kind,
|
|
176
|
+
Realpath: fs.currentRealpath(path),
|
|
177
|
+
},
|
|
130
178
|
})
|
|
131
179
|
}
|
|
132
180
|
return info
|
|
@@ -134,20 +182,28 @@ func (fs *inputObservationFS) Stat(path string) vfs.FileInfo {
|
|
|
134
182
|
|
|
135
183
|
func (fs *inputObservationFS) Realpath(path string) string {
|
|
136
184
|
realpath := fs.FS.Realpath(path)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
185
|
+
fs.observe(path, observedInput{
|
|
186
|
+
proof: TransformInputObservation{Realpath: realpathObservation(realpath)},
|
|
187
|
+
})
|
|
141
188
|
return realpath
|
|
142
189
|
}
|
|
143
190
|
|
|
144
|
-
func (fs *inputObservationFS) currentRealpath(path string) *
|
|
145
|
-
|
|
191
|
+
func (fs *inputObservationFS) currentRealpath(path string) *TransformInputRealpathObservation {
|
|
192
|
+
return realpathObservation(fs.FS.Realpath(path))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
func realpathObservation(realpath string) *TransformInputRealpathObservation {
|
|
146
196
|
if realpath == "" {
|
|
147
|
-
return
|
|
197
|
+
return &TransformInputRealpathObservation{OK: false}
|
|
198
|
+
}
|
|
199
|
+
return &TransformInputRealpathObservation{
|
|
200
|
+
OK: true,
|
|
201
|
+
Path: filepath.Clean(realpath),
|
|
148
202
|
}
|
|
149
|
-
|
|
150
|
-
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func boolPointer(value bool) *bool {
|
|
206
|
+
return &value
|
|
151
207
|
}
|
|
152
208
|
|
|
153
209
|
func (fs *inputObservationFS) observationKey(path string) string {
|
|
@@ -167,13 +223,17 @@ func (fs *inputObservationFS) observe(path string, next observedInput) {
|
|
|
167
223
|
}
|
|
168
224
|
fs.mu.Lock()
|
|
169
225
|
defer fs.mu.Unlock()
|
|
226
|
+
if _, found := fs.observationSpellings[key]; !found {
|
|
227
|
+
fs.observationSpellings[key] = filepath.Clean(path)
|
|
228
|
+
fs.observationOrder = append(fs.observationOrder, key)
|
|
229
|
+
}
|
|
170
230
|
keys := []string{key}
|
|
171
231
|
// A compiler read can arrive through an 8.3, case-variant, or already-real
|
|
172
232
|
// spelling while resolution recorded the selected lexical alias. Index the
|
|
173
233
|
// returned bytes by the final physical path too, so proof can join the two
|
|
174
234
|
// observations without another disk read.
|
|
175
|
-
if next.
|
|
176
|
-
physicalKey := fs.observationKey(
|
|
235
|
+
if next.proof.ReadFile != nil && next.proof.ReadFile.OK && next.proof.Realpath != nil && next.proof.Realpath.OK {
|
|
236
|
+
physicalKey := fs.observationKey(next.proof.Realpath.Path)
|
|
177
237
|
if physicalKey != "" && physicalKey != key {
|
|
178
238
|
keys = append(keys, physicalKey)
|
|
179
239
|
}
|
|
@@ -197,108 +257,238 @@ func (fs *inputObservationFS) mergeObservation(key string, next observedInput) {
|
|
|
197
257
|
fs.failObservation(key, previous, next.failure)
|
|
198
258
|
return
|
|
199
259
|
}
|
|
200
|
-
if previous.
|
|
201
|
-
fs.failObservation(key, previous,
|
|
260
|
+
if previous.proof.FileExists != nil && next.proof.FileExists != nil && *previous.proof.FileExists != *next.proof.FileExists {
|
|
261
|
+
fs.failObservation(key, previous, inputProofFileExistsChanged)
|
|
202
262
|
return
|
|
203
263
|
}
|
|
204
|
-
if
|
|
205
|
-
fs.failObservation(key, previous,
|
|
264
|
+
if previous.proof.DirectoryExists != nil && next.proof.DirectoryExists != nil && *previous.proof.DirectoryExists != *next.proof.DirectoryExists {
|
|
265
|
+
fs.failObservation(key, previous, inputProofDirectoryExistsChanged)
|
|
206
266
|
return
|
|
207
267
|
}
|
|
208
|
-
if previous.
|
|
268
|
+
if previous.proof.Stat != nil && next.proof.Stat != nil && *previous.proof.Stat != *next.proof.Stat {
|
|
269
|
+
fs.failObservation(key, previous, inputProofStatChanged)
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
if previous.proof.ReadFile != nil && next.proof.ReadFile != nil && !sameReadObservation(previous.proof.ReadFile, next.proof.ReadFile) {
|
|
209
273
|
fs.failObservation(key, previous, inputProofContentChanged)
|
|
210
274
|
return
|
|
211
275
|
}
|
|
212
|
-
if previous.
|
|
213
|
-
|
|
276
|
+
if previous.proof.Realpath != nil && next.proof.Realpath != nil && !sameRealpathObservation(previous.proof.Realpath, next.proof.Realpath) {
|
|
277
|
+
fs.failObservation(key, previous, inputProofRealpathChanged)
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
if previous.proof.AccessibleEntries != nil && next.proof.AccessibleEntries != nil && !sameEntriesObservation(previous.proof.AccessibleEntries, next.proof.AccessibleEntries) {
|
|
281
|
+
fs.failObservation(key, previous, inputProofAccessibleEntriesChanged)
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
if previous.proof.AccessibleEntries == nil {
|
|
285
|
+
previous.proof.AccessibleEntries = next.proof.AccessibleEntries
|
|
286
|
+
}
|
|
287
|
+
if previous.proof.FileExists == nil {
|
|
288
|
+
previous.proof.FileExists = next.proof.FileExists
|
|
289
|
+
}
|
|
290
|
+
if previous.proof.DirectoryExists == nil {
|
|
291
|
+
previous.proof.DirectoryExists = next.proof.DirectoryExists
|
|
292
|
+
}
|
|
293
|
+
if previous.proof.Stat == nil {
|
|
294
|
+
previous.proof.Stat = next.proof.Stat
|
|
295
|
+
}
|
|
296
|
+
if previous.proof.ReadFile == nil {
|
|
297
|
+
previous.proof.ReadFile = next.proof.ReadFile
|
|
298
|
+
}
|
|
299
|
+
if previous.proof.Realpath == nil {
|
|
300
|
+
previous.proof.Realpath = next.proof.Realpath
|
|
301
|
+
}
|
|
302
|
+
if !transformInputObservationCompatible(previous.proof) {
|
|
303
|
+
fs.failObservation(key, previous, inputProofPredicateConflict)
|
|
304
|
+
return
|
|
214
305
|
}
|
|
215
306
|
fs.observations[key] = previous
|
|
216
307
|
}
|
|
217
308
|
|
|
218
|
-
func (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
309
|
+
func sameEntriesObservation(left, right *TransformInputEntriesObservation) bool {
|
|
310
|
+
return slices.Equal(left.Directories, right.Directories) && slices.Equal(left.Files, right.Files)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// observedPaths returns the exact lexical spellings on which this wrapper
|
|
314
|
+
// observed at least one filesystem predicate, in first-observation order.
|
|
315
|
+
func (fs *inputObservationFS) observedPaths() []string {
|
|
316
|
+
fs.mu.Lock()
|
|
317
|
+
defer fs.mu.Unlock()
|
|
318
|
+
output := make([]string, 0, len(fs.observationOrder))
|
|
319
|
+
for _, key := range fs.observationOrder {
|
|
320
|
+
output = append(output, fs.observationSpellings[key])
|
|
321
|
+
}
|
|
322
|
+
return output
|
|
223
323
|
}
|
|
224
324
|
|
|
225
|
-
func (fs *inputObservationFS)
|
|
325
|
+
func (fs *inputObservationFS) observedAccessibleEntries(path string) bool {
|
|
226
326
|
key := fs.observationKey(path)
|
|
227
327
|
if key == "" {
|
|
228
|
-
return
|
|
328
|
+
return false
|
|
229
329
|
}
|
|
230
330
|
fs.mu.Lock()
|
|
231
331
|
defer fs.mu.Unlock()
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
332
|
+
observation, found := fs.observations[key]
|
|
333
|
+
return found && observation.proof.AccessibleEntries != nil
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// mergeFrom joins a replay transaction into the compiler-time observation
|
|
337
|
+
// set. Any predicate that changed between construction and replay becomes a
|
|
338
|
+
// stable proof failure instead of authorizing output from mixed generations.
|
|
339
|
+
func (fs *inputObservationFS) mergeFrom(replay *inputObservationFS) {
|
|
340
|
+
if replay == nil {
|
|
236
341
|
return
|
|
237
342
|
}
|
|
238
|
-
|
|
239
|
-
|
|
343
|
+
replay.mu.Lock()
|
|
344
|
+
observations := make(map[string]observedInput, len(replay.observations))
|
|
345
|
+
for key, observation := range replay.observations {
|
|
346
|
+
observations[key] = observation
|
|
240
347
|
}
|
|
241
|
-
|
|
242
|
-
|
|
348
|
+
order := append([]string{}, replay.observationOrder...)
|
|
349
|
+
spellings := make(map[string]string, len(replay.observationSpellings))
|
|
350
|
+
for key, spelling := range replay.observationSpellings {
|
|
351
|
+
spellings[key] = spelling
|
|
352
|
+
}
|
|
353
|
+
replay.mu.Unlock()
|
|
354
|
+
|
|
355
|
+
fs.mu.Lock()
|
|
356
|
+
defer fs.mu.Unlock()
|
|
357
|
+
for _, key := range order {
|
|
358
|
+
if _, found := fs.observationSpellings[key]; !found {
|
|
359
|
+
fs.observationSpellings[key] = spellings[key]
|
|
360
|
+
fs.observationOrder = append(fs.observationOrder, key)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
for key, observation := range observations {
|
|
364
|
+
fs.mergeObservation(key, observation)
|
|
243
365
|
}
|
|
244
366
|
}
|
|
245
367
|
|
|
246
|
-
func
|
|
247
|
-
|
|
248
|
-
|
|
368
|
+
func (fs *inputObservationFS) failObservation(key string, observed observedInput, failure inputProofFailure) {
|
|
369
|
+
observed.failure = failure
|
|
370
|
+
fs.observations[key] = observed
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
func sameReadObservation(left, right *TransformInputReadObservation) bool {
|
|
374
|
+
return left.OK == right.OK && left.Hash == right.Hash
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func sameRealpathObservation(left, right *TransformInputRealpathObservation) bool {
|
|
378
|
+
return left.OK == right.OK && left.Path == right.Path
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func transformInputObservationCompatible(observation TransformInputObservation) bool {
|
|
382
|
+
hasAccessibleEntries := observation.AccessibleEntries != nil &&
|
|
383
|
+
(len(observation.AccessibleEntries.Directories) != 0 || len(observation.AccessibleEntries.Files) != 0)
|
|
384
|
+
if hasAccessibleEntries &&
|
|
385
|
+
((observation.FileExists != nil && *observation.FileExists) ||
|
|
386
|
+
(observation.DirectoryExists != nil && !*observation.DirectoryExists) ||
|
|
387
|
+
(observation.Stat != nil && *observation.Stat != "directory") ||
|
|
388
|
+
(observation.ReadFile != nil && observation.ReadFile.OK)) {
|
|
389
|
+
return false
|
|
390
|
+
}
|
|
391
|
+
if observation.FileExists != nil && *observation.FileExists && observation.DirectoryExists != nil && *observation.DirectoryExists {
|
|
392
|
+
return false
|
|
249
393
|
}
|
|
250
|
-
|
|
394
|
+
if observation.Stat != nil {
|
|
395
|
+
switch *observation.Stat {
|
|
396
|
+
case "directory":
|
|
397
|
+
if (observation.FileExists != nil && *observation.FileExists) ||
|
|
398
|
+
(observation.DirectoryExists != nil && !*observation.DirectoryExists) {
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
case "file":
|
|
402
|
+
if (observation.FileExists != nil && !*observation.FileExists) ||
|
|
403
|
+
(observation.DirectoryExists != nil && *observation.DirectoryExists) {
|
|
404
|
+
return false
|
|
405
|
+
}
|
|
406
|
+
case "missing":
|
|
407
|
+
if (observation.FileExists != nil && *observation.FileExists) ||
|
|
408
|
+
(observation.DirectoryExists != nil && *observation.DirectoryExists) {
|
|
409
|
+
return false
|
|
410
|
+
}
|
|
411
|
+
default:
|
|
412
|
+
return false
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if observation.ReadFile != nil && observation.ReadFile.OK {
|
|
416
|
+
if (observation.FileExists != nil && !*observation.FileExists) ||
|
|
417
|
+
(observation.DirectoryExists != nil && *observation.DirectoryExists) ||
|
|
418
|
+
(observation.Stat != nil && *observation.Stat != "file") {
|
|
419
|
+
return false
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return true
|
|
251
423
|
}
|
|
252
424
|
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
func (fs *inputObservationFS) proof(path string) (hash, realpath *string, failure inputProofFailure) {
|
|
425
|
+
// predicateProof returns every compatible compiler filesystem constraint for
|
|
426
|
+
// path without collapsing different predicates into a guessed object kind.
|
|
427
|
+
func (fs *inputObservationFS) predicateProof(path string) (TransformInputObservation, inputProofFailure) {
|
|
257
428
|
key := fs.observationKey(path)
|
|
258
429
|
if key == "" {
|
|
259
|
-
return
|
|
430
|
+
return TransformInputObservation{}, inputProofInvalidPath
|
|
260
431
|
}
|
|
261
432
|
fs.mu.Lock()
|
|
262
433
|
observation, found := fs.observations[key]
|
|
263
434
|
// TypeScript-Go can probe a lexical symlink candidate and then read the
|
|
264
435
|
// selected source by its physical filename. Reuse that exact observed read
|
|
265
436
|
// for the alias instead of issuing an eager duplicate read from FileExists.
|
|
266
|
-
if found && observation.failure == "" && observation.
|
|
267
|
-
targetKey := fs.observationKey(
|
|
437
|
+
if found && observation.failure == "" && observation.proof.FileExists != nil && *observation.proof.FileExists && observation.proof.ReadFile == nil && observation.proof.Realpath != nil && observation.proof.Realpath.OK {
|
|
438
|
+
targetKey := fs.observationKey(observation.proof.Realpath.Path)
|
|
268
439
|
target, targetFound := fs.observations[targetKey]
|
|
269
|
-
if targetFound && target.failure == "" && target.
|
|
270
|
-
|
|
271
|
-
observation.
|
|
440
|
+
if targetFound && target.failure == "" && target.proof.ReadFile != nil && target.proof.ReadFile.OK && target.proof.Realpath != nil && sameRealpathObservation(target.proof.Realpath, observation.proof.Realpath) {
|
|
441
|
+
read := *target.proof.ReadFile
|
|
442
|
+
observation.proof.ReadFile = &read
|
|
272
443
|
}
|
|
273
444
|
}
|
|
274
445
|
fs.mu.Unlock()
|
|
275
446
|
if !found {
|
|
276
|
-
return
|
|
447
|
+
return TransformInputObservation{}, inputProofUnobserved
|
|
277
448
|
}
|
|
278
449
|
if observation.failure != "" {
|
|
279
|
-
return
|
|
450
|
+
return TransformInputObservation{}, observation.failure
|
|
280
451
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
452
|
+
return observation.proof, ""
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// proof returns a stable compiler-time state. A nil hash/realpath with an empty
|
|
456
|
+
// failure is an explicit JSON null for a path observed missing; a non-empty
|
|
457
|
+
// failure explains why no complete, internally consistent proof exists.
|
|
458
|
+
func (fs *inputObservationFS) proof(path string) (hash, realpath *string, failure inputProofFailure) {
|
|
459
|
+
observation, failure := fs.predicateProof(path)
|
|
460
|
+
if failure != "" {
|
|
461
|
+
return nil, nil, failure
|
|
462
|
+
}
|
|
463
|
+
if observation.ReadFile != nil && observation.ReadFile.OK {
|
|
464
|
+
if observation.Realpath == nil || !observation.Realpath.OK {
|
|
287
465
|
return nil, nil, inputProofRealpathUnavailable
|
|
288
466
|
}
|
|
289
|
-
|
|
467
|
+
hash := observation.ReadFile.Hash
|
|
468
|
+
realpath := observation.Realpath.Path
|
|
290
469
|
return &hash, &realpath, ""
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
470
|
+
}
|
|
471
|
+
directory := (observation.Stat != nil && *observation.Stat == "directory") ||
|
|
472
|
+
(observation.DirectoryExists != nil && *observation.DirectoryExists)
|
|
473
|
+
if directory {
|
|
474
|
+
hash := observedDirectoryDigest
|
|
475
|
+
if observation.Realpath == nil || !observation.Realpath.OK {
|
|
296
476
|
return nil, nil, inputProofRealpathUnavailable
|
|
297
477
|
}
|
|
298
|
-
|
|
299
|
-
realpath := *observation.realpath
|
|
478
|
+
realpath := observation.Realpath.Path
|
|
300
479
|
return &hash, &realpath, ""
|
|
301
|
-
default:
|
|
302
|
-
return nil, nil, inputProofUnsupportedInputKind
|
|
303
480
|
}
|
|
481
|
+
file := (observation.Stat != nil && *observation.Stat == "file") ||
|
|
482
|
+
(observation.FileExists != nil && *observation.FileExists)
|
|
483
|
+
if file {
|
|
484
|
+
return nil, nil, inputProofContentUnavailable
|
|
485
|
+
}
|
|
486
|
+
missing := (observation.Stat != nil && *observation.Stat == "missing") ||
|
|
487
|
+
(observation.FileExists != nil && !*observation.FileExists) ||
|
|
488
|
+
(observation.DirectoryExists != nil && !*observation.DirectoryExists) ||
|
|
489
|
+
(observation.ReadFile != nil && !observation.ReadFile.OK)
|
|
490
|
+
if missing {
|
|
491
|
+
return nil, nil, ""
|
|
492
|
+
}
|
|
493
|
+
return nil, nil, inputProofUnsupportedInputKind
|
|
304
494
|
}
|