onec-interactive-runtime-core 0.1.19__py3-none-any.whl
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.
- onec_interactive_runtime_core-0.1.19.dist-info/METADATA +12 -0
- onec_interactive_runtime_core-0.1.19.dist-info/RECORD +87 -0
- onec_interactive_runtime_core-0.1.19.dist-info/WHEEL +4 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/COPYRIGHT +9 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/LICENSE +232 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/THIRD_PARTY_NOTICES.txt +32 -0
- onec_runtime/AGENTS.md +7 -0
- onec_runtime/__init__.py +3 -0
- onec_runtime/artifacts.py +59 -0
- onec_runtime/bootstrap.py +310 -0
- onec_runtime/breakpoint_workspace.py +249 -0
- onec_runtime/bsl/__init__.py +139 -0
- onec_runtime/bsl/diagnostics.py +1046 -0
- onec_runtime/bsl/full_ast_worker_projection.py +522 -0
- onec_runtime/bsl/generated_semantic_parser.py +6322 -0
- onec_runtime/bsl/lexer.py +164 -0
- onec_runtime/bsl/module_catalog.py +420 -0
- onec_runtime/bsl/module_delta.py +1079 -0
- onec_runtime/bsl/module_universe.py +1877 -0
- onec_runtime/bsl/notebook_cells.py +232 -0
- onec_runtime/bsl/notebook_method_globals.py +74 -0
- onec_runtime/bsl/notebook_methods.py +175 -0
- onec_runtime/bsl/parser_artifact_identity.py +119 -0
- onec_runtime/bsl/parser_target.py +388 -0
- onec_runtime/bsl/preprocessor.py +16 -0
- onec_runtime/bsl/semantic_lowering.py +1539 -0
- onec_runtime/bsl/source_maps.py +2411 -0
- onec_runtime/bsl/worker_dependency_resolver.py +289 -0
- onec_runtime/bsl/worker_preprocessor.py +247 -0
- onec_runtime/bsl/worker_projection_model.py +137 -0
- onec_runtime/bsl/worker_reload_source_map.py +1697 -0
- onec_runtime/capture.py +113 -0
- onec_runtime/capture_source.py +453 -0
- onec_runtime/compact_table.py +376 -0
- onec_runtime/compact_table_backend.py +500 -0
- onec_runtime/config.py +198 -0
- onec_runtime/configurator_agent.py +520 -0
- onec_runtime/controller_worker.py +400 -0
- onec_runtime/credentials.py +18 -0
- onec_runtime/epf_container.py +196 -0
- onec_runtime/errors.py +205 -0
- onec_runtime/experiment.py +130 -0
- onec_runtime/extension_bundle.py +944 -0
- onec_runtime/extension_lifecycle.py +402 -0
- onec_runtime/extension_state.py +222 -0
- onec_runtime/fault_injection.py +30 -0
- onec_runtime/kernel.py +179 -0
- onec_runtime/lease.py +123 -0
- onec_runtime/observation.py +287 -0
- onec_runtime/performance_profile.py +245 -0
- onec_runtime/privacy.py +263 -0
- onec_runtime/processes.py +265 -0
- onec_runtime/prototype_runtime.py +2644 -0
- onec_runtime/rdbg/__init__.py +1 -0
- onec_runtime/rdbg/models.py +130 -0
- onec_runtime/rdbg/reconnect.py +59 -0
- onec_runtime/rdbg/session.py +913 -0
- onec_runtime/rdbg/transport.py +138 -0
- onec_runtime/rdbg/xml_codec.py +980 -0
- onec_runtime/recovery.py +106 -0
- onec_runtime/recovery_journal.py +79 -0
- onec_runtime/resources/extension/OnecInteractiveRuntime.cfe +0 -0
- onec_runtime/resources/extension/extension-manifest.json +108 -0
- onec_runtime/runtime_api.py +5272 -0
- onec_runtime/runtime_contracts.py +196 -0
- onec_runtime/server_worker.py +2034 -0
- onec_runtime/session.py +2123 -0
- onec_runtime/startup_diagnostics.py +45 -0
- onec_runtime/stop_routing.py +65 -0
- onec_runtime/supervised_processes.py +232 -0
- onec_runtime/supervised_runtime.py +512 -0
- onec_runtime/supervisor.py +363 -0
- onec_runtime/supervisor_model.py +148 -0
- onec_runtime/supervisor_protocol.py +77 -0
- onec_runtime/table_materialization.py +471 -0
- onec_runtime/table_transfer_backend.py +203 -0
- onec_runtime/table_value.py +311 -0
- onec_runtime/toolchain.py +730 -0
- onec_runtime/value_materialization.py +432 -0
- onec_runtime/value_transfer_backend.py +199 -0
- onec_runtime/worker_breakpoints.py +1431 -0
- onec_runtime/worker_epf.py +313 -0
- onec_runtime/worker_epf_template.py +53 -0
- onec_runtime/worker_stage_protocol.py +516 -0
- onec_runtime/worker_universe.py +3772 -0
- onec_runtime_build/__init__.py +1 -0
- onec_runtime_build/parser_target_development.py +116 -0
|
@@ -0,0 +1,2411 @@
|
|
|
1
|
+
"""Immutable identities and coordinate helpers for BSL source maps."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from bisect import bisect_right
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from hashlib import sha256
|
|
9
|
+
from hmac import compare_digest, new as hmac_new
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
from secrets import token_bytes
|
|
13
|
+
from threading import Lock
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$")
|
|
17
|
+
_TEXT_WORK_OBSERVER = None
|
|
18
|
+
_RETAINED_PROOF_KEY = token_bytes(32)
|
|
19
|
+
_SOURCE_MAP_PROOF_KEY = token_bytes(32)
|
|
20
|
+
_WORKER_TEXT_PROOF_KEY = token_bytes(32)
|
|
21
|
+
_RETAINED_CONSTRUCTION_AUTHORITY = object()
|
|
22
|
+
_TRUSTED_TEXT_PROOF_AUTHORITY = object()
|
|
23
|
+
_NORMALIZED_WORKER_AUTHORITY = object()
|
|
24
|
+
_TRUSTED_TEXT_PROOFS: dict[int, _TrustedTextProof] = {}
|
|
25
|
+
_TRUSTED_TEXT_PROOFS_LOCK = Lock()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _observe_text_work(event: str, width: int) -> None:
|
|
29
|
+
observer = _TEXT_WORK_OBSERVER
|
|
30
|
+
if observer is not None:
|
|
31
|
+
observer(event, width)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _validate_sha256(value: str) -> None:
|
|
35
|
+
if not isinstance(value, str) or _SHA256_RE.fullmatch(value) is None:
|
|
36
|
+
raise ValueError("source_sha256 must be a 64-character hexadecimal SHA-256 string")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _require_int(value: object, name: str) -> None:
|
|
40
|
+
if type(value) is not int:
|
|
41
|
+
raise ValueError(f"{name} must be an integer")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _require_str(value: object, name: str, *, nonempty: bool = False) -> None:
|
|
45
|
+
if type(value) is not str or (nonempty and not value):
|
|
46
|
+
suffix = " and must not be empty" if nonempty else ""
|
|
47
|
+
raise ValueError(f"{name} must be a string{suffix}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def source_sha256(text: str) -> str:
|
|
51
|
+
"""Return the SHA-256 digest of *text* encoded as UTF-8."""
|
|
52
|
+
_require_str(text, "text")
|
|
53
|
+
return sha256(text.encode("utf-8")).hexdigest()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SourceUnitKind(StrEnum):
|
|
57
|
+
NOTEBOOK_CELL = "notebook_cell"
|
|
58
|
+
MODULE = "module"
|
|
59
|
+
TEST_MODULE = "test_module"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class SourceArtifactKind(StrEnum):
|
|
63
|
+
VISIBLE = "visible"
|
|
64
|
+
WORKER_PROJECTION = "worker_projection"
|
|
65
|
+
STATEMENT_PROJECTION = "statement_projection"
|
|
66
|
+
SEMANTIC_LOWERING = "semantic_lowering"
|
|
67
|
+
COLLECTOR_WRAPPER = "collector_wrapper"
|
|
68
|
+
WORKER_MODULE = "worker_module"
|
|
69
|
+
EXECUTED_BSL = "executed_bsl"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True, slots=True, order=True)
|
|
73
|
+
class SourceSpan:
|
|
74
|
+
start: int
|
|
75
|
+
end: int
|
|
76
|
+
|
|
77
|
+
def __post_init__(self) -> None:
|
|
78
|
+
_require_int(self.start, "span start")
|
|
79
|
+
_require_int(self.end, "span end")
|
|
80
|
+
if self.start < 0 or self.end < self.start:
|
|
81
|
+
raise ValueError("span must be a nonnegative half-open interval")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True, slots=True)
|
|
85
|
+
class SourceUnitRef:
|
|
86
|
+
kind: SourceUnitKind
|
|
87
|
+
unit_id: str
|
|
88
|
+
revision: int
|
|
89
|
+
source_sha256: str
|
|
90
|
+
|
|
91
|
+
def __post_init__(self) -> None:
|
|
92
|
+
if type(self.kind) is not SourceUnitKind:
|
|
93
|
+
raise ValueError("kind must be a SourceUnitKind")
|
|
94
|
+
_require_str(self.unit_id, "unit_id", nonempty=True)
|
|
95
|
+
_require_int(self.revision, "revision")
|
|
96
|
+
_validate_sha256(self.source_sha256)
|
|
97
|
+
if self.revision < 0:
|
|
98
|
+
raise ValueError("revision must be nonnegative")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True, slots=True)
|
|
102
|
+
class SourceArtifactRef:
|
|
103
|
+
kind: SourceArtifactKind
|
|
104
|
+
source_sha256: str
|
|
105
|
+
character_length: int
|
|
106
|
+
line_ending_kind: str
|
|
107
|
+
lowering_semantic_version: str | None = None
|
|
108
|
+
wrapper_semantic_version: str | None = None
|
|
109
|
+
mode: str | None = None
|
|
110
|
+
worker_generation: int | None = None
|
|
111
|
+
worker_manifest_sha256: str | None = None
|
|
112
|
+
export_catalog_sha256: str | None = None
|
|
113
|
+
|
|
114
|
+
def __post_init__(self) -> None:
|
|
115
|
+
if type(self.kind) is not SourceArtifactKind:
|
|
116
|
+
raise ValueError("kind must be a SourceArtifactKind")
|
|
117
|
+
_require_int(self.character_length, "character_length")
|
|
118
|
+
_require_str(self.line_ending_kind, "line_ending_kind")
|
|
119
|
+
for name in (
|
|
120
|
+
"lowering_semantic_version",
|
|
121
|
+
"wrapper_semantic_version",
|
|
122
|
+
"mode",
|
|
123
|
+
):
|
|
124
|
+
value = getattr(self, name)
|
|
125
|
+
if value is not None:
|
|
126
|
+
_require_str(value, name)
|
|
127
|
+
if self.worker_generation is not None:
|
|
128
|
+
_require_int(self.worker_generation, "worker_generation")
|
|
129
|
+
_validate_sha256(self.source_sha256)
|
|
130
|
+
if self.character_length < 0:
|
|
131
|
+
raise ValueError("character_length must be nonnegative")
|
|
132
|
+
if self.worker_generation is not None and self.worker_generation < 0:
|
|
133
|
+
raise ValueError("worker_generation must be nonnegative")
|
|
134
|
+
for name in ("worker_manifest_sha256", "export_catalog_sha256"):
|
|
135
|
+
value = getattr(self, name)
|
|
136
|
+
if value is not None:
|
|
137
|
+
_validate_sha256(value)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class MappingRelation(StrEnum):
|
|
141
|
+
EXACT = "exact"
|
|
142
|
+
DERIVED = "derived"
|
|
143
|
+
SYNTHETIC = "synthetic"
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass(frozen=True, slots=True)
|
|
147
|
+
class SourceMapSegment:
|
|
148
|
+
generated: SourceSpan
|
|
149
|
+
origin_ref: SourceUnitRef | SourceArtifactRef | None
|
|
150
|
+
origin: SourceSpan | None
|
|
151
|
+
relation: MappingRelation
|
|
152
|
+
synthetic_region: str | None = None
|
|
153
|
+
anchor_ref: SourceUnitRef | SourceArtifactRef | None = None
|
|
154
|
+
anchor_span: SourceSpan | None = None
|
|
155
|
+
|
|
156
|
+
def __post_init__(self) -> None:
|
|
157
|
+
if not isinstance(self.generated, SourceSpan):
|
|
158
|
+
raise ValueError("generated must be a SourceSpan")
|
|
159
|
+
if type(self.relation) is not MappingRelation:
|
|
160
|
+
raise ValueError("relation must be a MappingRelation")
|
|
161
|
+
if self.origin_ref is not None and not isinstance(
|
|
162
|
+
self.origin_ref, (SourceUnitRef, SourceArtifactRef)
|
|
163
|
+
):
|
|
164
|
+
raise ValueError("origin_ref must be a source reference")
|
|
165
|
+
if self.origin is not None and not isinstance(self.origin, SourceSpan):
|
|
166
|
+
raise ValueError("origin must be a SourceSpan")
|
|
167
|
+
if (self.anchor_ref is None) != (self.anchor_span is None):
|
|
168
|
+
raise ValueError("anchor reference and span must be provided together")
|
|
169
|
+
if self.anchor_ref is not None and not isinstance(
|
|
170
|
+
self.anchor_ref, (SourceUnitRef, SourceArtifactRef)
|
|
171
|
+
):
|
|
172
|
+
raise ValueError("anchor_ref must be a source reference")
|
|
173
|
+
if self.anchor_span is not None and not isinstance(self.anchor_span, SourceSpan):
|
|
174
|
+
raise ValueError("anchor_span must be a SourceSpan")
|
|
175
|
+
if self.synthetic_region is not None:
|
|
176
|
+
_require_str(self.synthetic_region, "synthetic region", nonempty=True)
|
|
177
|
+
|
|
178
|
+
if self.relation is MappingRelation.SYNTHETIC:
|
|
179
|
+
if self.origin_ref is not None or self.origin is not None:
|
|
180
|
+
raise ValueError("synthetic text must not claim an origin coordinate")
|
|
181
|
+
if self.synthetic_region is None:
|
|
182
|
+
raise ValueError("synthetic text requires a named region")
|
|
183
|
+
else:
|
|
184
|
+
if self.origin_ref is None or self.origin is None:
|
|
185
|
+
raise ValueError("non-synthetic text requires an origin coordinate")
|
|
186
|
+
if self.relation is MappingRelation.EXACT and (
|
|
187
|
+
self.generated.end - self.generated.start != self.origin.end - self.origin.start
|
|
188
|
+
):
|
|
189
|
+
raise ValueError("exact mapping must preserve one-to-one span lengths")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass(frozen=True, slots=True)
|
|
193
|
+
class MappedOffset:
|
|
194
|
+
relation: MappingRelation
|
|
195
|
+
unit: SourceUnitRef | None = None
|
|
196
|
+
origin_span: SourceSpan | None = None
|
|
197
|
+
synthetic_region: str | None = None
|
|
198
|
+
anchor_unit: SourceUnitRef | None = None
|
|
199
|
+
anchor_span: SourceSpan | None = None
|
|
200
|
+
|
|
201
|
+
def __post_init__(self) -> None:
|
|
202
|
+
if type(self.relation) is not MappingRelation:
|
|
203
|
+
raise ValueError("relation must be a MappingRelation")
|
|
204
|
+
if self.unit is not None and not isinstance(self.unit, SourceUnitRef):
|
|
205
|
+
raise ValueError("mapped unit must be a SourceUnitRef")
|
|
206
|
+
if self.origin_span is not None and not isinstance(self.origin_span, SourceSpan):
|
|
207
|
+
raise ValueError("mapped origin must be a SourceSpan")
|
|
208
|
+
if self.anchor_unit is not None and not isinstance(self.anchor_unit, SourceUnitRef):
|
|
209
|
+
raise ValueError("mapped anchor unit must be a SourceUnitRef")
|
|
210
|
+
if (self.anchor_unit is None) != (self.anchor_span is None):
|
|
211
|
+
raise ValueError("mapped anchor unit and span must be provided together")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _artifact_manifest(
|
|
215
|
+
value: SourceArtifactRef,
|
|
216
|
+
*,
|
|
217
|
+
observe: bool = True,
|
|
218
|
+
) -> dict[str, object]:
|
|
219
|
+
if observe:
|
|
220
|
+
_observe_text_work("map_manifest_allocation", 1)
|
|
221
|
+
return {
|
|
222
|
+
"kind": value.kind.value,
|
|
223
|
+
"source_sha256": value.source_sha256,
|
|
224
|
+
"character_length": value.character_length,
|
|
225
|
+
"line_ending_kind": value.line_ending_kind,
|
|
226
|
+
"lowering_semantic_version": value.lowering_semantic_version,
|
|
227
|
+
"wrapper_semantic_version": value.wrapper_semantic_version,
|
|
228
|
+
"mode": value.mode,
|
|
229
|
+
"worker_generation": value.worker_generation,
|
|
230
|
+
"worker_manifest_sha256": value.worker_manifest_sha256,
|
|
231
|
+
"export_catalog_sha256": value.export_catalog_sha256,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _reference_manifest(
|
|
236
|
+
value: SourceUnitRef | SourceArtifactRef | None,
|
|
237
|
+
*,
|
|
238
|
+
observe: bool = True,
|
|
239
|
+
) -> dict[str, object] | None:
|
|
240
|
+
if value is None:
|
|
241
|
+
return None
|
|
242
|
+
if isinstance(value, SourceArtifactRef):
|
|
243
|
+
if observe:
|
|
244
|
+
_observe_text_work("map_manifest_allocation", 1)
|
|
245
|
+
return {
|
|
246
|
+
"reference_kind": "artifact",
|
|
247
|
+
**_artifact_manifest(value, observe=observe),
|
|
248
|
+
}
|
|
249
|
+
if observe:
|
|
250
|
+
_observe_text_work("map_manifest_allocation", 1)
|
|
251
|
+
return {
|
|
252
|
+
"reference_kind": "unit",
|
|
253
|
+
"kind": value.kind.value,
|
|
254
|
+
"unit_id": value.unit_id,
|
|
255
|
+
"revision": value.revision,
|
|
256
|
+
"source_sha256": value.source_sha256,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _span_manifest(
|
|
261
|
+
value: SourceSpan | None,
|
|
262
|
+
*,
|
|
263
|
+
observe: bool = True,
|
|
264
|
+
) -> list[int] | None:
|
|
265
|
+
if value is not None and observe:
|
|
266
|
+
_observe_text_work("map_manifest_allocation", 1)
|
|
267
|
+
return None if value is None else [value.start, value.end]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _proof_uint(mac: object, value: int) -> int:
|
|
271
|
+
if type(value) is not int or value < 0:
|
|
272
|
+
raise ValueError("proof integer must be nonnegative")
|
|
273
|
+
payload = value.to_bytes(max(1, (value.bit_length() + 7) // 8), "big")
|
|
274
|
+
length = len(payload).to_bytes(8, "big", signed=False)
|
|
275
|
+
mac.update(length) # type: ignore[attr-defined]
|
|
276
|
+
mac.update(payload) # type: ignore[attr-defined]
|
|
277
|
+
return len(length) + len(payload)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _proof_optional_uint(mac: object, value: int | None) -> int:
|
|
281
|
+
marker = b"\x00" if value is None else b"\x01"
|
|
282
|
+
mac.update(marker) # type: ignore[attr-defined]
|
|
283
|
+
return 1 if value is None else 1 + _proof_uint(mac, value)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _proof_text(mac: object, value: str) -> int:
|
|
287
|
+
payload = value.encode("utf-8")
|
|
288
|
+
width = len(payload).to_bytes(8, "big", signed=False)
|
|
289
|
+
mac.update(width) # type: ignore[attr-defined]
|
|
290
|
+
mac.update(payload) # type: ignore[attr-defined]
|
|
291
|
+
return len(width) + len(payload)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _proof_optional_text(mac: object, value: str | None) -> int:
|
|
295
|
+
marker = b"\x00" if value is None else b"\x01"
|
|
296
|
+
mac.update(marker) # type: ignore[attr-defined]
|
|
297
|
+
return 1 if value is None else 1 + _proof_text(mac, value)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _proof_span(mac: object, value: SourceSpan | None) -> int:
|
|
301
|
+
marker = b"\x00" if value is None else b"\x01"
|
|
302
|
+
mac.update(marker) # type: ignore[attr-defined]
|
|
303
|
+
if value is None:
|
|
304
|
+
return 1
|
|
305
|
+
return 1 + _proof_uint(mac, value.start) + _proof_uint(mac, value.end)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _proof_artifact(mac: object, value: SourceArtifactRef) -> int:
|
|
309
|
+
width = _proof_text(mac, value.kind.value)
|
|
310
|
+
width += _proof_text(mac, value.source_sha256)
|
|
311
|
+
width += _proof_uint(mac, value.character_length)
|
|
312
|
+
width += _proof_text(mac, value.line_ending_kind)
|
|
313
|
+
width += _proof_optional_text(mac, value.lowering_semantic_version)
|
|
314
|
+
width += _proof_optional_text(mac, value.wrapper_semantic_version)
|
|
315
|
+
width += _proof_optional_text(mac, value.mode)
|
|
316
|
+
width += _proof_optional_uint(mac, value.worker_generation)
|
|
317
|
+
width += _proof_optional_text(mac, value.worker_manifest_sha256)
|
|
318
|
+
width += _proof_optional_text(mac, value.export_catalog_sha256)
|
|
319
|
+
return width
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _proof_reference(
|
|
323
|
+
mac: object,
|
|
324
|
+
value: SourceUnitRef | SourceArtifactRef | None,
|
|
325
|
+
) -> int:
|
|
326
|
+
if value is None:
|
|
327
|
+
mac.update(b"\x00") # type: ignore[attr-defined]
|
|
328
|
+
return 1
|
|
329
|
+
if isinstance(value, SourceArtifactRef):
|
|
330
|
+
mac.update(b"\x01") # type: ignore[attr-defined]
|
|
331
|
+
return 1 + _proof_artifact(mac, value)
|
|
332
|
+
mac.update(b"\x02") # type: ignore[attr-defined]
|
|
333
|
+
width = 1 + _proof_text(mac, value.kind.value)
|
|
334
|
+
width += _proof_text(mac, value.unit_id)
|
|
335
|
+
width += _proof_uint(mac, value.revision)
|
|
336
|
+
width += _proof_text(mac, value.source_sha256)
|
|
337
|
+
return width
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _source_map_structural_proof(
|
|
341
|
+
source_map: SourceMap,
|
|
342
|
+
*,
|
|
343
|
+
observe: bool,
|
|
344
|
+
) -> bytes:
|
|
345
|
+
if observe:
|
|
346
|
+
_observe_text_work(
|
|
347
|
+
"semantic_structural_segment_visit",
|
|
348
|
+
len(source_map.segments),
|
|
349
|
+
)
|
|
350
|
+
payload = _canonical_source_map_bytes(source_map, observe=False)
|
|
351
|
+
return _source_map_structural_proof_from_canonical(payload)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _source_map_structural_proof_from_canonical(payload: bytes) -> bytes:
|
|
355
|
+
mac = hmac_new(_SOURCE_MAP_PROOF_KEY, digestmod="sha256")
|
|
356
|
+
_observe_text_work("semantic_structural_hmac_update", 1)
|
|
357
|
+
mac.update(payload)
|
|
358
|
+
return mac.digest()
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _source_map_manifest(
|
|
362
|
+
source_map: SourceMap,
|
|
363
|
+
*,
|
|
364
|
+
observe: bool,
|
|
365
|
+
) -> dict[str, object]:
|
|
366
|
+
return {
|
|
367
|
+
"generated": _artifact_manifest(source_map.generated, observe=observe),
|
|
368
|
+
"segments": [
|
|
369
|
+
{
|
|
370
|
+
"generated": _span_manifest(segment.generated, observe=observe),
|
|
371
|
+
"origin_ref": _reference_manifest(
|
|
372
|
+
segment.origin_ref,
|
|
373
|
+
observe=observe,
|
|
374
|
+
),
|
|
375
|
+
"origin": _span_manifest(segment.origin, observe=observe),
|
|
376
|
+
"relation": segment.relation.value,
|
|
377
|
+
"synthetic_region": segment.synthetic_region,
|
|
378
|
+
"anchor_ref": _reference_manifest(
|
|
379
|
+
segment.anchor_ref,
|
|
380
|
+
observe=observe,
|
|
381
|
+
),
|
|
382
|
+
"anchor_span": _span_manifest(
|
|
383
|
+
segment.anchor_span,
|
|
384
|
+
observe=observe,
|
|
385
|
+
),
|
|
386
|
+
}
|
|
387
|
+
for segment in source_map.segments
|
|
388
|
+
],
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _canonical_source_map_bytes(
|
|
393
|
+
source_map: SourceMap,
|
|
394
|
+
*,
|
|
395
|
+
observe: bool,
|
|
396
|
+
) -> bytes:
|
|
397
|
+
manifest = (
|
|
398
|
+
source_map.to_manifest()
|
|
399
|
+
if observe
|
|
400
|
+
else _source_map_manifest(source_map, observe=False)
|
|
401
|
+
)
|
|
402
|
+
return json.dumps(
|
|
403
|
+
manifest,
|
|
404
|
+
ensure_ascii=False,
|
|
405
|
+
sort_keys=True,
|
|
406
|
+
separators=(",", ":"),
|
|
407
|
+
).encode("utf-8")
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
@dataclass(frozen=True, slots=True)
|
|
411
|
+
class SourceMap:
|
|
412
|
+
generated: SourceArtifactRef
|
|
413
|
+
segments: tuple[SourceMapSegment, ...]
|
|
414
|
+
_canonical_manifest_bytes: bytes = field(init=False, repr=False, compare=False)
|
|
415
|
+
_canonical_sha256: str = field(init=False, repr=False, compare=False)
|
|
416
|
+
_structural_proof: bytes = field(init=False, repr=False, compare=False)
|
|
417
|
+
|
|
418
|
+
def __post_init__(self) -> None:
|
|
419
|
+
if not isinstance(self.generated, SourceArtifactRef):
|
|
420
|
+
raise ValueError("generated must be a SourceArtifactRef")
|
|
421
|
+
if type(self.segments) is not tuple or not self.segments:
|
|
422
|
+
raise ValueError("source map requires segments for full generated coverage")
|
|
423
|
+
|
|
424
|
+
cursor = 0
|
|
425
|
+
previous_start = -1
|
|
426
|
+
for segment in self.segments:
|
|
427
|
+
if not isinstance(segment, SourceMapSegment):
|
|
428
|
+
raise ValueError("segments must contain SourceMapSegment values")
|
|
429
|
+
if segment.generated.end > self.generated.character_length:
|
|
430
|
+
raise ValueError("generated segment is out of bounds")
|
|
431
|
+
if segment.generated.start < previous_start or segment.generated.start < cursor:
|
|
432
|
+
raise ValueError("source map segments must be ordered and non-overlapping")
|
|
433
|
+
if segment.generated.start > cursor:
|
|
434
|
+
raise ValueError("source map must provide full generated coverage")
|
|
435
|
+
previous_start = segment.generated.start
|
|
436
|
+
cursor = max(cursor, segment.generated.end)
|
|
437
|
+
for reference, span, name in (
|
|
438
|
+
(segment.origin_ref, segment.origin, "origin"),
|
|
439
|
+
(segment.anchor_ref, segment.anchor_span, "anchor"),
|
|
440
|
+
):
|
|
441
|
+
if (
|
|
442
|
+
isinstance(reference, SourceArtifactRef)
|
|
443
|
+
and span is not None
|
|
444
|
+
and span.end > reference.character_length
|
|
445
|
+
):
|
|
446
|
+
raise ValueError(f"segment {name} is out of bounds")
|
|
447
|
+
if cursor != self.generated.character_length:
|
|
448
|
+
raise ValueError("source map must provide full generated coverage")
|
|
449
|
+
_observe_text_work("map_manifest_segment_visit", len(self.segments))
|
|
450
|
+
_observe_text_work("map_manifest_allocation", 2 + len(self.segments))
|
|
451
|
+
payload = _canonical_source_map_bytes(self, observe=True)
|
|
452
|
+
_observe_text_work("map_manifest_serialized_bytes", len(payload))
|
|
453
|
+
_observe_text_work("map_manifest_hash", len(payload))
|
|
454
|
+
object.__setattr__(self, "_canonical_manifest_bytes", payload)
|
|
455
|
+
object.__setattr__(self, "_canonical_sha256", sha256(payload).hexdigest())
|
|
456
|
+
object.__setattr__(
|
|
457
|
+
self,
|
|
458
|
+
"_structural_proof",
|
|
459
|
+
_source_map_structural_proof_from_canonical(payload),
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
def map_offset(self, offset: int) -> MappedOffset:
|
|
463
|
+
return map_offset(self, offset)
|
|
464
|
+
|
|
465
|
+
def to_manifest(self) -> dict[str, object]:
|
|
466
|
+
return _source_map_manifest(self, observe=True)
|
|
467
|
+
|
|
468
|
+
@property
|
|
469
|
+
def source_map_sha256(self) -> str:
|
|
470
|
+
return self._canonical_sha256
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def _validate_flattened_map(source_map: SourceMap, *, name: str) -> None:
|
|
474
|
+
if getattr(source_map, "_compact_reload_map", False):
|
|
475
|
+
return
|
|
476
|
+
for segment in source_map.segments:
|
|
477
|
+
if segment.relation is not MappingRelation.SYNTHETIC and not isinstance(
|
|
478
|
+
segment.origin_ref, SourceUnitRef
|
|
479
|
+
):
|
|
480
|
+
raise ValueError(f"{name} must reference visible source units")
|
|
481
|
+
if segment.anchor_ref is not None and not isinstance(
|
|
482
|
+
segment.anchor_ref, SourceUnitRef
|
|
483
|
+
):
|
|
484
|
+
raise ValueError(f"{name} anchors must reference visible source units")
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
488
|
+
class _TrustedTextProof:
|
|
489
|
+
text_object_id: int
|
|
490
|
+
source_sha256: str
|
|
491
|
+
character_length: int
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _mint_trusted_text_proof(
|
|
495
|
+
text: str,
|
|
496
|
+
source_hash: str,
|
|
497
|
+
*,
|
|
498
|
+
authority: object,
|
|
499
|
+
) -> _TrustedTextProof:
|
|
500
|
+
if authority is not _TRUSTED_TEXT_PROOF_AUTHORITY:
|
|
501
|
+
raise ValueError("trusted text proof authority is invalid")
|
|
502
|
+
proof = _TrustedTextProof(id(text), source_hash, len(text))
|
|
503
|
+
with _TRUSTED_TEXT_PROOFS_LOCK:
|
|
504
|
+
_TRUSTED_TEXT_PROOFS[id(proof)] = proof
|
|
505
|
+
return proof
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _consume_trusted_text_proof(
|
|
509
|
+
proof: object,
|
|
510
|
+
text: str,
|
|
511
|
+
artifact: SourceArtifactRef,
|
|
512
|
+
) -> bool:
|
|
513
|
+
if not isinstance(proof, _TrustedTextProof):
|
|
514
|
+
return False
|
|
515
|
+
with _TRUSTED_TEXT_PROOFS_LOCK:
|
|
516
|
+
registered = _TRUSTED_TEXT_PROOFS.pop(id(proof), None)
|
|
517
|
+
return bool(
|
|
518
|
+
registered is proof
|
|
519
|
+
and proof.text_object_id == id(text)
|
|
520
|
+
and proof.source_sha256 == artifact.source_sha256
|
|
521
|
+
and proof.character_length == artifact.character_length
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
526
|
+
class _RetainedTextFragment:
|
|
527
|
+
generated: SourceSpan
|
|
528
|
+
text: str
|
|
529
|
+
retained_source: object | None = None
|
|
530
|
+
retained_span: SourceSpan | None = None
|
|
531
|
+
line_endings: tuple[int, int, int, bool, bool] | None = None
|
|
532
|
+
contains_nul: bool | None = None
|
|
533
|
+
|
|
534
|
+
def __post_init__(self) -> None:
|
|
535
|
+
if (
|
|
536
|
+
not isinstance(self.generated, SourceSpan)
|
|
537
|
+
or type(self.text) is not str
|
|
538
|
+
or len(self.text) != self.generated.end - self.generated.start
|
|
539
|
+
or (self.retained_source is None) != (self.retained_span is None)
|
|
540
|
+
):
|
|
541
|
+
raise ValueError("retained text fragment is invalid")
|
|
542
|
+
if self.line_endings is None:
|
|
543
|
+
object.__setattr__(self, "line_endings", _line_ending_summary(self.text))
|
|
544
|
+
elif self.line_endings != _line_ending_summary_from_counts(
|
|
545
|
+
self.text,
|
|
546
|
+
self.line_endings,
|
|
547
|
+
):
|
|
548
|
+
raise ValueError("retained text fragment line-ending summary is invalid")
|
|
549
|
+
if self.contains_nul is not None and type(self.contains_nul) is not bool:
|
|
550
|
+
raise ValueError("retained text fragment NUL summary is invalid")
|
|
551
|
+
_observe_text_work("fragment_allocate", 1)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _line_ending_summary(text: str) -> tuple[int, int, int, bool, bool]:
|
|
555
|
+
return (
|
|
556
|
+
text.count("\r"),
|
|
557
|
+
text.count("\n"),
|
|
558
|
+
text.count("\r\n"),
|
|
559
|
+
text.startswith("\n"),
|
|
560
|
+
text.endswith("\r"),
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _local_nul_summary(text: str) -> bool:
|
|
565
|
+
_observe_text_work("fragment_nul_scan", len(text))
|
|
566
|
+
return "\x00" in text
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _line_ending_summary_from_counts(
|
|
570
|
+
text: str,
|
|
571
|
+
summary: tuple[int, int, int, bool, bool],
|
|
572
|
+
) -> tuple[int, int, int, bool, bool]:
|
|
573
|
+
if (
|
|
574
|
+
type(summary) is not tuple
|
|
575
|
+
or len(summary) != 5
|
|
576
|
+
or any(type(value) is not int or value < 0 for value in summary[:3])
|
|
577
|
+
or any(type(value) is not bool for value in summary[3:])
|
|
578
|
+
):
|
|
579
|
+
raise ValueError("retained text fragment line-ending summary is invalid")
|
|
580
|
+
# Immutable text identity is bound by the retained proof. Avoid rescanning
|
|
581
|
+
# it here; only constant-time edge properties can be checked independently.
|
|
582
|
+
if summary[3] != text.startswith("\n") or summary[4] != text.endswith("\r"):
|
|
583
|
+
raise ValueError("retained text fragment line-ending summary is invalid")
|
|
584
|
+
return summary
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _retained_state_proof(
|
|
588
|
+
artifact: SourceArtifactRef,
|
|
589
|
+
fragments: tuple[_RetainedTextFragment, ...],
|
|
590
|
+
) -> bytes:
|
|
591
|
+
mac = hmac_new(_RETAINED_PROOF_KEY, digestmod="sha256")
|
|
592
|
+
width = _proof_text(mac, artifact.source_sha256)
|
|
593
|
+
width += _proof_uint(mac, artifact.character_length)
|
|
594
|
+
width += _proof_uint(mac, len(fragments))
|
|
595
|
+
for fragment in fragments:
|
|
596
|
+
_observe_text_work("retained_proof_fragment_visit", 1)
|
|
597
|
+
width += _proof_span(mac, fragment.generated)
|
|
598
|
+
width += _proof_uint(mac, id(fragment.text))
|
|
599
|
+
width += _proof_uint(mac, len(fragment.text))
|
|
600
|
+
width += _proof_optional_uint(
|
|
601
|
+
mac,
|
|
602
|
+
None if fragment.retained_source is None else id(fragment.retained_source),
|
|
603
|
+
)
|
|
604
|
+
width += _proof_span(mac, fragment.retained_span)
|
|
605
|
+
line_endings = fragment.line_endings
|
|
606
|
+
assert line_endings is not None
|
|
607
|
+
width += sum(_proof_uint(mac, value) for value in line_endings[:3])
|
|
608
|
+
for value in line_endings[3:]:
|
|
609
|
+
mac.update(b"\x01" if value else b"\x00")
|
|
610
|
+
width += 1
|
|
611
|
+
mac.update(
|
|
612
|
+
b"\x00"
|
|
613
|
+
if fragment.contains_nul is None
|
|
614
|
+
else b"\x02"
|
|
615
|
+
if fragment.contains_nul
|
|
616
|
+
else b"\x01"
|
|
617
|
+
)
|
|
618
|
+
width += 1
|
|
619
|
+
_observe_text_work("retained_proof_bytes", width)
|
|
620
|
+
return mac.digest()
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def _mapped_text_authority_proof(
|
|
624
|
+
artifact: SourceArtifactRef,
|
|
625
|
+
text: str,
|
|
626
|
+
) -> bytes:
|
|
627
|
+
mac = hmac_new(_RETAINED_PROOF_KEY, digestmod="sha256")
|
|
628
|
+
_proof_text(mac, artifact.source_sha256)
|
|
629
|
+
_proof_uint(mac, artifact.character_length)
|
|
630
|
+
_proof_uint(mac, id(text))
|
|
631
|
+
_proof_uint(mac, len(text))
|
|
632
|
+
return mac.digest()
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
def _validate_retained_fragments(
|
|
636
|
+
artifact: SourceArtifactRef,
|
|
637
|
+
fragments: object,
|
|
638
|
+
proof: object,
|
|
639
|
+
*,
|
|
640
|
+
construction_authority: object | None = None,
|
|
641
|
+
) -> tuple[_RetainedTextFragment, ...]:
|
|
642
|
+
_observe_text_work(
|
|
643
|
+
"retained_metadata",
|
|
644
|
+
len(fragments) if isinstance(fragments, tuple) else 0,
|
|
645
|
+
)
|
|
646
|
+
if (
|
|
647
|
+
type(fragments) is not tuple
|
|
648
|
+
or not fragments
|
|
649
|
+
or any(not isinstance(item, _RetainedTextFragment) for item in fragments)
|
|
650
|
+
or fragments[0].generated.start != 0
|
|
651
|
+
or fragments[-1].generated.end != artifact.character_length
|
|
652
|
+
or any(
|
|
653
|
+
current.generated.end != following.generated.start
|
|
654
|
+
for current, following in zip(fragments, fragments[1:], strict=False)
|
|
655
|
+
)
|
|
656
|
+
or not isinstance(proof, bytes)
|
|
657
|
+
or (
|
|
658
|
+
construction_authority is not _RETAINED_CONSTRUCTION_AUTHORITY
|
|
659
|
+
and not compare_digest(proof, _retained_state_proof(artifact, fragments))
|
|
660
|
+
)
|
|
661
|
+
):
|
|
662
|
+
raise ValueError("retained fragment authority is invalid")
|
|
663
|
+
return fragments
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@dataclass(frozen=True, slots=True, weakref_slot=True, repr=False)
|
|
667
|
+
class _DeferredMappedSource:
|
|
668
|
+
artifact: SourceArtifactRef
|
|
669
|
+
source_map: SourceMap
|
|
670
|
+
lineage: tuple[SourceMap, ...]
|
|
671
|
+
local_source_map: SourceMap
|
|
672
|
+
retained_fragments: tuple[_RetainedTextFragment, ...]
|
|
673
|
+
retained_starts: tuple[int, ...]
|
|
674
|
+
_retained_proof: bytes
|
|
675
|
+
_construction_authority: object = field(repr=False, compare=False)
|
|
676
|
+
|
|
677
|
+
def __post_init__(self) -> None:
|
|
678
|
+
retained = _validate_retained_fragments(
|
|
679
|
+
self.artifact,
|
|
680
|
+
self.retained_fragments,
|
|
681
|
+
self._retained_proof,
|
|
682
|
+
construction_authority=self._construction_authority,
|
|
683
|
+
)
|
|
684
|
+
if self._construction_authority is not _RETAINED_CONSTRUCTION_AUTHORITY:
|
|
685
|
+
raise ValueError("retained fragment construction authority is invalid")
|
|
686
|
+
if self.retained_starts != tuple(
|
|
687
|
+
fragment.generated.start for fragment in retained
|
|
688
|
+
):
|
|
689
|
+
raise ValueError("retained fragment index is invalid")
|
|
690
|
+
|
|
691
|
+
@property
|
|
692
|
+
def character_length(self) -> int:
|
|
693
|
+
return self.artifact.character_length
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def _detached_retained_source(
|
|
697
|
+
source: _DeferredMappedSource,
|
|
698
|
+
) -> _DeferredMappedSource:
|
|
699
|
+
fragments = tuple(
|
|
700
|
+
_RetainedTextFragment(
|
|
701
|
+
fragment.generated,
|
|
702
|
+
fragment.text,
|
|
703
|
+
line_endings=fragment.line_endings,
|
|
704
|
+
contains_nul=fragment.contains_nul,
|
|
705
|
+
)
|
|
706
|
+
for fragment in source.retained_fragments
|
|
707
|
+
)
|
|
708
|
+
return _DeferredMappedSource(
|
|
709
|
+
source.artifact,
|
|
710
|
+
source.source_map,
|
|
711
|
+
source.lineage,
|
|
712
|
+
source.local_source_map,
|
|
713
|
+
fragments,
|
|
714
|
+
tuple(fragment.generated.start for fragment in fragments),
|
|
715
|
+
_retained_state_proof(source.artifact, fragments),
|
|
716
|
+
_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def _validate_retained_source(
|
|
721
|
+
source: MappedSource | _DeferredMappedSource,
|
|
722
|
+
) -> None:
|
|
723
|
+
if isinstance(source, MappedSource) and not compare_digest(
|
|
724
|
+
source._text_authority_proof,
|
|
725
|
+
_mapped_text_authority_proof(source.artifact, source.text),
|
|
726
|
+
):
|
|
727
|
+
raise ValueError("mapped text authority is invalid")
|
|
728
|
+
retained = (
|
|
729
|
+
source._retained_fragments
|
|
730
|
+
if isinstance(source, MappedSource)
|
|
731
|
+
else source.retained_fragments
|
|
732
|
+
)
|
|
733
|
+
starts = (
|
|
734
|
+
source._retained_starts
|
|
735
|
+
if isinstance(source, MappedSource)
|
|
736
|
+
else source.retained_starts
|
|
737
|
+
)
|
|
738
|
+
_validate_retained_fragments(
|
|
739
|
+
source.artifact,
|
|
740
|
+
retained,
|
|
741
|
+
source._retained_proof,
|
|
742
|
+
)
|
|
743
|
+
if starts != tuple(fragment.generated.start for fragment in retained):
|
|
744
|
+
raise ValueError("retained fragment index is invalid")
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def _validated_mapped_text_hash(source: MappedSource) -> str:
|
|
748
|
+
if (
|
|
749
|
+
not isinstance(source, MappedSource)
|
|
750
|
+
or source.artifact.kind is not SourceArtifactKind.WORKER_MODULE
|
|
751
|
+
or not isinstance(source._worker_text_proof, bytes)
|
|
752
|
+
or not compare_digest(
|
|
753
|
+
source._text_authority_proof,
|
|
754
|
+
_mapped_text_authority_proof(source.artifact, source.text),
|
|
755
|
+
)
|
|
756
|
+
or not compare_digest(
|
|
757
|
+
source._worker_text_proof,
|
|
758
|
+
_normalized_worker_text_proof(source.artifact, source.text),
|
|
759
|
+
)
|
|
760
|
+
):
|
|
761
|
+
raise ValueError("mapped text authority is invalid")
|
|
762
|
+
return source.artifact.source_sha256
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def _retained_fragments_for_span(
|
|
766
|
+
source: MappedSource | _DeferredMappedSource,
|
|
767
|
+
span: SourceSpan,
|
|
768
|
+
*,
|
|
769
|
+
scan_local_nul: bool = True,
|
|
770
|
+
) -> tuple[_RetainedTextFragment, ...]:
|
|
771
|
+
length = (
|
|
772
|
+
len(source.text)
|
|
773
|
+
if isinstance(source, MappedSource)
|
|
774
|
+
else source.character_length
|
|
775
|
+
)
|
|
776
|
+
if not isinstance(span, SourceSpan) or span.end > length:
|
|
777
|
+
raise ValueError("retained fragment span must be within the source")
|
|
778
|
+
retained = (
|
|
779
|
+
source._retained_fragments
|
|
780
|
+
if isinstance(source, MappedSource)
|
|
781
|
+
else source.retained_fragments
|
|
782
|
+
)
|
|
783
|
+
starts = (
|
|
784
|
+
source._retained_starts
|
|
785
|
+
if isinstance(source, MappedSource)
|
|
786
|
+
else source.retained_starts
|
|
787
|
+
)
|
|
788
|
+
result: list[_RetainedTextFragment] = []
|
|
789
|
+
cursor = span.start
|
|
790
|
+
index = max(0, bisect_right(starts, span.start) - 1)
|
|
791
|
+
while index < len(retained):
|
|
792
|
+
fragment = retained[index]
|
|
793
|
+
index += 1
|
|
794
|
+
if fragment.generated.start >= span.end:
|
|
795
|
+
break
|
|
796
|
+
start = max(fragment.generated.start, span.start)
|
|
797
|
+
end = min(fragment.generated.end, span.end)
|
|
798
|
+
if start >= end:
|
|
799
|
+
continue
|
|
800
|
+
local_start = start - fragment.generated.start
|
|
801
|
+
local_end = end - fragment.generated.start
|
|
802
|
+
if local_start == 0 and local_end == len(fragment.text):
|
|
803
|
+
text = fragment.text
|
|
804
|
+
line_endings = fragment.line_endings
|
|
805
|
+
contains_nul = fragment.contains_nul
|
|
806
|
+
else:
|
|
807
|
+
_observe_text_work("slice", local_end - local_start)
|
|
808
|
+
text = fragment.text[local_start:local_end]
|
|
809
|
+
line_endings = None
|
|
810
|
+
contains_nul = (
|
|
811
|
+
_local_nul_summary(text)
|
|
812
|
+
if scan_local_nul
|
|
813
|
+
else fragment.contains_nul
|
|
814
|
+
)
|
|
815
|
+
retained_span = (
|
|
816
|
+
SourceSpan(start, end)
|
|
817
|
+
if fragment.retained_source is None
|
|
818
|
+
else SourceSpan(
|
|
819
|
+
fragment.retained_span.start + local_start, # type: ignore[union-attr]
|
|
820
|
+
fragment.retained_span.start + local_end, # type: ignore[union-attr]
|
|
821
|
+
)
|
|
822
|
+
)
|
|
823
|
+
result.append(
|
|
824
|
+
_RetainedTextFragment(
|
|
825
|
+
SourceSpan(cursor, cursor + len(text)),
|
|
826
|
+
text,
|
|
827
|
+
source if fragment.retained_source is None else fragment.retained_source,
|
|
828
|
+
retained_span,
|
|
829
|
+
line_endings,
|
|
830
|
+
contains_nul,
|
|
831
|
+
)
|
|
832
|
+
)
|
|
833
|
+
cursor += len(text)
|
|
834
|
+
if cursor != span.end:
|
|
835
|
+
raise ValueError("retained fragments do not cover the requested span")
|
|
836
|
+
return tuple(result)
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
840
|
+
class _MappedSemanticSummary:
|
|
841
|
+
source_map_sha256: str
|
|
842
|
+
lineage_sha256: str
|
|
843
|
+
source_map_object_id: int
|
|
844
|
+
lineage_object_id: int
|
|
845
|
+
local_source_map_object_id: int | None
|
|
846
|
+
structural_proofs: tuple[tuple[int, bytes], ...]
|
|
847
|
+
authority_proof: bytes
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
def _canonical_lineage_sha256(lineage: tuple[SourceMap, ...]) -> str:
|
|
851
|
+
digest_value = sha256()
|
|
852
|
+
digest_value.update(b"[")
|
|
853
|
+
width = 2
|
|
854
|
+
for index, source_map in enumerate(lineage):
|
|
855
|
+
if index:
|
|
856
|
+
digest_value.update(b",")
|
|
857
|
+
width += 1
|
|
858
|
+
payload = source_map._canonical_manifest_bytes
|
|
859
|
+
digest_value.update(payload)
|
|
860
|
+
width += len(payload)
|
|
861
|
+
_observe_text_work("semantic_lineage_entry_visit", 1)
|
|
862
|
+
digest_value.update(b"]")
|
|
863
|
+
_observe_text_work("semantic_lineage_serialized_bytes", width)
|
|
864
|
+
_observe_text_work("semantic_lineage_hash", width)
|
|
865
|
+
return digest_value.hexdigest()
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
def _mapped_semantic_authority_proof(
|
|
869
|
+
artifact: SourceArtifactRef,
|
|
870
|
+
source_map: SourceMap,
|
|
871
|
+
lineage: tuple[SourceMap, ...],
|
|
872
|
+
local_source_map: SourceMap | None,
|
|
873
|
+
source_map_digest: str,
|
|
874
|
+
lineage_digest: str,
|
|
875
|
+
structural_proofs: tuple[tuple[int, bytes], ...],
|
|
876
|
+
) -> bytes:
|
|
877
|
+
mac = hmac_new(_SOURCE_MAP_PROOF_KEY, digestmod="sha256")
|
|
878
|
+
_proof_artifact(mac, artifact)
|
|
879
|
+
_proof_uint(mac, id(source_map))
|
|
880
|
+
_proof_uint(mac, id(lineage))
|
|
881
|
+
_proof_optional_uint(
|
|
882
|
+
mac,
|
|
883
|
+
None if local_source_map is None else id(local_source_map),
|
|
884
|
+
)
|
|
885
|
+
_proof_text(mac, source_map_digest)
|
|
886
|
+
_proof_text(mac, lineage_digest)
|
|
887
|
+
for object_id, proof in structural_proofs:
|
|
888
|
+
_proof_uint(mac, object_id)
|
|
889
|
+
_proof_uint(mac, len(proof))
|
|
890
|
+
mac.update(proof)
|
|
891
|
+
return mac.digest()
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def _build_mapped_semantic_summary(
|
|
895
|
+
artifact: SourceArtifactRef,
|
|
896
|
+
source_map: SourceMap,
|
|
897
|
+
lineage: tuple[SourceMap, ...],
|
|
898
|
+
local_source_map: SourceMap | None,
|
|
899
|
+
) -> _MappedSemanticSummary:
|
|
900
|
+
maps: list[SourceMap] = []
|
|
901
|
+
for item in (source_map, *lineage, local_source_map):
|
|
902
|
+
if item is not None and all(item is not existing for existing in maps):
|
|
903
|
+
maps.append(item)
|
|
904
|
+
structural_proofs = tuple(
|
|
905
|
+
(id(item), item._structural_proof)
|
|
906
|
+
for item in maps
|
|
907
|
+
)
|
|
908
|
+
source_map_digest = source_map.source_map_sha256
|
|
909
|
+
lineage_digest = _canonical_lineage_sha256(lineage)
|
|
910
|
+
authority_proof = _mapped_semantic_authority_proof(
|
|
911
|
+
artifact,
|
|
912
|
+
source_map,
|
|
913
|
+
lineage,
|
|
914
|
+
local_source_map,
|
|
915
|
+
source_map_digest,
|
|
916
|
+
lineage_digest,
|
|
917
|
+
structural_proofs,
|
|
918
|
+
)
|
|
919
|
+
return _MappedSemanticSummary(
|
|
920
|
+
source_map_digest,
|
|
921
|
+
lineage_digest,
|
|
922
|
+
id(source_map),
|
|
923
|
+
id(lineage),
|
|
924
|
+
None if local_source_map is None else id(local_source_map),
|
|
925
|
+
structural_proofs,
|
|
926
|
+
authority_proof,
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def _normalized_worker_text_proof(
|
|
931
|
+
artifact: SourceArtifactRef,
|
|
932
|
+
text: str,
|
|
933
|
+
) -> bytes:
|
|
934
|
+
mac = hmac_new(_WORKER_TEXT_PROOF_KEY, digestmod="sha256")
|
|
935
|
+
_proof_artifact(mac, artifact)
|
|
936
|
+
_proof_uint(mac, id(text))
|
|
937
|
+
_proof_uint(mac, len(text))
|
|
938
|
+
return mac.digest()
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
class MappedSource:
|
|
942
|
+
"""A private source artifact whose printable state never includes its text."""
|
|
943
|
+
|
|
944
|
+
__slots__ = (
|
|
945
|
+
"__weakref__",
|
|
946
|
+
"_artifact",
|
|
947
|
+
"_lineage",
|
|
948
|
+
"_local_source_map",
|
|
949
|
+
"_semantic_summary",
|
|
950
|
+
"_retained_fragments",
|
|
951
|
+
"_retained_proof",
|
|
952
|
+
"_retained_starts",
|
|
953
|
+
"_source_map",
|
|
954
|
+
"_text",
|
|
955
|
+
"_text_authority_proof",
|
|
956
|
+
"_transform_parent",
|
|
957
|
+
"_worker_text_proof",
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
def __init__(
|
|
961
|
+
self,
|
|
962
|
+
text: str,
|
|
963
|
+
artifact: SourceArtifactRef,
|
|
964
|
+
source_map: SourceMap,
|
|
965
|
+
lineage: tuple[SourceMap, ...] = (),
|
|
966
|
+
local_source_map: SourceMap | None = None,
|
|
967
|
+
*,
|
|
968
|
+
_retained_fragments: tuple[_RetainedTextFragment, ...] | None = None,
|
|
969
|
+
_transform_parent: MappedSource | _DeferredMappedSource | None = None,
|
|
970
|
+
_trusted_source_sha256: str | None = None,
|
|
971
|
+
_trusted_text_proof: _TrustedTextProof | None = None,
|
|
972
|
+
_retained_authority: object | None = None,
|
|
973
|
+
_normalized_worker_authority: object | None = None,
|
|
974
|
+
) -> None:
|
|
975
|
+
_require_str(text, "mapped source text")
|
|
976
|
+
if not isinstance(artifact, SourceArtifactRef):
|
|
977
|
+
raise ValueError("mapped source artifact must be a SourceArtifactRef")
|
|
978
|
+
if _trusted_source_sha256 is not None:
|
|
979
|
+
raise ValueError("trusted source hash requires a builder-owned proof")
|
|
980
|
+
text_is_trusted = _consume_trusted_text_proof(
|
|
981
|
+
_trusted_text_proof,
|
|
982
|
+
text,
|
|
983
|
+
artifact,
|
|
984
|
+
)
|
|
985
|
+
if len(text) != artifact.character_length or (
|
|
986
|
+
not text_is_trusted and source_sha256(text) != artifact.source_sha256
|
|
987
|
+
):
|
|
988
|
+
raise ValueError("mapped source artifact hash or length does not match its text")
|
|
989
|
+
if not isinstance(source_map, SourceMap) or source_map.generated != artifact:
|
|
990
|
+
raise ValueError("mapped source map must identify the exact artifact")
|
|
991
|
+
_validate_flattened_map(source_map, name="flattened source map")
|
|
992
|
+
if type(lineage) is not tuple or any(not isinstance(item, SourceMap) for item in lineage):
|
|
993
|
+
raise ValueError("mapped source lineage must contain source maps")
|
|
994
|
+
if local_source_map is not None and local_source_map.generated != artifact:
|
|
995
|
+
raise ValueError("local source map must identify the exact artifact")
|
|
996
|
+
if local_source_map is not None and (not lineage or lineage[-1] != local_source_map):
|
|
997
|
+
raise ValueError("local source map must be the latest lineage entry")
|
|
998
|
+
if _retained_fragments is not None and (
|
|
999
|
+
_retained_authority is not _RETAINED_CONSTRUCTION_AUTHORITY
|
|
1000
|
+
or not text_is_trusted
|
|
1001
|
+
):
|
|
1002
|
+
raise ValueError("retained fragment construction authority is invalid")
|
|
1003
|
+
retained = (
|
|
1004
|
+
(
|
|
1005
|
+
_RetainedTextFragment(
|
|
1006
|
+
SourceSpan(0, len(text)),
|
|
1007
|
+
text,
|
|
1008
|
+
),
|
|
1009
|
+
)
|
|
1010
|
+
if _retained_fragments is None
|
|
1011
|
+
else _retained_fragments
|
|
1012
|
+
)
|
|
1013
|
+
retained_proof = _retained_state_proof(artifact, retained)
|
|
1014
|
+
_validate_retained_fragments(
|
|
1015
|
+
artifact,
|
|
1016
|
+
retained,
|
|
1017
|
+
retained_proof,
|
|
1018
|
+
construction_authority=_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
1019
|
+
)
|
|
1020
|
+
if _transform_parent is not None and not isinstance(
|
|
1021
|
+
_transform_parent,
|
|
1022
|
+
(MappedSource, _DeferredMappedSource),
|
|
1023
|
+
):
|
|
1024
|
+
raise ValueError("mapped source transform parent is invalid")
|
|
1025
|
+
object.__setattr__(self, "_text", text)
|
|
1026
|
+
object.__setattr__(
|
|
1027
|
+
self,
|
|
1028
|
+
"_text_authority_proof",
|
|
1029
|
+
_mapped_text_authority_proof(artifact, text),
|
|
1030
|
+
)
|
|
1031
|
+
object.__setattr__(self, "_artifact", artifact)
|
|
1032
|
+
object.__setattr__(self, "_source_map", source_map)
|
|
1033
|
+
object.__setattr__(self, "_lineage", lineage)
|
|
1034
|
+
object.__setattr__(self, "_local_source_map", local_source_map)
|
|
1035
|
+
object.__setattr__(
|
|
1036
|
+
self,
|
|
1037
|
+
"_semantic_summary",
|
|
1038
|
+
_build_mapped_semantic_summary(
|
|
1039
|
+
artifact,
|
|
1040
|
+
source_map,
|
|
1041
|
+
lineage,
|
|
1042
|
+
local_source_map,
|
|
1043
|
+
),
|
|
1044
|
+
)
|
|
1045
|
+
object.__setattr__(self, "_retained_fragments", retained)
|
|
1046
|
+
object.__setattr__(self, "_retained_starts", tuple(
|
|
1047
|
+
fragment.generated.start for fragment in retained
|
|
1048
|
+
))
|
|
1049
|
+
object.__setattr__(self, "_retained_proof", retained_proof)
|
|
1050
|
+
object.__setattr__(self, "_transform_parent", _transform_parent)
|
|
1051
|
+
if _normalized_worker_authority is not None and (
|
|
1052
|
+
_normalized_worker_authority is not _NORMALIZED_WORKER_AUTHORITY
|
|
1053
|
+
or artifact.kind is not SourceArtifactKind.WORKER_MODULE
|
|
1054
|
+
):
|
|
1055
|
+
raise ValueError("normalized Worker authority is invalid")
|
|
1056
|
+
object.__setattr__(
|
|
1057
|
+
self,
|
|
1058
|
+
"_worker_text_proof",
|
|
1059
|
+
None
|
|
1060
|
+
if _normalized_worker_authority is None
|
|
1061
|
+
else _normalized_worker_text_proof(artifact, text),
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
1065
|
+
raise AttributeError("MappedSource is immutable")
|
|
1066
|
+
|
|
1067
|
+
def __delattr__(self, name: str) -> None:
|
|
1068
|
+
raise AttributeError("MappedSource is immutable")
|
|
1069
|
+
|
|
1070
|
+
@property
|
|
1071
|
+
def text(self) -> str:
|
|
1072
|
+
return self._text
|
|
1073
|
+
|
|
1074
|
+
@property
|
|
1075
|
+
def artifact(self) -> SourceArtifactRef:
|
|
1076
|
+
return self._artifact
|
|
1077
|
+
|
|
1078
|
+
@property
|
|
1079
|
+
def source_map(self) -> SourceMap:
|
|
1080
|
+
return self._source_map
|
|
1081
|
+
|
|
1082
|
+
@property
|
|
1083
|
+
def lineage(self) -> tuple[SourceMap, ...]:
|
|
1084
|
+
return self._lineage
|
|
1085
|
+
|
|
1086
|
+
@property
|
|
1087
|
+
def local_source_map(self) -> SourceMap:
|
|
1088
|
+
if self._local_source_map is None:
|
|
1089
|
+
raise ValueError("visible or externally assembled source has no local source map")
|
|
1090
|
+
return self._local_source_map
|
|
1091
|
+
|
|
1092
|
+
@property
|
|
1093
|
+
def transform_parent(self) -> MappedSource | _DeferredMappedSource | None:
|
|
1094
|
+
return self._transform_parent
|
|
1095
|
+
|
|
1096
|
+
@property
|
|
1097
|
+
def source_map_sha256(self) -> str:
|
|
1098
|
+
return self.source_map.source_map_sha256
|
|
1099
|
+
|
|
1100
|
+
def lineage_manifest(self) -> tuple[dict[str, object], ...]:
|
|
1101
|
+
return tuple(source_map.to_manifest() for source_map in self.lineage)
|
|
1102
|
+
|
|
1103
|
+
def __repr__(self) -> str:
|
|
1104
|
+
segment_count = (
|
|
1105
|
+
getattr(self.source_map, "compact_interval_count")
|
|
1106
|
+
if getattr(self.source_map, "_compact_reload_map", False)
|
|
1107
|
+
else len(self.source_map.segments)
|
|
1108
|
+
)
|
|
1109
|
+
return (
|
|
1110
|
+
"MappedSource(text=<redacted>, artifact="
|
|
1111
|
+
f"{self.artifact.kind.value}:{self.artifact.source_sha256}, "
|
|
1112
|
+
f"segments={segment_count}, lineage={len(self.lineage)})"
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
def __deepcopy__(self, memo: dict[int, object]) -> MappedSource:
|
|
1116
|
+
return self
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def _mapped_semantic_summary(
|
|
1120
|
+
source: MappedSource,
|
|
1121
|
+
*,
|
|
1122
|
+
validate_structure: bool,
|
|
1123
|
+
) -> _MappedSemanticSummary:
|
|
1124
|
+
if not isinstance(source, MappedSource):
|
|
1125
|
+
raise ValueError("mapped semantic authority is invalid")
|
|
1126
|
+
summary = source._semantic_summary
|
|
1127
|
+
maps: list[SourceMap] = []
|
|
1128
|
+
for item in (source.source_map, *source.lineage, source._local_source_map):
|
|
1129
|
+
if item is not None and all(item is not existing for existing in maps):
|
|
1130
|
+
maps.append(item)
|
|
1131
|
+
current_proofs = tuple(
|
|
1132
|
+
(id(item), item._structural_proof)
|
|
1133
|
+
for item in maps
|
|
1134
|
+
)
|
|
1135
|
+
expected_authority = _mapped_semantic_authority_proof(
|
|
1136
|
+
source.artifact,
|
|
1137
|
+
source.source_map,
|
|
1138
|
+
source.lineage,
|
|
1139
|
+
source._local_source_map,
|
|
1140
|
+
summary.source_map_sha256,
|
|
1141
|
+
summary.lineage_sha256,
|
|
1142
|
+
summary.structural_proofs,
|
|
1143
|
+
)
|
|
1144
|
+
if (
|
|
1145
|
+
summary.source_map_object_id != id(source.source_map)
|
|
1146
|
+
or summary.lineage_object_id != id(source.lineage)
|
|
1147
|
+
or summary.local_source_map_object_id
|
|
1148
|
+
!= (None if source._local_source_map is None else id(source._local_source_map))
|
|
1149
|
+
or summary.structural_proofs != current_proofs
|
|
1150
|
+
or not compare_digest(summary.authority_proof, expected_authority)
|
|
1151
|
+
):
|
|
1152
|
+
raise ValueError("mapped semantic authority is invalid")
|
|
1153
|
+
if validate_structure:
|
|
1154
|
+
for source_map, (_, expected_proof) in zip(
|
|
1155
|
+
maps,
|
|
1156
|
+
summary.structural_proofs,
|
|
1157
|
+
strict=True,
|
|
1158
|
+
):
|
|
1159
|
+
compact_validator = getattr(
|
|
1160
|
+
source_map,
|
|
1161
|
+
"_validated_compact_structural_proof",
|
|
1162
|
+
None,
|
|
1163
|
+
)
|
|
1164
|
+
actual_proof = (
|
|
1165
|
+
compact_validator()
|
|
1166
|
+
if compact_validator is not None
|
|
1167
|
+
else _source_map_structural_proof(source_map, observe=True)
|
|
1168
|
+
)
|
|
1169
|
+
if not compare_digest(
|
|
1170
|
+
expected_proof,
|
|
1171
|
+
actual_proof,
|
|
1172
|
+
):
|
|
1173
|
+
raise ValueError("mapped semantic authority is invalid")
|
|
1174
|
+
_observe_text_work("semantic_summary_reuse", 1)
|
|
1175
|
+
return summary
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
def _trusted_mapped_semantic_summary(source: MappedSource) -> _MappedSemanticSummary:
|
|
1179
|
+
return _mapped_semantic_summary(source, validate_structure=False)
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
def _validated_mapped_semantic_summary(source: MappedSource) -> _MappedSemanticSummary:
|
|
1183
|
+
return _mapped_semantic_summary(source, validate_structure=True)
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
def _mapped_segment(segment: SourceMapSegment, offset: int, *, eof: bool = False) -> MappedOffset:
|
|
1187
|
+
if segment.relation is MappingRelation.SYNTHETIC:
|
|
1188
|
+
anchor_unit = segment.anchor_ref if isinstance(segment.anchor_ref, SourceUnitRef) else None
|
|
1189
|
+
return MappedOffset(
|
|
1190
|
+
MappingRelation.SYNTHETIC,
|
|
1191
|
+
synthetic_region=segment.synthetic_region,
|
|
1192
|
+
anchor_unit=anchor_unit,
|
|
1193
|
+
anchor_span=segment.anchor_span if anchor_unit is not None else None,
|
|
1194
|
+
)
|
|
1195
|
+
|
|
1196
|
+
unit = segment.origin_ref if isinstance(segment.origin_ref, SourceUnitRef) else None
|
|
1197
|
+
if unit is None or segment.origin is None:
|
|
1198
|
+
raise ValueError("flattened source map contains an intermediate artifact reference")
|
|
1199
|
+
anchor_unit = (
|
|
1200
|
+
segment.anchor_ref
|
|
1201
|
+
if isinstance(segment.anchor_ref, SourceUnitRef)
|
|
1202
|
+
else None
|
|
1203
|
+
)
|
|
1204
|
+
anchor_span = segment.anchor_span if anchor_unit is not None else None
|
|
1205
|
+
if segment.relation is MappingRelation.DERIVED:
|
|
1206
|
+
return MappedOffset(
|
|
1207
|
+
MappingRelation.DERIVED,
|
|
1208
|
+
unit,
|
|
1209
|
+
segment.origin,
|
|
1210
|
+
anchor_unit=anchor_unit,
|
|
1211
|
+
anchor_span=anchor_span,
|
|
1212
|
+
)
|
|
1213
|
+
delta = offset - segment.generated.start
|
|
1214
|
+
origin_offset = segment.origin.start + delta
|
|
1215
|
+
width = 0 if eof or segment.generated.start == segment.generated.end else 1
|
|
1216
|
+
return MappedOffset(
|
|
1217
|
+
MappingRelation.EXACT,
|
|
1218
|
+
unit,
|
|
1219
|
+
SourceSpan(origin_offset, origin_offset + width),
|
|
1220
|
+
anchor_unit=anchor_unit,
|
|
1221
|
+
anchor_span=anchor_span,
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
def map_offset(source_map: SourceMap, offset: int) -> MappedOffset:
|
|
1226
|
+
if not isinstance(source_map, SourceMap):
|
|
1227
|
+
raise ValueError("source_map must be a SourceMap")
|
|
1228
|
+
if type(offset) is not int or not 0 <= offset <= source_map.generated.character_length:
|
|
1229
|
+
raise ValueError("offset must be within the generated source")
|
|
1230
|
+
|
|
1231
|
+
positive = tuple(
|
|
1232
|
+
segment
|
|
1233
|
+
for segment in source_map.segments
|
|
1234
|
+
if segment.generated.start < segment.generated.end
|
|
1235
|
+
)
|
|
1236
|
+
if offset == source_map.generated.character_length:
|
|
1237
|
+
if positive:
|
|
1238
|
+
return _mapped_segment(positive[-1], offset, eof=True)
|
|
1239
|
+
exact_boundaries = tuple(
|
|
1240
|
+
segment
|
|
1241
|
+
for segment in source_map.segments
|
|
1242
|
+
if segment.relation is MappingRelation.EXACT
|
|
1243
|
+
and segment.origin is not None
|
|
1244
|
+
and segment.origin.start == segment.origin.end
|
|
1245
|
+
)
|
|
1246
|
+
if len(exact_boundaries) == 1:
|
|
1247
|
+
return _mapped_segment(exact_boundaries[0], offset, eof=True)
|
|
1248
|
+
raise ValueError("offset has no mappable position in the generated source")
|
|
1249
|
+
|
|
1250
|
+
starts = tuple(segment.generated.start for segment in positive)
|
|
1251
|
+
index = bisect_right(starts, offset) - 1
|
|
1252
|
+
if index < 0 or offset >= positive[index].generated.end:
|
|
1253
|
+
raise ValueError("offset has no generated source mapping")
|
|
1254
|
+
return _mapped_segment(positive[index], offset)
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
_RELATION_COMPOSITION: dict[tuple[MappingRelation, MappingRelation], MappingRelation] = {
|
|
1258
|
+
(MappingRelation.EXACT, MappingRelation.EXACT): MappingRelation.EXACT,
|
|
1259
|
+
(MappingRelation.EXACT, MappingRelation.DERIVED): MappingRelation.DERIVED,
|
|
1260
|
+
(MappingRelation.EXACT, MappingRelation.SYNTHETIC): MappingRelation.SYNTHETIC,
|
|
1261
|
+
(MappingRelation.DERIVED, MappingRelation.EXACT): MappingRelation.DERIVED,
|
|
1262
|
+
(MappingRelation.DERIVED, MappingRelation.DERIVED): MappingRelation.DERIVED,
|
|
1263
|
+
(MappingRelation.DERIVED, MappingRelation.SYNTHETIC): MappingRelation.SYNTHETIC,
|
|
1264
|
+
(MappingRelation.SYNTHETIC, MappingRelation.EXACT): MappingRelation.SYNTHETIC,
|
|
1265
|
+
(MappingRelation.SYNTHETIC, MappingRelation.DERIVED): MappingRelation.SYNTHETIC,
|
|
1266
|
+
(MappingRelation.SYNTHETIC, MappingRelation.SYNTHETIC): MappingRelation.SYNTHETIC,
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
def _overlaps(segment: SourceMapSegment, span: SourceSpan) -> bool:
|
|
1271
|
+
if segment.generated.start == segment.generated.end:
|
|
1272
|
+
return span.start <= segment.generated.start <= span.end
|
|
1273
|
+
return segment.generated.start < span.end and span.start < segment.generated.end
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
def _project_parent_piece(
|
|
1277
|
+
local: SourceMapSegment,
|
|
1278
|
+
parent: SourceMapSegment,
|
|
1279
|
+
generated: SourceSpan,
|
|
1280
|
+
parent_piece: SourceSpan,
|
|
1281
|
+
local_anchor: tuple[SourceUnitRef | None, SourceSpan | None],
|
|
1282
|
+
) -> SourceMapSegment:
|
|
1283
|
+
relation = _RELATION_COMPOSITION[(local.relation, parent.relation)]
|
|
1284
|
+
if relation is MappingRelation.SYNTHETIC:
|
|
1285
|
+
region = (
|
|
1286
|
+
parent.synthetic_region
|
|
1287
|
+
if parent.relation is MappingRelation.SYNTHETIC
|
|
1288
|
+
else local.synthetic_region
|
|
1289
|
+
)
|
|
1290
|
+
anchor_ref = parent.anchor_ref
|
|
1291
|
+
anchor_span = parent.anchor_span
|
|
1292
|
+
return SourceMapSegment(
|
|
1293
|
+
generated,
|
|
1294
|
+
None,
|
|
1295
|
+
None,
|
|
1296
|
+
relation,
|
|
1297
|
+
region or "composed_synthetic",
|
|
1298
|
+
anchor_ref,
|
|
1299
|
+
anchor_span,
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
assert parent.origin is not None
|
|
1303
|
+
if parent.relation is MappingRelation.EXACT:
|
|
1304
|
+
delta_start = parent_piece.start - parent.generated.start
|
|
1305
|
+
delta_end = parent_piece.end - parent.generated.start
|
|
1306
|
+
origin = SourceSpan(parent.origin.start + delta_start, parent.origin.start + delta_end)
|
|
1307
|
+
else:
|
|
1308
|
+
origin = parent.origin
|
|
1309
|
+
anchor_ref, anchor_span = local_anchor
|
|
1310
|
+
if anchor_ref is None and isinstance(parent.anchor_ref, SourceUnitRef):
|
|
1311
|
+
anchor_ref = parent.anchor_ref
|
|
1312
|
+
anchor_span = parent.anchor_span
|
|
1313
|
+
region = (
|
|
1314
|
+
local.synthetic_region
|
|
1315
|
+
if local.relation is MappingRelation.DERIVED
|
|
1316
|
+
else parent.synthetic_region
|
|
1317
|
+
if parent.relation is MappingRelation.DERIVED
|
|
1318
|
+
else None
|
|
1319
|
+
)
|
|
1320
|
+
return SourceMapSegment(
|
|
1321
|
+
generated,
|
|
1322
|
+
parent.origin_ref,
|
|
1323
|
+
origin,
|
|
1324
|
+
relation,
|
|
1325
|
+
region if relation is MappingRelation.DERIVED else None,
|
|
1326
|
+
anchor_ref,
|
|
1327
|
+
anchor_span,
|
|
1328
|
+
)
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
def _anchor_for_parent_span(
|
|
1332
|
+
parent_map: SourceMap, span: SourceSpan
|
|
1333
|
+
) -> tuple[SourceUnitRef | None, SourceSpan | None]:
|
|
1334
|
+
if span.start == span.end:
|
|
1335
|
+
mapped = map_offset(parent_map, span.start)
|
|
1336
|
+
if mapped.unit is not None and mapped.origin_span is not None:
|
|
1337
|
+
point = mapped.origin_span.start
|
|
1338
|
+
return mapped.unit, SourceSpan(point, point)
|
|
1339
|
+
return mapped.anchor_unit, mapped.anchor_span
|
|
1340
|
+
|
|
1341
|
+
candidates: list[tuple[SourceUnitRef, SourceSpan]] = []
|
|
1342
|
+
for segment in parent_map.segments:
|
|
1343
|
+
if not _overlaps(segment, span) or segment.generated.start == segment.generated.end:
|
|
1344
|
+
continue
|
|
1345
|
+
intersection = SourceSpan(
|
|
1346
|
+
max(segment.generated.start, span.start),
|
|
1347
|
+
min(segment.generated.end, span.end),
|
|
1348
|
+
)
|
|
1349
|
+
if segment.relation is MappingRelation.EXACT and isinstance(
|
|
1350
|
+
segment.origin_ref, SourceUnitRef
|
|
1351
|
+
):
|
|
1352
|
+
assert segment.origin is not None
|
|
1353
|
+
start = segment.origin.start + intersection.start - segment.generated.start
|
|
1354
|
+
end = segment.origin.start + intersection.end - segment.generated.start
|
|
1355
|
+
candidates.append((segment.origin_ref, SourceSpan(start, end)))
|
|
1356
|
+
elif segment.relation is MappingRelation.DERIVED and isinstance(
|
|
1357
|
+
segment.origin_ref, SourceUnitRef
|
|
1358
|
+
):
|
|
1359
|
+
assert segment.origin is not None
|
|
1360
|
+
candidates.append((segment.origin_ref, segment.origin))
|
|
1361
|
+
elif isinstance(segment.anchor_ref, SourceUnitRef) and segment.anchor_span is not None:
|
|
1362
|
+
candidates.append((segment.anchor_ref, segment.anchor_span))
|
|
1363
|
+
if not candidates or any(unit != candidates[0][0] for unit, _ in candidates[1:]):
|
|
1364
|
+
return None, None
|
|
1365
|
+
return candidates[0][0], SourceSpan(
|
|
1366
|
+
min(candidate.start for _, candidate in candidates),
|
|
1367
|
+
max(candidate.end for _, candidate in candidates),
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def _compose_segment(
|
|
1372
|
+
local: SourceMapSegment, parent_map: SourceMap
|
|
1373
|
+
) -> list[SourceMapSegment]:
|
|
1374
|
+
if local.relation is MappingRelation.SYNTHETIC:
|
|
1375
|
+
anchor_ref: SourceUnitRef | None = None
|
|
1376
|
+
anchor_span: SourceSpan | None = None
|
|
1377
|
+
if local.anchor_span is not None:
|
|
1378
|
+
anchor_ref, anchor_span = _anchor_for_parent_span(parent_map, local.anchor_span)
|
|
1379
|
+
return [
|
|
1380
|
+
SourceMapSegment(
|
|
1381
|
+
local.generated,
|
|
1382
|
+
None,
|
|
1383
|
+
None,
|
|
1384
|
+
MappingRelation.SYNTHETIC,
|
|
1385
|
+
local.synthetic_region,
|
|
1386
|
+
anchor_ref,
|
|
1387
|
+
anchor_span,
|
|
1388
|
+
)
|
|
1389
|
+
]
|
|
1390
|
+
|
|
1391
|
+
assert local.origin is not None
|
|
1392
|
+
local_anchor: tuple[SourceUnitRef | None, SourceSpan | None] = (None, None)
|
|
1393
|
+
if local.anchor_span is not None:
|
|
1394
|
+
local_anchor = _anchor_for_parent_span(parent_map, local.anchor_span)
|
|
1395
|
+
local_generated_length = local.generated.end - local.generated.start
|
|
1396
|
+
local_origin_length = local.origin.end - local.origin.start
|
|
1397
|
+
if local_generated_length != local_origin_length:
|
|
1398
|
+
positive_parents = [
|
|
1399
|
+
segment
|
|
1400
|
+
for segment in parent_map.segments
|
|
1401
|
+
if segment.generated.start < segment.generated.end
|
|
1402
|
+
and _overlaps(segment, local.origin)
|
|
1403
|
+
]
|
|
1404
|
+
if local.origin.start < local.origin.end and len(positive_parents) != 1:
|
|
1405
|
+
raise ValueError(
|
|
1406
|
+
"unequal-width derived fragment crosses multiple parent segments"
|
|
1407
|
+
)
|
|
1408
|
+
unit, origin = _anchor_for_parent_span(parent_map, local.origin)
|
|
1409
|
+
synthetic_parent = next(
|
|
1410
|
+
(
|
|
1411
|
+
segment
|
|
1412
|
+
for segment in positive_parents
|
|
1413
|
+
if segment.relation is MappingRelation.SYNTHETIC
|
|
1414
|
+
),
|
|
1415
|
+
None,
|
|
1416
|
+
)
|
|
1417
|
+
if synthetic_parent is not None:
|
|
1418
|
+
anchor_ref = (
|
|
1419
|
+
synthetic_parent.anchor_ref
|
|
1420
|
+
if isinstance(synthetic_parent.anchor_ref, SourceUnitRef)
|
|
1421
|
+
else local_anchor[0] or unit
|
|
1422
|
+
)
|
|
1423
|
+
anchor_span = (
|
|
1424
|
+
synthetic_parent.anchor_span
|
|
1425
|
+
if anchor_ref == synthetic_parent.anchor_ref
|
|
1426
|
+
else local_anchor[1] or origin
|
|
1427
|
+
)
|
|
1428
|
+
return [
|
|
1429
|
+
SourceMapSegment(
|
|
1430
|
+
local.generated,
|
|
1431
|
+
None,
|
|
1432
|
+
None,
|
|
1433
|
+
MappingRelation.SYNTHETIC,
|
|
1434
|
+
synthetic_parent.synthetic_region
|
|
1435
|
+
or local.synthetic_region
|
|
1436
|
+
or "composed_synthetic",
|
|
1437
|
+
anchor_ref,
|
|
1438
|
+
anchor_span,
|
|
1439
|
+
)
|
|
1440
|
+
]
|
|
1441
|
+
if unit is None or origin is None:
|
|
1442
|
+
raise ValueError("derived fragment crosses multiple parent origins")
|
|
1443
|
+
anchor_ref, anchor_span = local_anchor
|
|
1444
|
+
if anchor_ref is None and len(positive_parents) == 1 and isinstance(
|
|
1445
|
+
positive_parents[0].anchor_ref, SourceUnitRef
|
|
1446
|
+
):
|
|
1447
|
+
anchor_ref = positive_parents[0].anchor_ref
|
|
1448
|
+
anchor_span = positive_parents[0].anchor_span
|
|
1449
|
+
return [
|
|
1450
|
+
SourceMapSegment(
|
|
1451
|
+
local.generated,
|
|
1452
|
+
unit,
|
|
1453
|
+
origin,
|
|
1454
|
+
MappingRelation.DERIVED,
|
|
1455
|
+
local.synthetic_region,
|
|
1456
|
+
anchor_ref,
|
|
1457
|
+
anchor_span,
|
|
1458
|
+
)
|
|
1459
|
+
]
|
|
1460
|
+
|
|
1461
|
+
composed: list[SourceMapSegment] = []
|
|
1462
|
+
for parent in parent_map.segments:
|
|
1463
|
+
if not _overlaps(parent, local.origin):
|
|
1464
|
+
continue
|
|
1465
|
+
if parent.generated.start == parent.generated.end:
|
|
1466
|
+
parent_piece = parent.generated
|
|
1467
|
+
else:
|
|
1468
|
+
parent_piece = SourceSpan(
|
|
1469
|
+
max(parent.generated.start, local.origin.start),
|
|
1470
|
+
min(parent.generated.end, local.origin.end),
|
|
1471
|
+
)
|
|
1472
|
+
generated = SourceSpan(
|
|
1473
|
+
local.generated.start + parent_piece.start - local.origin.start,
|
|
1474
|
+
local.generated.start + parent_piece.end - local.origin.start,
|
|
1475
|
+
)
|
|
1476
|
+
composed.append(
|
|
1477
|
+
_project_parent_piece(
|
|
1478
|
+
local,
|
|
1479
|
+
parent,
|
|
1480
|
+
generated,
|
|
1481
|
+
parent_piece,
|
|
1482
|
+
local_anchor,
|
|
1483
|
+
)
|
|
1484
|
+
)
|
|
1485
|
+
if not composed:
|
|
1486
|
+
raise ValueError("local segment has no parent mapping")
|
|
1487
|
+
return composed
|
|
1488
|
+
|
|
1489
|
+
|
|
1490
|
+
def _coalesce_segments(segments: list[SourceMapSegment]) -> tuple[SourceMapSegment, ...]:
|
|
1491
|
+
result: list[SourceMapSegment] = []
|
|
1492
|
+
for segment in segments:
|
|
1493
|
+
if not result:
|
|
1494
|
+
result.append(segment)
|
|
1495
|
+
continue
|
|
1496
|
+
previous = result[-1]
|
|
1497
|
+
positive = (
|
|
1498
|
+
previous.generated.start < previous.generated.end
|
|
1499
|
+
and segment.generated.start < segment.generated.end
|
|
1500
|
+
)
|
|
1501
|
+
common = (
|
|
1502
|
+
positive
|
|
1503
|
+
and previous.generated.end == segment.generated.start
|
|
1504
|
+
and previous.relation is segment.relation
|
|
1505
|
+
and previous.origin_ref == segment.origin_ref
|
|
1506
|
+
and previous.synthetic_region == segment.synthetic_region
|
|
1507
|
+
and previous.anchor_ref == segment.anchor_ref
|
|
1508
|
+
and previous.anchor_span == segment.anchor_span
|
|
1509
|
+
)
|
|
1510
|
+
origins_join = (
|
|
1511
|
+
previous.origin is None
|
|
1512
|
+
and segment.origin is None
|
|
1513
|
+
or previous.origin is not None
|
|
1514
|
+
and segment.origin is not None
|
|
1515
|
+
and previous.origin.end == segment.origin.start
|
|
1516
|
+
)
|
|
1517
|
+
if common and origins_join:
|
|
1518
|
+
origin = (
|
|
1519
|
+
None
|
|
1520
|
+
if previous.origin is None
|
|
1521
|
+
else SourceSpan(
|
|
1522
|
+
previous.origin.start,
|
|
1523
|
+
segment.origin.end, # type: ignore[union-attr]
|
|
1524
|
+
)
|
|
1525
|
+
)
|
|
1526
|
+
result[-1] = SourceMapSegment(
|
|
1527
|
+
SourceSpan(previous.generated.start, segment.generated.end),
|
|
1528
|
+
previous.origin_ref,
|
|
1529
|
+
origin,
|
|
1530
|
+
previous.relation,
|
|
1531
|
+
previous.synthetic_region,
|
|
1532
|
+
previous.anchor_ref,
|
|
1533
|
+
previous.anchor_span,
|
|
1534
|
+
)
|
|
1535
|
+
else:
|
|
1536
|
+
result.append(segment)
|
|
1537
|
+
return tuple(result)
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
def compose_source_maps(local: SourceMap, parent: SourceMap) -> SourceMap:
|
|
1541
|
+
if not isinstance(local, SourceMap) or not isinstance(parent, SourceMap):
|
|
1542
|
+
raise ValueError("composition requires SourceMap values")
|
|
1543
|
+
_validate_flattened_map(parent, name="flattened parent map")
|
|
1544
|
+
for segment in local.segments:
|
|
1545
|
+
for reference in (segment.origin_ref, segment.anchor_ref):
|
|
1546
|
+
if isinstance(reference, SourceArtifactRef) and reference != parent.generated:
|
|
1547
|
+
raise ValueError("local map does not reference the exact parent artifact")
|
|
1548
|
+
if segment.relation is not MappingRelation.SYNTHETIC and not isinstance(
|
|
1549
|
+
segment.origin_ref, SourceArtifactRef
|
|
1550
|
+
):
|
|
1551
|
+
raise ValueError("local map origin must reference its parent artifact")
|
|
1552
|
+
if segment.anchor_ref is not None and not isinstance(segment.anchor_ref, SourceArtifactRef):
|
|
1553
|
+
raise ValueError("local map anchor must reference its parent artifact")
|
|
1554
|
+
|
|
1555
|
+
composed = [
|
|
1556
|
+
projected
|
|
1557
|
+
for segment in local.segments
|
|
1558
|
+
for projected in _compose_segment(segment, parent)
|
|
1559
|
+
]
|
|
1560
|
+
result = SourceMap(local.generated, _coalesce_segments(composed))
|
|
1561
|
+
_validate_flattened_map(result, name="composed flattened source map")
|
|
1562
|
+
return result
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
def _compose_reused_source_maps(local: SourceMap, parent: SourceMap) -> SourceMap:
|
|
1566
|
+
"""Compose monotonic imported fragments in one pass, else use the general path."""
|
|
1567
|
+
if (
|
|
1568
|
+
any(
|
|
1569
|
+
segment.generated.start == segment.generated.end
|
|
1570
|
+
for segment in parent.segments
|
|
1571
|
+
)
|
|
1572
|
+
or any(
|
|
1573
|
+
segment.relation is MappingRelation.SYNTHETIC
|
|
1574
|
+
or segment.anchor_ref is not None
|
|
1575
|
+
or segment.origin is None
|
|
1576
|
+
or segment.origin.start == segment.origin.end
|
|
1577
|
+
for segment in local.segments
|
|
1578
|
+
)
|
|
1579
|
+
or any(
|
|
1580
|
+
current.origin is None
|
|
1581
|
+
or following.origin is None
|
|
1582
|
+
or current.origin.start > following.origin.start
|
|
1583
|
+
for current, following in zip(
|
|
1584
|
+
local.segments,
|
|
1585
|
+
local.segments[1:],
|
|
1586
|
+
strict=False,
|
|
1587
|
+
)
|
|
1588
|
+
)
|
|
1589
|
+
):
|
|
1590
|
+
return compose_source_maps(local, parent)
|
|
1591
|
+
_validate_flattened_map(parent, name="flattened parent map")
|
|
1592
|
+
for segment in local.segments:
|
|
1593
|
+
if (
|
|
1594
|
+
segment.origin_ref != parent.generated
|
|
1595
|
+
or segment.anchor_ref is not None
|
|
1596
|
+
):
|
|
1597
|
+
return compose_source_maps(local, parent)
|
|
1598
|
+
|
|
1599
|
+
positive = tuple(
|
|
1600
|
+
segment
|
|
1601
|
+
for segment in parent.segments
|
|
1602
|
+
if segment.generated.start < segment.generated.end
|
|
1603
|
+
)
|
|
1604
|
+
composed: list[SourceMapSegment] = []
|
|
1605
|
+
parent_index = 0
|
|
1606
|
+
for segment in local.segments:
|
|
1607
|
+
assert segment.origin is not None
|
|
1608
|
+
while (
|
|
1609
|
+
parent_index < len(positive)
|
|
1610
|
+
and positive[parent_index].generated.end <= segment.origin.start
|
|
1611
|
+
):
|
|
1612
|
+
parent_index += 1
|
|
1613
|
+
overlapping: list[SourceMapSegment] = []
|
|
1614
|
+
scan = parent_index
|
|
1615
|
+
while (
|
|
1616
|
+
scan < len(positive)
|
|
1617
|
+
and positive[scan].generated.start < segment.origin.end
|
|
1618
|
+
):
|
|
1619
|
+
if _overlaps(positive[scan], segment.origin):
|
|
1620
|
+
overlapping.append(positive[scan])
|
|
1621
|
+
scan += 1
|
|
1622
|
+
generated_width = segment.generated.end - segment.generated.start
|
|
1623
|
+
origin_width = segment.origin.end - segment.origin.start
|
|
1624
|
+
if generated_width != origin_width:
|
|
1625
|
+
if len(overlapping) != 1:
|
|
1626
|
+
return compose_source_maps(local, parent)
|
|
1627
|
+
parent_segment = overlapping[0]
|
|
1628
|
+
if parent_segment.relation is MappingRelation.SYNTHETIC:
|
|
1629
|
+
composed.append(
|
|
1630
|
+
SourceMapSegment(
|
|
1631
|
+
segment.generated,
|
|
1632
|
+
None,
|
|
1633
|
+
None,
|
|
1634
|
+
MappingRelation.SYNTHETIC,
|
|
1635
|
+
parent_segment.synthetic_region
|
|
1636
|
+
or segment.synthetic_region
|
|
1637
|
+
or "composed_synthetic",
|
|
1638
|
+
parent_segment.anchor_ref,
|
|
1639
|
+
parent_segment.anchor_span,
|
|
1640
|
+
)
|
|
1641
|
+
)
|
|
1642
|
+
continue
|
|
1643
|
+
assert parent_segment.origin is not None
|
|
1644
|
+
origin = (
|
|
1645
|
+
SourceSpan(
|
|
1646
|
+
parent_segment.origin.start
|
|
1647
|
+
+ segment.origin.start
|
|
1648
|
+
- parent_segment.generated.start,
|
|
1649
|
+
parent_segment.origin.start
|
|
1650
|
+
+ segment.origin.end
|
|
1651
|
+
- parent_segment.generated.start,
|
|
1652
|
+
)
|
|
1653
|
+
if parent_segment.relation is MappingRelation.EXACT
|
|
1654
|
+
else parent_segment.origin
|
|
1655
|
+
)
|
|
1656
|
+
composed.append(
|
|
1657
|
+
SourceMapSegment(
|
|
1658
|
+
segment.generated,
|
|
1659
|
+
parent_segment.origin_ref,
|
|
1660
|
+
origin,
|
|
1661
|
+
MappingRelation.DERIVED,
|
|
1662
|
+
segment.synthetic_region,
|
|
1663
|
+
parent_segment.anchor_ref,
|
|
1664
|
+
parent_segment.anchor_span,
|
|
1665
|
+
)
|
|
1666
|
+
)
|
|
1667
|
+
continue
|
|
1668
|
+
if not overlapping:
|
|
1669
|
+
return compose_source_maps(local, parent)
|
|
1670
|
+
for parent_segment in overlapping:
|
|
1671
|
+
parent_piece = SourceSpan(
|
|
1672
|
+
max(parent_segment.generated.start, segment.origin.start),
|
|
1673
|
+
min(parent_segment.generated.end, segment.origin.end),
|
|
1674
|
+
)
|
|
1675
|
+
generated = SourceSpan(
|
|
1676
|
+
segment.generated.start
|
|
1677
|
+
+ parent_piece.start
|
|
1678
|
+
- segment.origin.start,
|
|
1679
|
+
segment.generated.start
|
|
1680
|
+
+ parent_piece.end
|
|
1681
|
+
- segment.origin.start,
|
|
1682
|
+
)
|
|
1683
|
+
composed.append(
|
|
1684
|
+
_project_parent_piece(
|
|
1685
|
+
segment,
|
|
1686
|
+
parent_segment,
|
|
1687
|
+
generated,
|
|
1688
|
+
parent_piece,
|
|
1689
|
+
(None, None),
|
|
1690
|
+
)
|
|
1691
|
+
)
|
|
1692
|
+
result = SourceMap(local.generated, _coalesce_segments(composed))
|
|
1693
|
+
_validate_flattened_map(result, name="composed flattened source map")
|
|
1694
|
+
return result
|
|
1695
|
+
|
|
1696
|
+
|
|
1697
|
+
def _line_ending_kind(text: str) -> str:
|
|
1698
|
+
crlf_count = text.count("\r\n")
|
|
1699
|
+
bare_lf_count = text.count("\n") - crlf_count
|
|
1700
|
+
bare_cr_count = text.count("\r") - crlf_count
|
|
1701
|
+
kinds = sum(count > 0 for count in (crlf_count, bare_lf_count, bare_cr_count))
|
|
1702
|
+
if kinds > 1:
|
|
1703
|
+
return "mixed"
|
|
1704
|
+
if crlf_count:
|
|
1705
|
+
return "crlf"
|
|
1706
|
+
if bare_lf_count:
|
|
1707
|
+
return "lf"
|
|
1708
|
+
if bare_cr_count:
|
|
1709
|
+
return "cr"
|
|
1710
|
+
return "none"
|
|
1711
|
+
|
|
1712
|
+
|
|
1713
|
+
def _line_ending_kind_fragments(
|
|
1714
|
+
fragments: list[_RetainedTextFragment],
|
|
1715
|
+
) -> str:
|
|
1716
|
+
"""Classify line endings without materializing retained fragments."""
|
|
1717
|
+
cr_count = 0
|
|
1718
|
+
lf_count = 0
|
|
1719
|
+
crlf_count = 0
|
|
1720
|
+
previous_ended_cr = False
|
|
1721
|
+
for fragment in fragments:
|
|
1722
|
+
summary = fragment.line_endings
|
|
1723
|
+
assert summary is not None
|
|
1724
|
+
cr_count += summary[0]
|
|
1725
|
+
lf_count += summary[1]
|
|
1726
|
+
crlf_count += summary[2]
|
|
1727
|
+
if previous_ended_cr and summary[3]:
|
|
1728
|
+
crlf_count += 1
|
|
1729
|
+
if fragment.text:
|
|
1730
|
+
previous_ended_cr = summary[4]
|
|
1731
|
+
bare_cr_count = cr_count - crlf_count
|
|
1732
|
+
bare_lf_count = lf_count - crlf_count
|
|
1733
|
+
kinds = sum(count > 0 for count in (crlf_count, bare_lf_count, bare_cr_count))
|
|
1734
|
+
if kinds > 1:
|
|
1735
|
+
return "mixed"
|
|
1736
|
+
if crlf_count:
|
|
1737
|
+
return "crlf"
|
|
1738
|
+
if bare_lf_count:
|
|
1739
|
+
return "lf"
|
|
1740
|
+
if bare_cr_count:
|
|
1741
|
+
return "cr"
|
|
1742
|
+
return "none"
|
|
1743
|
+
|
|
1744
|
+
|
|
1745
|
+
def mapped_visible_source(text: str, unit: SourceUnitRef) -> MappedSource:
|
|
1746
|
+
_require_str(text, "visible source")
|
|
1747
|
+
if not isinstance(unit, SourceUnitRef):
|
|
1748
|
+
raise ValueError("visible source unit must be a SourceUnitRef")
|
|
1749
|
+
if source_sha256(text) != unit.source_sha256:
|
|
1750
|
+
raise ValueError("visible source hash does not match its source unit")
|
|
1751
|
+
artifact = SourceArtifactRef(
|
|
1752
|
+
SourceArtifactKind.VISIBLE,
|
|
1753
|
+
unit.source_sha256,
|
|
1754
|
+
len(text),
|
|
1755
|
+
_line_ending_kind(text),
|
|
1756
|
+
)
|
|
1757
|
+
segment = SourceMapSegment(
|
|
1758
|
+
SourceSpan(0, len(text)),
|
|
1759
|
+
unit,
|
|
1760
|
+
SourceSpan(0, len(text)),
|
|
1761
|
+
MappingRelation.EXACT,
|
|
1762
|
+
)
|
|
1763
|
+
source_map = SourceMap(artifact, (segment,))
|
|
1764
|
+
return MappedSource(text, artifact, source_map)
|
|
1765
|
+
|
|
1766
|
+
|
|
1767
|
+
def compose_mapped_sources(
|
|
1768
|
+
sources: tuple[MappedSource, ...],
|
|
1769
|
+
*,
|
|
1770
|
+
visible_sources: tuple[MappedSource, ...],
|
|
1771
|
+
kind: SourceArtifactKind,
|
|
1772
|
+
) -> MappedSource:
|
|
1773
|
+
"""Join complete mapped fragments with checked, multi-origin provenance.
|
|
1774
|
+
|
|
1775
|
+
Visible originals fence hashes, bounds, and the bytes of exact mappings.
|
|
1776
|
+
Newlines are synthetic and anchored to the following fragment. The result
|
|
1777
|
+
retains no prior assembled artifact or generation through transform parents.
|
|
1778
|
+
"""
|
|
1779
|
+
if type(sources) is not tuple or type(visible_sources) is not tuple:
|
|
1780
|
+
raise ValueError("mapped composition requires immutable source tuples")
|
|
1781
|
+
visible: dict[SourceUnitRef, str] = {}
|
|
1782
|
+
identities: dict[tuple[SourceUnitKind, str, int], SourceUnitRef] = {}
|
|
1783
|
+
for source in visible_sources:
|
|
1784
|
+
_validated_mapped_semantic_summary(source)
|
|
1785
|
+
unit = source.source_map.map_offset(0).unit
|
|
1786
|
+
if (
|
|
1787
|
+
source.artifact.kind is not SourceArtifactKind.VISIBLE
|
|
1788
|
+
or unit is None
|
|
1789
|
+
or source_sha256(source.text) != unit.source_sha256
|
|
1790
|
+
or source.artifact.source_sha256 != unit.source_sha256
|
|
1791
|
+
or source.artifact.character_length != len(source.text)
|
|
1792
|
+
or source.source_map.segments != (
|
|
1793
|
+
SourceMapSegment(
|
|
1794
|
+
SourceSpan(0, len(source.text)),
|
|
1795
|
+
unit,
|
|
1796
|
+
SourceSpan(0, len(source.text)),
|
|
1797
|
+
MappingRelation.EXACT,
|
|
1798
|
+
),
|
|
1799
|
+
)
|
|
1800
|
+
):
|
|
1801
|
+
raise ValueError("mapped composition requires hash-matched visible sources")
|
|
1802
|
+
key = (unit.kind, unit.unit_id, unit.revision)
|
|
1803
|
+
if key in identities and identities[key] != unit:
|
|
1804
|
+
raise ValueError("mapped composition has conflicting source identities")
|
|
1805
|
+
identities[key] = unit
|
|
1806
|
+
visible[unit] = source.text
|
|
1807
|
+
|
|
1808
|
+
parts: list[str] = []
|
|
1809
|
+
segments: list[SourceMapSegment] = []
|
|
1810
|
+
local_segments: list[SourceMapSegment] = []
|
|
1811
|
+
lineage: list[SourceMap] = []
|
|
1812
|
+
cursor = 0
|
|
1813
|
+
for source in sources:
|
|
1814
|
+
_validated_mapped_semantic_summary(source)
|
|
1815
|
+
if (
|
|
1816
|
+
len(source.text) != source.artifact.character_length
|
|
1817
|
+
or source_sha256(source.text) != source.artifact.source_sha256
|
|
1818
|
+
):
|
|
1819
|
+
raise ValueError("mapped composition fragment hash or length does not match")
|
|
1820
|
+
for segment in source.source_map.segments:
|
|
1821
|
+
for reference, span in (
|
|
1822
|
+
(segment.origin_ref, segment.origin),
|
|
1823
|
+
(segment.anchor_ref, segment.anchor_span),
|
|
1824
|
+
):
|
|
1825
|
+
if reference is not None and (
|
|
1826
|
+
reference not in visible
|
|
1827
|
+
or span is None
|
|
1828
|
+
or span.end > len(visible[reference])
|
|
1829
|
+
):
|
|
1830
|
+
raise ValueError("mapped composition origin is not covered by visible sources")
|
|
1831
|
+
if segment.relation is MappingRelation.EXACT:
|
|
1832
|
+
assert segment.origin_ref is not None and segment.origin is not None
|
|
1833
|
+
if (
|
|
1834
|
+
source.text[segment.generated.start:segment.generated.end]
|
|
1835
|
+
!= visible[segment.origin_ref][segment.origin.start:segment.origin.end]
|
|
1836
|
+
):
|
|
1837
|
+
raise ValueError("mapped composition exact origin text does not match")
|
|
1838
|
+
if parts:
|
|
1839
|
+
anchor = source.source_map.map_offset(0)
|
|
1840
|
+
join = SourceMapSegment(
|
|
1841
|
+
SourceSpan(cursor, cursor + 1),
|
|
1842
|
+
None,
|
|
1843
|
+
None,
|
|
1844
|
+
MappingRelation.SYNTHETIC,
|
|
1845
|
+
"notebook_method_join",
|
|
1846
|
+
anchor.unit or anchor.anchor_unit,
|
|
1847
|
+
anchor.origin_span or anchor.anchor_span,
|
|
1848
|
+
)
|
|
1849
|
+
parts.append("\n")
|
|
1850
|
+
segments.append(join)
|
|
1851
|
+
local_segments.append(join)
|
|
1852
|
+
cursor += 1
|
|
1853
|
+
parts.append(source.text)
|
|
1854
|
+
for segment in source.source_map.segments:
|
|
1855
|
+
segments.append(SourceMapSegment(
|
|
1856
|
+
SourceSpan(cursor + segment.generated.start, cursor + segment.generated.end),
|
|
1857
|
+
segment.origin_ref,
|
|
1858
|
+
segment.origin,
|
|
1859
|
+
segment.relation,
|
|
1860
|
+
segment.synthetic_region,
|
|
1861
|
+
segment.anchor_ref,
|
|
1862
|
+
segment.anchor_span,
|
|
1863
|
+
))
|
|
1864
|
+
local_segments.append(SourceMapSegment(
|
|
1865
|
+
SourceSpan(cursor, cursor + len(source.text)),
|
|
1866
|
+
source.artifact,
|
|
1867
|
+
SourceSpan(0, len(source.text)),
|
|
1868
|
+
MappingRelation.EXACT,
|
|
1869
|
+
))
|
|
1870
|
+
lineage.extend(source.lineage)
|
|
1871
|
+
cursor += len(source.text)
|
|
1872
|
+
text = "".join(parts)
|
|
1873
|
+
artifact = SourceArtifactRef(kind, source_sha256(text), len(text), _line_ending_kind(text))
|
|
1874
|
+
if not segments:
|
|
1875
|
+
empty = SourceMapSegment(
|
|
1876
|
+
SourceSpan(0, 0), None, None, MappingRelation.SYNTHETIC, "empty_notebook_methods"
|
|
1877
|
+
)
|
|
1878
|
+
segments.append(empty)
|
|
1879
|
+
local_segments.append(empty)
|
|
1880
|
+
flattened = SourceMap(artifact, _coalesce_segments(segments))
|
|
1881
|
+
local = SourceMap(artifact, _coalesce_segments(local_segments))
|
|
1882
|
+
return MappedSource(text, artifact, flattened, (*lineage, local), local)
|
|
1883
|
+
|
|
1884
|
+
|
|
1885
|
+
_ARTIFACT_IDENTITY_FIELDS = {
|
|
1886
|
+
"lowering_semantic_version",
|
|
1887
|
+
"wrapper_semantic_version",
|
|
1888
|
+
"mode",
|
|
1889
|
+
"worker_generation",
|
|
1890
|
+
"worker_manifest_sha256",
|
|
1891
|
+
"export_catalog_sha256",
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
|
|
1895
|
+
class SourceTransformBuilder:
|
|
1896
|
+
def __init__(
|
|
1897
|
+
self,
|
|
1898
|
+
parent: MappedSource | _DeferredMappedSource,
|
|
1899
|
+
*,
|
|
1900
|
+
_normalized_parent_authority: object | None = None,
|
|
1901
|
+
) -> None:
|
|
1902
|
+
if not isinstance(parent, (MappedSource, _DeferredMappedSource)):
|
|
1903
|
+
raise ValueError("transform parent must be a mapped source")
|
|
1904
|
+
if (
|
|
1905
|
+
_normalized_parent_authority is not None
|
|
1906
|
+
and _normalized_parent_authority is not _NORMALIZED_WORKER_AUTHORITY
|
|
1907
|
+
):
|
|
1908
|
+
raise ValueError("normalized parent authority is invalid")
|
|
1909
|
+
self._parent = parent
|
|
1910
|
+
self._normalized_parent = _normalized_parent_authority is not None
|
|
1911
|
+
self._parts: list[_RetainedTextFragment] = []
|
|
1912
|
+
self._segments: list[SourceMapSegment] = []
|
|
1913
|
+
self._cursor = 0
|
|
1914
|
+
self._validated_retained_sources: set[int] = set()
|
|
1915
|
+
|
|
1916
|
+
def _validate_parent_span(self, span: SourceSpan) -> None:
|
|
1917
|
+
length = (
|
|
1918
|
+
len(self._parent.text)
|
|
1919
|
+
if isinstance(self._parent, MappedSource)
|
|
1920
|
+
else self._parent.character_length
|
|
1921
|
+
)
|
|
1922
|
+
if not isinstance(span, SourceSpan) or span.end > length:
|
|
1923
|
+
raise ValueError("fragment span must be within the parent source")
|
|
1924
|
+
|
|
1925
|
+
def _validate_normalized_worker_policy(
|
|
1926
|
+
self,
|
|
1927
|
+
kind: SourceArtifactKind,
|
|
1928
|
+
authority: object | None,
|
|
1929
|
+
) -> None:
|
|
1930
|
+
if authority is None:
|
|
1931
|
+
return
|
|
1932
|
+
if (
|
|
1933
|
+
authority is not _NORMALIZED_WORKER_AUTHORITY
|
|
1934
|
+
or kind is not SourceArtifactKind.WORKER_MODULE
|
|
1935
|
+
):
|
|
1936
|
+
raise ValueError("normalized Worker authority is invalid")
|
|
1937
|
+
for fragment in self._parts:
|
|
1938
|
+
_observe_text_work("worker_policy_fragment_visit", 1)
|
|
1939
|
+
if fragment.contains_nul is None:
|
|
1940
|
+
raise ValueError("normalized Worker fragment authority is incomplete")
|
|
1941
|
+
if fragment.contains_nul:
|
|
1942
|
+
raise ValueError("Worker source contains a NUL character")
|
|
1943
|
+
_observe_text_work("normalized_worker_authority", 1)
|
|
1944
|
+
|
|
1945
|
+
def _append(
|
|
1946
|
+
self,
|
|
1947
|
+
text: str,
|
|
1948
|
+
segment: SourceMapSegment,
|
|
1949
|
+
*,
|
|
1950
|
+
retained_source: object | None = None,
|
|
1951
|
+
retained_span: SourceSpan | None = None,
|
|
1952
|
+
) -> None:
|
|
1953
|
+
_observe_text_work("segment_allocate", 1)
|
|
1954
|
+
self._parts.append(
|
|
1955
|
+
_RetainedTextFragment(
|
|
1956
|
+
SourceSpan(self._cursor, self._cursor + len(text)),
|
|
1957
|
+
text,
|
|
1958
|
+
retained_source,
|
|
1959
|
+
retained_span,
|
|
1960
|
+
contains_nul=_local_nul_summary(text),
|
|
1961
|
+
)
|
|
1962
|
+
)
|
|
1963
|
+
self._segments.append(segment)
|
|
1964
|
+
self._cursor += len(text)
|
|
1965
|
+
|
|
1966
|
+
def _append_retained(
|
|
1967
|
+
self,
|
|
1968
|
+
retained_source: MappedSource | _DeferredMappedSource,
|
|
1969
|
+
retained_span: SourceSpan,
|
|
1970
|
+
segment: SourceMapSegment,
|
|
1971
|
+
) -> None:
|
|
1972
|
+
_observe_text_work("segment_allocate", 1)
|
|
1973
|
+
source_identity = id(retained_source)
|
|
1974
|
+
if source_identity not in self._validated_retained_sources:
|
|
1975
|
+
_validate_retained_source(retained_source)
|
|
1976
|
+
self._validated_retained_sources.add(source_identity)
|
|
1977
|
+
trusted_normalized_parent = (
|
|
1978
|
+
self._normalized_parent and retained_source is self._parent
|
|
1979
|
+
)
|
|
1980
|
+
fragments = _retained_fragments_for_span(
|
|
1981
|
+
retained_source,
|
|
1982
|
+
retained_span,
|
|
1983
|
+
scan_local_nul=not trusted_normalized_parent,
|
|
1984
|
+
)
|
|
1985
|
+
retained_cursor = retained_span.start
|
|
1986
|
+
for fragment in fragments:
|
|
1987
|
+
width = len(fragment.text)
|
|
1988
|
+
self._parts.append(
|
|
1989
|
+
_RetainedTextFragment(
|
|
1990
|
+
SourceSpan(self._cursor, self._cursor + width),
|
|
1991
|
+
fragment.text,
|
|
1992
|
+
retained_source,
|
|
1993
|
+
SourceSpan(retained_cursor, retained_cursor + width),
|
|
1994
|
+
fragment.line_endings,
|
|
1995
|
+
(
|
|
1996
|
+
False
|
|
1997
|
+
if trusted_normalized_parent
|
|
1998
|
+
and fragment.contains_nul is None
|
|
1999
|
+
else fragment.contains_nul
|
|
2000
|
+
),
|
|
2001
|
+
)
|
|
2002
|
+
)
|
|
2003
|
+
self._cursor += width
|
|
2004
|
+
retained_cursor += width
|
|
2005
|
+
_observe_text_work("retained_fragment", retained_span.end - retained_span.start)
|
|
2006
|
+
self._segments.append(segment)
|
|
2007
|
+
|
|
2008
|
+
def _exact_segment(self, span: SourceSpan, width: int) -> SourceMapSegment:
|
|
2009
|
+
return SourceMapSegment(
|
|
2010
|
+
SourceSpan(self._cursor, self._cursor + width),
|
|
2011
|
+
self._parent.artifact,
|
|
2012
|
+
span,
|
|
2013
|
+
MappingRelation.EXACT,
|
|
2014
|
+
)
|
|
2015
|
+
|
|
2016
|
+
def copy(self, span: SourceSpan) -> None:
|
|
2017
|
+
self._validate_parent_span(span)
|
|
2018
|
+
width = span.end - span.start
|
|
2019
|
+
self._append_retained(
|
|
2020
|
+
self._parent,
|
|
2021
|
+
span,
|
|
2022
|
+
self._exact_segment(span, width),
|
|
2023
|
+
)
|
|
2024
|
+
|
|
2025
|
+
def exact(self, text: str, span: SourceSpan) -> None:
|
|
2026
|
+
"""Append already materialized exact text for a deferred parent."""
|
|
2027
|
+
_require_str(text, "exact text")
|
|
2028
|
+
self._validate_parent_span(span)
|
|
2029
|
+
if len(text) != span.end - span.start:
|
|
2030
|
+
raise ValueError("exact text must preserve the parent span length")
|
|
2031
|
+
self._append(text, self._exact_segment(span, len(text)))
|
|
2032
|
+
|
|
2033
|
+
def derived(
|
|
2034
|
+
self,
|
|
2035
|
+
text: str,
|
|
2036
|
+
span: SourceSpan,
|
|
2037
|
+
region: str,
|
|
2038
|
+
*,
|
|
2039
|
+
anchor: SourceSpan | None = None,
|
|
2040
|
+
) -> None:
|
|
2041
|
+
_require_str(text, "derived text")
|
|
2042
|
+
_require_str(region, "derived region", nonempty=True)
|
|
2043
|
+
self._validate_parent_span(span)
|
|
2044
|
+
if anchor is not None:
|
|
2045
|
+
self._validate_parent_span(anchor)
|
|
2046
|
+
self._append(
|
|
2047
|
+
text,
|
|
2048
|
+
SourceMapSegment(
|
|
2049
|
+
SourceSpan(self._cursor, self._cursor + len(text)),
|
|
2050
|
+
self._parent.artifact,
|
|
2051
|
+
span,
|
|
2052
|
+
MappingRelation.DERIVED,
|
|
2053
|
+
region,
|
|
2054
|
+
self._parent.artifact if anchor is not None else None,
|
|
2055
|
+
anchor,
|
|
2056
|
+
),
|
|
2057
|
+
)
|
|
2058
|
+
|
|
2059
|
+
def reuse_exact(self, span: SourceSpan) -> None:
|
|
2060
|
+
"""Import an unchanged exact parent segment without entering copy work."""
|
|
2061
|
+
self.copy(span)
|
|
2062
|
+
|
|
2063
|
+
def retained_exact(
|
|
2064
|
+
self,
|
|
2065
|
+
retained_source: MappedSource | _DeferredMappedSource,
|
|
2066
|
+
retained_span: SourceSpan,
|
|
2067
|
+
parent_span: SourceSpan,
|
|
2068
|
+
) -> None:
|
|
2069
|
+
"""Map an existing materialized span exactly against the current parent."""
|
|
2070
|
+
self._validate_parent_span(parent_span)
|
|
2071
|
+
width = retained_span.end - retained_span.start
|
|
2072
|
+
if width != parent_span.end - parent_span.start:
|
|
2073
|
+
raise ValueError("retained exact text must preserve span length")
|
|
2074
|
+
self._append_retained(
|
|
2075
|
+
retained_source,
|
|
2076
|
+
retained_span,
|
|
2077
|
+
self._exact_segment(parent_span, width),
|
|
2078
|
+
)
|
|
2079
|
+
|
|
2080
|
+
def synthetic(self, text: str, anchor: SourceSpan, region: str) -> None:
|
|
2081
|
+
_require_str(text, "synthetic text")
|
|
2082
|
+
_require_str(region, "synthetic region", nonempty=True)
|
|
2083
|
+
self._validate_parent_span(anchor)
|
|
2084
|
+
self._append(
|
|
2085
|
+
text,
|
|
2086
|
+
SourceMapSegment(
|
|
2087
|
+
SourceSpan(self._cursor, self._cursor + len(text)),
|
|
2088
|
+
None,
|
|
2089
|
+
None,
|
|
2090
|
+
MappingRelation.SYNTHETIC,
|
|
2091
|
+
region,
|
|
2092
|
+
self._parent.artifact,
|
|
2093
|
+
anchor,
|
|
2094
|
+
),
|
|
2095
|
+
)
|
|
2096
|
+
|
|
2097
|
+
def reuse_derived(
|
|
2098
|
+
self,
|
|
2099
|
+
text: str,
|
|
2100
|
+
span: SourceSpan,
|
|
2101
|
+
region: str,
|
|
2102
|
+
*,
|
|
2103
|
+
anchor: SourceSpan | None = None,
|
|
2104
|
+
) -> None:
|
|
2105
|
+
"""Import an unchanged derived segment against the current parent."""
|
|
2106
|
+
_require_str(text, "derived text")
|
|
2107
|
+
_require_str(region, "derived region", nonempty=True)
|
|
2108
|
+
self._validate_parent_span(span)
|
|
2109
|
+
if anchor is not None:
|
|
2110
|
+
self._validate_parent_span(anchor)
|
|
2111
|
+
self._append(
|
|
2112
|
+
text,
|
|
2113
|
+
SourceMapSegment(
|
|
2114
|
+
SourceSpan(self._cursor, self._cursor + len(text)),
|
|
2115
|
+
self._parent.artifact,
|
|
2116
|
+
span,
|
|
2117
|
+
MappingRelation.DERIVED,
|
|
2118
|
+
region,
|
|
2119
|
+
self._parent.artifact if anchor is not None else None,
|
|
2120
|
+
anchor,
|
|
2121
|
+
),
|
|
2122
|
+
)
|
|
2123
|
+
|
|
2124
|
+
def retained_derived(
|
|
2125
|
+
self,
|
|
2126
|
+
retained_source: MappedSource | _DeferredMappedSource,
|
|
2127
|
+
retained_span: SourceSpan,
|
|
2128
|
+
span: SourceSpan,
|
|
2129
|
+
region: str,
|
|
2130
|
+
*,
|
|
2131
|
+
anchor: SourceSpan | None = None,
|
|
2132
|
+
) -> None:
|
|
2133
|
+
"""Map an existing materialized span as derived current-parent text."""
|
|
2134
|
+
_require_str(region, "derived region", nonempty=True)
|
|
2135
|
+
self._validate_parent_span(span)
|
|
2136
|
+
if anchor is not None:
|
|
2137
|
+
self._validate_parent_span(anchor)
|
|
2138
|
+
width = retained_span.end - retained_span.start
|
|
2139
|
+
self._append_retained(
|
|
2140
|
+
retained_source,
|
|
2141
|
+
retained_span,
|
|
2142
|
+
SourceMapSegment(
|
|
2143
|
+
SourceSpan(self._cursor, self._cursor + width),
|
|
2144
|
+
self._parent.artifact,
|
|
2145
|
+
span,
|
|
2146
|
+
MappingRelation.DERIVED,
|
|
2147
|
+
region,
|
|
2148
|
+
self._parent.artifact if anchor is not None else None,
|
|
2149
|
+
anchor,
|
|
2150
|
+
),
|
|
2151
|
+
)
|
|
2152
|
+
|
|
2153
|
+
def reuse_synthetic(self, text: str, anchor: SourceSpan, region: str) -> None:
|
|
2154
|
+
"""Import an unchanged synthetic segment against the current parent."""
|
|
2155
|
+
_require_str(text, "synthetic text")
|
|
2156
|
+
_require_str(region, "synthetic region", nonempty=True)
|
|
2157
|
+
self._validate_parent_span(anchor)
|
|
2158
|
+
self._append(
|
|
2159
|
+
text,
|
|
2160
|
+
SourceMapSegment(
|
|
2161
|
+
SourceSpan(self._cursor, self._cursor + len(text)),
|
|
2162
|
+
None,
|
|
2163
|
+
None,
|
|
2164
|
+
MappingRelation.SYNTHETIC,
|
|
2165
|
+
region,
|
|
2166
|
+
self._parent.artifact,
|
|
2167
|
+
anchor,
|
|
2168
|
+
),
|
|
2169
|
+
)
|
|
2170
|
+
|
|
2171
|
+
def retained_synthetic(
|
|
2172
|
+
self,
|
|
2173
|
+
retained_source: MappedSource | _DeferredMappedSource,
|
|
2174
|
+
retained_span: SourceSpan,
|
|
2175
|
+
anchor: SourceSpan,
|
|
2176
|
+
region: str,
|
|
2177
|
+
) -> None:
|
|
2178
|
+
"""Map an existing materialized span as synthetic current-parent text."""
|
|
2179
|
+
_require_str(region, "synthetic region", nonempty=True)
|
|
2180
|
+
self._validate_parent_span(anchor)
|
|
2181
|
+
width = retained_span.end - retained_span.start
|
|
2182
|
+
self._append_retained(
|
|
2183
|
+
retained_source,
|
|
2184
|
+
retained_span,
|
|
2185
|
+
SourceMapSegment(
|
|
2186
|
+
SourceSpan(self._cursor, self._cursor + width),
|
|
2187
|
+
None,
|
|
2188
|
+
None,
|
|
2189
|
+
MappingRelation.SYNTHETIC,
|
|
2190
|
+
region,
|
|
2191
|
+
self._parent.artifact,
|
|
2192
|
+
anchor,
|
|
2193
|
+
),
|
|
2194
|
+
)
|
|
2195
|
+
|
|
2196
|
+
def _artifact(
|
|
2197
|
+
self,
|
|
2198
|
+
kind: SourceArtifactKind,
|
|
2199
|
+
identity: dict[str, object],
|
|
2200
|
+
*,
|
|
2201
|
+
deferred: bool,
|
|
2202
|
+
) -> tuple[SourceArtifactRef, str | None]:
|
|
2203
|
+
if type(kind) is not SourceArtifactKind:
|
|
2204
|
+
raise ValueError("artifact kind must be a SourceArtifactKind")
|
|
2205
|
+
unknown = set(identity) - _ARTIFACT_IDENTITY_FIELDS
|
|
2206
|
+
if unknown:
|
|
2207
|
+
raise ValueError(
|
|
2208
|
+
f"unsupported artifact identity metadata: {sorted(unknown)!r}"
|
|
2209
|
+
)
|
|
2210
|
+
if not self._segments:
|
|
2211
|
+
raise ValueError("transform must contain at least one explicit fragment")
|
|
2212
|
+
if deferred:
|
|
2213
|
+
digest = sha256()
|
|
2214
|
+
for fragment in self._parts:
|
|
2215
|
+
digest.update(fragment.text.encode("utf-8"))
|
|
2216
|
+
text_hash = digest.hexdigest()
|
|
2217
|
+
_observe_text_work("projection_hash", self._cursor)
|
|
2218
|
+
text = None
|
|
2219
|
+
else:
|
|
2220
|
+
_observe_text_work("materialize", self._cursor)
|
|
2221
|
+
text = "".join(fragment.text for fragment in self._parts)
|
|
2222
|
+
_observe_text_work("final_hash", self._cursor)
|
|
2223
|
+
text_hash = source_sha256(text)
|
|
2224
|
+
ending = _line_ending_kind_fragments(self._parts)
|
|
2225
|
+
artifact = SourceArtifactRef(
|
|
2226
|
+
kind,
|
|
2227
|
+
text_hash,
|
|
2228
|
+
self._cursor,
|
|
2229
|
+
ending,
|
|
2230
|
+
**identity, # type: ignore[arg-type]
|
|
2231
|
+
)
|
|
2232
|
+
return artifact, text
|
|
2233
|
+
|
|
2234
|
+
def build_deferred(
|
|
2235
|
+
self,
|
|
2236
|
+
kind: SourceArtifactKind,
|
|
2237
|
+
**identity: object,
|
|
2238
|
+
) -> _DeferredMappedSource:
|
|
2239
|
+
artifact, _ = self._artifact(kind, identity, deferred=True)
|
|
2240
|
+
local = SourceMap(artifact, _coalesce_segments(self._segments))
|
|
2241
|
+
flattened = _compose_reused_source_maps(local, self._parent.source_map)
|
|
2242
|
+
lineage = (*self._parent.lineage, local)
|
|
2243
|
+
retained = tuple(self._parts)
|
|
2244
|
+
return _DeferredMappedSource(
|
|
2245
|
+
artifact,
|
|
2246
|
+
flattened,
|
|
2247
|
+
lineage,
|
|
2248
|
+
local,
|
|
2249
|
+
retained,
|
|
2250
|
+
tuple(fragment.generated.start for fragment in retained),
|
|
2251
|
+
_retained_state_proof(artifact, retained),
|
|
2252
|
+
_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
2253
|
+
)
|
|
2254
|
+
|
|
2255
|
+
def build(
|
|
2256
|
+
self,
|
|
2257
|
+
kind: SourceArtifactKind,
|
|
2258
|
+
*,
|
|
2259
|
+
_normalized_worker_authority: object | None = None,
|
|
2260
|
+
**identity: object,
|
|
2261
|
+
) -> MappedSource:
|
|
2262
|
+
self._validate_normalized_worker_policy(
|
|
2263
|
+
kind,
|
|
2264
|
+
_normalized_worker_authority,
|
|
2265
|
+
)
|
|
2266
|
+
artifact, text = self._artifact(kind, identity, deferred=False)
|
|
2267
|
+
assert text is not None
|
|
2268
|
+
local = SourceMap(artifact, _coalesce_segments(self._segments))
|
|
2269
|
+
flattened = compose_source_maps(local, self._parent.source_map)
|
|
2270
|
+
lineage = (*self._parent.lineage, local)
|
|
2271
|
+
proof = _mint_trusted_text_proof(
|
|
2272
|
+
text,
|
|
2273
|
+
artifact.source_sha256,
|
|
2274
|
+
authority=_TRUSTED_TEXT_PROOF_AUTHORITY,
|
|
2275
|
+
)
|
|
2276
|
+
return MappedSource(
|
|
2277
|
+
text,
|
|
2278
|
+
artifact,
|
|
2279
|
+
flattened,
|
|
2280
|
+
lineage,
|
|
2281
|
+
local,
|
|
2282
|
+
_retained_fragments=tuple(self._parts),
|
|
2283
|
+
_transform_parent=self._parent,
|
|
2284
|
+
_trusted_text_proof=proof,
|
|
2285
|
+
_retained_authority=_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
2286
|
+
_normalized_worker_authority=_normalized_worker_authority,
|
|
2287
|
+
)
|
|
2288
|
+
|
|
2289
|
+
def build_reused(
|
|
2290
|
+
self,
|
|
2291
|
+
kind: SourceArtifactKind,
|
|
2292
|
+
*,
|
|
2293
|
+
_normalized_worker_authority: object | None = None,
|
|
2294
|
+
**identity: object,
|
|
2295
|
+
) -> MappedSource:
|
|
2296
|
+
"""Build imported monotonic segments with a linear source-map splice."""
|
|
2297
|
+
self._validate_normalized_worker_policy(
|
|
2298
|
+
kind,
|
|
2299
|
+
_normalized_worker_authority,
|
|
2300
|
+
)
|
|
2301
|
+
artifact, text = self._artifact(kind, identity, deferred=False)
|
|
2302
|
+
assert text is not None
|
|
2303
|
+
local = SourceMap(artifact, _coalesce_segments(self._segments))
|
|
2304
|
+
flattened = _compose_reused_source_maps(local, self._parent.source_map)
|
|
2305
|
+
lineage = (*self._parent.lineage, local)
|
|
2306
|
+
proof = _mint_trusted_text_proof(
|
|
2307
|
+
text,
|
|
2308
|
+
artifact.source_sha256,
|
|
2309
|
+
authority=_TRUSTED_TEXT_PROOF_AUTHORITY,
|
|
2310
|
+
)
|
|
2311
|
+
return MappedSource(
|
|
2312
|
+
text,
|
|
2313
|
+
artifact,
|
|
2314
|
+
flattened,
|
|
2315
|
+
lineage,
|
|
2316
|
+
local,
|
|
2317
|
+
_retained_fragments=tuple(self._parts),
|
|
2318
|
+
_transform_parent=self._parent,
|
|
2319
|
+
_trusted_text_proof=proof,
|
|
2320
|
+
_retained_authority=_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
2321
|
+
_normalized_worker_authority=_normalized_worker_authority,
|
|
2322
|
+
)
|
|
2323
|
+
|
|
2324
|
+
def build_canonical_reused(
|
|
2325
|
+
self,
|
|
2326
|
+
kind: SourceArtifactKind,
|
|
2327
|
+
*,
|
|
2328
|
+
_normalized_worker_authority: object | None = None,
|
|
2329
|
+
**identity: object,
|
|
2330
|
+
) -> MappedSource:
|
|
2331
|
+
"""Build a final source whose retained state owns no prior generation."""
|
|
2332
|
+
if not isinstance(self._parent, _DeferredMappedSource):
|
|
2333
|
+
raise ValueError("canonical retained build requires a deferred parent")
|
|
2334
|
+
self._validate_normalized_worker_policy(
|
|
2335
|
+
kind,
|
|
2336
|
+
_normalized_worker_authority,
|
|
2337
|
+
)
|
|
2338
|
+
artifact, text = self._artifact(kind, identity, deferred=False)
|
|
2339
|
+
assert text is not None
|
|
2340
|
+
local = SourceMap(artifact, _coalesce_segments(self._segments))
|
|
2341
|
+
flattened = _compose_reused_source_maps(local, self._parent.source_map)
|
|
2342
|
+
lineage = (*self._parent.lineage, local)
|
|
2343
|
+
retained = tuple(
|
|
2344
|
+
_RetainedTextFragment(
|
|
2345
|
+
fragment.generated,
|
|
2346
|
+
fragment.text,
|
|
2347
|
+
line_endings=fragment.line_endings,
|
|
2348
|
+
contains_nul=fragment.contains_nul,
|
|
2349
|
+
)
|
|
2350
|
+
for fragment in self._parts
|
|
2351
|
+
)
|
|
2352
|
+
parent = _detached_retained_source(self._parent)
|
|
2353
|
+
proof = _mint_trusted_text_proof(
|
|
2354
|
+
text,
|
|
2355
|
+
artifact.source_sha256,
|
|
2356
|
+
authority=_TRUSTED_TEXT_PROOF_AUTHORITY,
|
|
2357
|
+
)
|
|
2358
|
+
return MappedSource(
|
|
2359
|
+
text,
|
|
2360
|
+
artifact,
|
|
2361
|
+
flattened,
|
|
2362
|
+
lineage,
|
|
2363
|
+
local,
|
|
2364
|
+
_retained_fragments=retained,
|
|
2365
|
+
_transform_parent=parent,
|
|
2366
|
+
_trusted_text_proof=proof,
|
|
2367
|
+
_retained_authority=_RETAINED_CONSTRUCTION_AUTHORITY,
|
|
2368
|
+
_normalized_worker_authority=_normalized_worker_authority,
|
|
2369
|
+
)
|
|
2370
|
+
|
|
2371
|
+
|
|
2372
|
+
class LineIndex:
|
|
2373
|
+
"""Map zero-based Unicode code-point offsets to one-based line/columns."""
|
|
2374
|
+
|
|
2375
|
+
def __init__(self, source: str) -> None:
|
|
2376
|
+
_require_str(source, "source")
|
|
2377
|
+
self._length = len(source)
|
|
2378
|
+
self._line_starts = (0, *(i + 1 for i, char in enumerate(source) if char == "\n"))
|
|
2379
|
+
|
|
2380
|
+
def offset_to_line_column(self, offset: int) -> tuple[int, int]:
|
|
2381
|
+
if type(offset) is not int or not 0 <= offset <= self._length:
|
|
2382
|
+
raise ValueError("offset must be within the source")
|
|
2383
|
+
line_index = bisect_right(self._line_starts, offset) - 1
|
|
2384
|
+
return line_index + 1, offset - self._line_starts[line_index] + 1
|
|
2385
|
+
|
|
2386
|
+
def line_column_to_offset(self, line: int, column: int) -> int:
|
|
2387
|
+
if type(line) is not int or not 1 <= line <= len(self._line_starts):
|
|
2388
|
+
raise ValueError("line must be within the source")
|
|
2389
|
+
if type(column) is not int or column < 1:
|
|
2390
|
+
raise ValueError("column must be positive")
|
|
2391
|
+
line_index = line - 1
|
|
2392
|
+
start = self._line_starts[line_index]
|
|
2393
|
+
next_start = (
|
|
2394
|
+
self._line_starts[line_index + 1]
|
|
2395
|
+
if line_index + 1 < len(self._line_starts)
|
|
2396
|
+
else self._length + 1
|
|
2397
|
+
)
|
|
2398
|
+
if start + column > next_start:
|
|
2399
|
+
raise ValueError("column must be within the source line")
|
|
2400
|
+
return start + column - 1
|
|
2401
|
+
|
|
2402
|
+
def line_range(self, line: int) -> SourceSpan:
|
|
2403
|
+
if type(line) is not int or not 1 <= line <= len(self._line_starts):
|
|
2404
|
+
raise ValueError("line must be within the source")
|
|
2405
|
+
start = self._line_starts[line - 1]
|
|
2406
|
+
end = (
|
|
2407
|
+
self._line_starts[line]
|
|
2408
|
+
if line < len(self._line_starts)
|
|
2409
|
+
else self._length
|
|
2410
|
+
)
|
|
2411
|
+
return SourceSpan(start, end)
|