Share declaring import assembly

This commit is contained in:
2026-11-09 21:18:16 +00:00
parent 720df98a5a
commit 68de431bb9
4 changed files with 85 additions and 87 deletions

View File

@@ -54,6 +54,8 @@ filesystems. [Native link resolver](../../internal/engine/filesystem_links.go#L2
## Finding 2 — Contextless imports duplicate root contribution assembly ## Finding 2 — Contextless imports duplicate root contribution assembly
**Status** — Complete. Bound and contextless imports share root charging, placement, and sequence-item scope assembly. The full test suite passed.
**Priority P3.** Stage 4 calls for sharing the root contribution assembly **Priority P3.** Stage 4 calls for sharing the root contribution assembly
and accounting. The new contextless path has its own source charging, and accounting. The new contextless path has its own source charging,
mapping placement, wrapper charging, pending sequence-item scope mapping placement, wrapper charging, pending sequence-item scope

View File

@@ -48,36 +48,22 @@ func acquireContextlessInput(input assembledInput, request inputAcquisitionReque
} }
return input, diagnostics return input, diagnostics
} }
key := importKeyForMainSource(parsed.source) accounting, limits := input.accounting, policy.importLimits
info := cloneSourceInfo(*parsed.source) if diagnostic := chargeDeclaringImport(parsed, accounting, limits); diagnostic != nil {
accounting := input.accounting return input, []Diagnostic{*diagnostic}
accounting.sources++
accounting.bytes += parsed.bytes
accounting.nodes += countInputNodes(parsed.document)
limits := policy.importLimits
if accounting.sources > limits.sources || accounting.bytes > limits.bytes || accounting.nodes > limits.nodes {
return input, []Diagnostic{importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate input limit", nil)}
} }
contribution := acquiredContribution{source: info, payload: body, identity: key, role: importRoleMain, scopeOwner: key, processing: processing, sequenceDeclarations: sequences} key := importKeyForMainSource(parsed.source)
var scope acquisitionScope root := acquiredContribution{
if request.input.placementMode == PlacementModeSequenceItem { source: cloneSourceInfo(*parsed.source), payload: body, identity: key,
contribution.document = body role: importRoleMain, scopeOwner: key, processing: processing, sequenceDeclarations: sequences,
local := acquisitionScope{owner: key, contributions: []acquiredContribution{contribution}} }
scope = acquisitionScope{owner: key, destination: destination.clone(), contributions: []acquiredContribution{{source: info, payload: body, identity: key, role: importRoleMain, scopeOwner: key, destination: destination.clone(), processing: processing, sequenceDeclarations: sequences, pendingItem: &local}}} scope, diagnostic := prepareDeclaringImportScope(root, accounting, limits)
if diagnostic := reservePendingItemPlacements(&scope, limits, accounting); diagnostic != nil { if diagnostic != nil {
return input, []Diagnostic{*diagnostic} return input, []Diagnostic{*diagnostic}
} }
} else { scope, diagnostic = finalizeDeclaringImportScope(scope, root, accounting, limits)
_, placed, err := placeDocument(body, destination, body.Location, maxDocumentDepth, maxDocumentNodes) if diagnostic != nil {
if err != nil { return input, []Diagnostic{*diagnostic}
return input, []Diagnostic{sourceDiagnostic(CodeSourceLimitExceeded, info.ID, "configuration exceeds the source depth or node limit after destination placement")}
}
accounting.nodes += destinationWrapperNodeCount(destination)
if accounting.nodes > limits.nodes {
return input, []Diagnostic{importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate node limit", nil)}
}
contribution.document, contribution.destination = placed, destination.clone()
scope = acquisitionScope{owner: key, destination: destination.clone(), contributions: []acquiredContribution{contribution}}
} }
input.document = body input.document = body
input.composition = &composedDocument{acquisition: []acquisitionScope{scope}, importGraph: newImportInclusionGraph(), inputBytes: accounting.bytes, inputNodes: accounting.nodes} input.composition = &composedDocument{acquisition: []acquisitionScope{scope}, importGraph: newImportInclusionGraph(), inputBytes: accounting.bytes, inputNodes: accounting.nodes}

View File

@@ -112,9 +112,6 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
_ = closeAccess() _ = closeAccess()
return nil, sourceInfos(main.source), []Diagnostic{importRepeatedDiagnostic(Location{SourceID: main.source.ID}, previous, importRoute{})} return nil, sourceInfos(main.source), []Diagnostic{importRepeatedDiagnostic(Location{SourceID: main.source.ID}, previous, importRoute{})}
} }
accounting.sources++
accounting.bytes += main.bytes
accounting.nodes += countInputNodes(main.document)
directiveContext := request.input.directiveContext directiveContext := request.input.directiveContext
if directiveContext == 0 { if directiveContext == 0 {
directiveContext = rootDirectiveContextImportsOnly directiveContext = rootDirectiveContextImportsOnly
@@ -125,7 +122,6 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
request.input.directiveContext = directiveContext request.input.directiveContext = directiveContext
state := importTraversal{ state := importTraversal{
access: access, request: request, policy: policy, state: loadState, tier: tier, access: access, request: request, policy: policy, state: loadState, tier: tier,
scope: acquisitionScope{owner: mainNodeKey, destination: destination.clone()},
sources: []SourceInfo{mainInfo}, sources: []SourceInfo{mainInfo},
active: make(map[resourceIdentity]int), active: make(map[resourceIdentity]int),
importGraph: newImportInclusionGraph(), importGraph: newImportInclusionGraph(),
@@ -134,11 +130,10 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
state.itemScope = true state.itemScope = true
state.itemPrefix = destination.clone() state.itemPrefix = destination.clone()
state.itemFallback = destination.clone() state.itemFallback = destination.clone()
state.scope.destination = Path{}
state.sources[0] = cloneSourceInfo(mainInfo) state.sources[0] = cloneSourceInfo(mainInfo)
} }
if diagnostic := state.checkInitialLimits(); diagnostic != nil { if diagnostic := chargeDeclaringImport(main, accounting, limits); diagnostic != nil {
_ = closeAccess() _ = closeAccess()
return nil, cloneSourceInfos(state.sources), []Diagnostic{*diagnostic} return nil, cloneSourceInfos(state.sources), []Diagnostic{*diagnostic}
} }
@@ -164,31 +159,18 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
} else { } else {
body, declarations, diagnostics = splitImportDirectives(main.document) body, declarations, diagnostics = splitImportDirectives(main.document)
} }
root := acquiredContribution{
source: cloneSourceInfo(mainInfo), payload: body, identity: mainNodeKey,
role: importRoleMain, scopeOwner: mainNodeKey, processing: processing,
sequenceDeclarations: sequenceDeclarations,
}
if len(diagnostics) == 0 { if len(diagnostics) == 0 {
if mainIsItem { scope, diagnostic := prepareDeclaringImportScope(root, accounting, limits)
state.scope.contributions = append(state.scope.contributions, acquiredContribution{ if diagnostic != nil {
source: cloneSourceInfo(mainInfo), identity: mainNodeKey, role: importRoleMain, diagnostics = []Diagnostic{*diagnostic}
scopeOwner: mainNodeKey, payload: body, document: body, processing: processing,
sequenceDeclarations: cloneSequenceDeclarations(sequenceDeclarations),
})
diagnostics = state.walkDeclarations(basePath, declarations, 0, Path{}, 0)
} else { } else {
_, placed, err := placeDocument(body, destination, body.Location, maxDocumentDepth, maxDocumentNodes) state.scope = scope
placementNodes := destinationWrapperNodeCount(destination) diagnostics = state.walkDeclarations(basePath, declarations, 0, scope.destination, 0)
if err != nil {
diagnostics = []Diagnostic{sourceDiagnostic(CodeSourceLimitExceeded, main.source.ID, "configuration exceeds the source depth or node limit after destination placement")}
} else if accounting.nodes+placementNodes > limits.nodes {
diagnostics = []Diagnostic{importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate node limit", nil)}
} else {
accounting.nodes += placementNodes
state.scope.contributions = append(state.scope.contributions, acquiredContribution{
source: cloneSourceInfo(mainInfo), payload: body, document: placed,
identity: mainNodeKey, role: importRoleMain, scopeOwner: mainNodeKey,
destination: destination.clone(), processing: processing,
sequenceDeclarations: cloneSequenceDeclarations(sequenceDeclarations),
})
diagnostics = state.walkDeclarations(basePath, declarations, 0, destination, 0)
}
} }
} }
if len(diagnostics) == 0 { if len(diagnostics) == 0 {
@@ -205,19 +187,8 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
if closeErr != nil { if closeErr != nil {
return nil, cloneSourceInfos(state.sources), []Diagnostic{importDiagnostic(CodeImportRead, "configuration root could not be closed", nil)} return nil, cloneSourceInfos(state.sources), []Diagnostic{importDiagnostic(CodeImportRead, "configuration root could not be closed", nil)}
} }
acquisition := state.scope acquisition, diagnostic := finalizeDeclaringImportScope(state.scope, root, accounting, limits)
if mainIsItem { if diagnostic != nil {
localScope := state.scope
acquisition = acquisitionScope{
owner: mainNodeKey, destination: destination.clone(),
contributions: []acquiredContribution{{
source: cloneSourceInfo(mainInfo), payload: body, identity: mainNodeKey,
role: importRoleMain, scopeOwner: mainNodeKey, destination: destination.clone(),
processing: processing, sequenceDeclarations: cloneSequenceDeclarations(sequenceDeclarations), pendingItem: &localScope,
}},
}
}
if diagnostic := reservePendingItemPlacements(&acquisition, limits, accounting); diagnostic != nil {
return nil, cloneSourceInfos(state.sources), []Diagnostic{*diagnostic} return nil, cloneSourceInfos(state.sources), []Diagnostic{*diagnostic}
} }
return &composedDocument{ return &composedDocument{
@@ -226,22 +197,6 @@ func acquireImportedWithAccess(request importAcquisitionRequest, policy *acquisi
}, cloneSourceInfos(state.sources), nil }, cloneSourceInfos(state.sources), nil
} }
func (s *importTraversal) checkInitialLimits() *Diagnostic {
if s.state.accounting.sources > s.policy.importLimits.sources {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the source-count limit", nil)
return &value
}
if s.state.accounting.bytes > s.policy.importLimits.bytes {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate byte limit", nil)
return &value
}
if s.state.accounting.nodes > s.policy.importLimits.nodes {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate node limit", nil)
return &value
}
return nil
}
func prepareImportDeclarations(declarations []importDeclarationSpec, defaults ImportDefaults, parentDestination Path) ([]resolvedImportPolicy, []Diagnostic) { func prepareImportDeclarations(declarations []importDeclarationSpec, defaults ImportDefaults, parentDestination Path) ([]resolvedImportPolicy, []Diagnostic) {
policies := make([]resolvedImportPolicy, len(declarations)) policies := make([]resolvedImportPolicy, len(declarations))
for index, spec := range declarations { for index, spec := range declarations {

View File

@@ -285,3 +285,58 @@ func importedVersionClaim(documents []*document.Node, scope acquisitionScope, co
} }
return nil return nil
} }
// The declaring payload contributes once even when there are no admitted imports.
func chargeDeclaringImport(parsed parsedSource, accounting *importAccounting, limits importLimits) *Diagnostic {
accounting.sources++
accounting.bytes += parsed.bytes
accounting.nodes += countInputNodes(parsed.document)
if accounting.sources > limits.sources {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the source-count limit", nil)
return &value
}
if accounting.bytes > limits.bytes {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate byte limit", nil)
return &value
}
if accounting.nodes > limits.nodes {
value := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate node limit", nil)
return &value
}
return nil
}
func prepareDeclaringImportScope(root acquiredContribution, accounting *importAccounting, limits importLimits) (acquisitionScope, *Diagnostic) {
scope := acquisitionScope{owner: root.identity}
if root.source.placementMode == PlacementModeSequenceItem {
root.document = root.payload
} else {
destination := root.source.Destination
_, placed, err := placeDocument(root.payload, destination, root.payload.Location, maxDocumentDepth, maxDocumentNodes)
if err != nil {
diagnostic := sourceDiagnostic(CodeSourceLimitExceeded, root.source.ID, "configuration exceeds the source depth or node limit after destination placement")
return acquisitionScope{}, &diagnostic
}
placementNodes := destinationWrapperNodeCount(destination)
if accounting.nodes+placementNodes > limits.nodes {
diagnostic := importDiagnostic(CodeImportLimitExceeded, "configuration exceeds the aggregate node limit", nil)
return acquisitionScope{}, &diagnostic
}
accounting.nodes += placementNodes
root.document, root.destination = placed, destination.clone()
scope.destination = destination.clone()
}
scope.contributions = []acquiredContribution{root}
return scope, nil
}
func finalizeDeclaringImportScope(scope acquisitionScope, root acquiredContribution, accounting *importAccounting, limits importLimits) (acquisitionScope, *Diagnostic) {
if root.source.placementMode == PlacementModeSequenceItem {
root.destination = root.source.Destination.clone()
localScope := scope
root.pendingItem = &localScope
scope = acquisitionScope{owner: root.identity, destination: root.destination.clone(), contributions: []acquiredContribution{root}}
}
diagnostic := reservePendingItemPlacements(&scope, limits, accounting)
return scope, diagnostic
}