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,1046 @@
|
|
|
1
|
+
"""Bounded, typed normalization for deterministic and 1C BSL diagnostics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import dataclass, field as dataclass_field, replace
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from hashlib import sha256
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
from onec_runtime.bsl.source_maps import (
|
|
12
|
+
LineIndex,
|
|
13
|
+
MappedSource,
|
|
14
|
+
MappingRelation,
|
|
15
|
+
SourceMapSegment,
|
|
16
|
+
SourceSpan,
|
|
17
|
+
SourceUnitRef,
|
|
18
|
+
source_sha256,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_PLATFORM_DIAGNOSTIC_LIMIT = 4096
|
|
23
|
+
_MODULE_LOCATOR_LIMIT = 512
|
|
24
|
+
_MODULE_COMPONENT_LIMIT = 32
|
|
25
|
+
_MODULE_IDENTIFIER = r"[A-Za-zА-Яа-яЁё_][0-9A-Za-zА-Яа-яЁё_]*"
|
|
26
|
+
_LOCATION_RE = re.compile(
|
|
27
|
+
rf"^\{{(?P<module><Неизвестный модуль>|Неизвестный модуль|{_MODULE_IDENTIFIER}(?:\.{_MODULE_IDENTIFIER})*)"
|
|
28
|
+
r"\((?P<line>[0-9]{1,10})\s*,\s*(?P<column>[0-9]{1,10})\)\}",
|
|
29
|
+
re.MULTILINE,
|
|
30
|
+
)
|
|
31
|
+
_WORKER_LINE_ONLY_LOCATION_RE = re.compile(
|
|
32
|
+
r"^\{(?P<module>ВнешняяОбработка\."
|
|
33
|
+
r"(?P<registration>OnecRuntime_[0-9a-f]{8}_[0-9a-f]{16})\."
|
|
34
|
+
r"МодульОбъекта)\((?P<line>[1-9][0-9]{0,9})\)\}",
|
|
35
|
+
re.IGNORECASE | re.MULTILINE,
|
|
36
|
+
)
|
|
37
|
+
_COMPILATION_MARKER_RE = re.compile(
|
|
38
|
+
r"(?:[ \t]+|\r?\n[ \t]*)"
|
|
39
|
+
r"\[ОшибкаКомпиляцииВстроенногоЯзыка\]"
|
|
40
|
+
r"[ \t]*(?:\r?\n)?\Z",
|
|
41
|
+
)
|
|
42
|
+
_NESTED_COMPILE_CAUSE_PREFIX_RE = re.compile(
|
|
43
|
+
r"(?:^|\r?\n)[ \t]*по причине:[ \t]*\r?\n\Z",
|
|
44
|
+
re.IGNORECASE,
|
|
45
|
+
)
|
|
46
|
+
_UNKNOWN_MODULES = frozenset({"<Неизвестный модуль>", "Неизвестный модуль"})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DiagnosticStage(StrEnum):
|
|
50
|
+
PARSING = "parsing"
|
|
51
|
+
LOWERING = "lowering"
|
|
52
|
+
COMPILATION = "compilation"
|
|
53
|
+
EXECUTION = "execution"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DiagnosticCoordinateSpace(StrEnum):
|
|
57
|
+
UNKNOWN = "unknown"
|
|
58
|
+
EXECUTED_BSL = "executed_bsl"
|
|
59
|
+
HOST_MODULE = "host_module"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MappingConfidence(StrEnum):
|
|
63
|
+
EXACT = "exact"
|
|
64
|
+
NEAREST = "nearest"
|
|
65
|
+
SYNTHETIC = "synthetic"
|
|
66
|
+
UNKNOWN = "unknown"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class _PrivatePlatformEvidence:
|
|
70
|
+
"""Bounded expert prose that generic serializers cannot traverse."""
|
|
71
|
+
|
|
72
|
+
__slots__ = ("_text",)
|
|
73
|
+
|
|
74
|
+
def __init__(self, text: str) -> None:
|
|
75
|
+
self._text = text
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def text(self) -> str:
|
|
79
|
+
return self._text
|
|
80
|
+
|
|
81
|
+
def __repr__(self) -> str:
|
|
82
|
+
return "<redacted platform diagnostic>"
|
|
83
|
+
|
|
84
|
+
def __deepcopy__(self, memo: dict[int, object]) -> object:
|
|
85
|
+
return _REDACTED_PLATFORM_EVIDENCE
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class _RedactedPlatformEvidence:
|
|
89
|
+
__slots__ = ()
|
|
90
|
+
|
|
91
|
+
def __repr__(self) -> str:
|
|
92
|
+
return "<redacted platform diagnostic>"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
_REDACTED_PLATFORM_EVIDENCE = _RedactedPlatformEvidence()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True, slots=True)
|
|
99
|
+
class ParsedPlatformDiagnostic:
|
|
100
|
+
_platform_evidence: _PrivatePlatformEvidence = dataclass_field(repr=False)
|
|
101
|
+
platform_diagnostic_sha256: str
|
|
102
|
+
platform_diagnostic_truncated: bool
|
|
103
|
+
platform_diagnostic_redacted: bool
|
|
104
|
+
line: int | None
|
|
105
|
+
column: int | None
|
|
106
|
+
coordinate_space: DiagnosticCoordinateSpace
|
|
107
|
+
module_name: str | None
|
|
108
|
+
additional_locations: tuple[tuple[int, int], ...]
|
|
109
|
+
has_compilation_marker: bool
|
|
110
|
+
locations: tuple[PlatformDiagnosticLocation, ...]
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def platform_diagnostic(self) -> str:
|
|
114
|
+
return self._platform_evidence.text
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass(frozen=True, slots=True)
|
|
118
|
+
class VisibleSourceLocation:
|
|
119
|
+
source_unit: SourceUnitRef
|
|
120
|
+
line: int
|
|
121
|
+
column: int
|
|
122
|
+
span: SourceSpan
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class VisibleSourceContext:
|
|
126
|
+
"""Hash-fenced visible line indexes that retain no source text."""
|
|
127
|
+
|
|
128
|
+
__slots__ = ("_indices",)
|
|
129
|
+
|
|
130
|
+
def __init__(self, sources: Mapping[SourceUnitRef, str]) -> None:
|
|
131
|
+
if not isinstance(sources, Mapping):
|
|
132
|
+
raise ValueError("visible sources must be a mapping")
|
|
133
|
+
indices: dict[
|
|
134
|
+
tuple[object, str, int],
|
|
135
|
+
tuple[str, LineIndex],
|
|
136
|
+
] = {}
|
|
137
|
+
for unit, source in sources.items():
|
|
138
|
+
if not isinstance(unit, SourceUnitRef) or type(source) is not str:
|
|
139
|
+
raise ValueError("visible sources require SourceUnitRef and string pairs")
|
|
140
|
+
indices[_visible_unit_key(unit)] = (
|
|
141
|
+
source_sha256(source),
|
|
142
|
+
LineIndex(source),
|
|
143
|
+
)
|
|
144
|
+
self._indices = indices
|
|
145
|
+
|
|
146
|
+
def line_column(
|
|
147
|
+
self,
|
|
148
|
+
unit: SourceUnitRef,
|
|
149
|
+
offset: int,
|
|
150
|
+
) -> tuple[int, int] | None:
|
|
151
|
+
entry = self._indices.get(_visible_unit_key(unit))
|
|
152
|
+
if entry is None or entry[0] != unit.source_sha256:
|
|
153
|
+
return None
|
|
154
|
+
try:
|
|
155
|
+
return entry[1].offset_to_line_column(offset)
|
|
156
|
+
except ValueError:
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
def line_range(
|
|
160
|
+
self,
|
|
161
|
+
unit: SourceUnitRef,
|
|
162
|
+
line: int,
|
|
163
|
+
) -> SourceSpan | None:
|
|
164
|
+
entry = self._indices.get(_visible_unit_key(unit))
|
|
165
|
+
if entry is None or entry[0] != unit.source_sha256:
|
|
166
|
+
return None
|
|
167
|
+
try:
|
|
168
|
+
return entry[1].line_range(line)
|
|
169
|
+
except ValueError:
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
def __repr__(self) -> str:
|
|
173
|
+
return f"VisibleSourceContext(entries={len(self._indices)}, source=<redacted>)"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass(frozen=True, slots=True)
|
|
177
|
+
class WorkerArtifactPlatformLocation:
|
|
178
|
+
registration_name: str
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass(frozen=True, slots=True)
|
|
182
|
+
class PlatformDiagnosticLocation:
|
|
183
|
+
module_name: str
|
|
184
|
+
module_components: tuple[str, ...]
|
|
185
|
+
worker_artifact_location: WorkerArtifactPlatformLocation | None
|
|
186
|
+
line: int
|
|
187
|
+
column: int | None
|
|
188
|
+
coordinate_space: DiagnosticCoordinateSpace
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
192
|
+
class WorkerDiagnosticArtifact:
|
|
193
|
+
"""Private, manifest-fenced source-map input for Worker diagnostics."""
|
|
194
|
+
|
|
195
|
+
logical_name: str
|
|
196
|
+
revision: int
|
|
197
|
+
artifact_sha256: str
|
|
198
|
+
registration_name: str
|
|
199
|
+
manifest_sha256: str
|
|
200
|
+
source_map_sha256: str
|
|
201
|
+
mapped_source: MappedSource = dataclass_field(repr=False)
|
|
202
|
+
visible_source_context: VisibleSourceContext | None = dataclass_field(
|
|
203
|
+
default=None,
|
|
204
|
+
repr=False,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def __post_init__(self) -> None:
|
|
208
|
+
if (
|
|
209
|
+
not isinstance(self.logical_name, str)
|
|
210
|
+
or not self.logical_name
|
|
211
|
+
or type(self.revision) is not int
|
|
212
|
+
or self.revision < 0
|
|
213
|
+
or re.fullmatch(r"[0-9a-f]{64}", self.artifact_sha256) is None
|
|
214
|
+
or not isinstance(self.registration_name, str)
|
|
215
|
+
or not self.registration_name
|
|
216
|
+
or re.fullmatch(r"[0-9a-f]{64}", self.manifest_sha256) is None
|
|
217
|
+
or re.fullmatch(r"[0-9a-f]{64}", self.source_map_sha256) is None
|
|
218
|
+
or not isinstance(self.mapped_source, MappedSource)
|
|
219
|
+
or self.source_map_sha256 != self.mapped_source.source_map_sha256
|
|
220
|
+
or (
|
|
221
|
+
self.visible_source_context is not None
|
|
222
|
+
and not isinstance(self.visible_source_context, VisibleSourceContext)
|
|
223
|
+
)
|
|
224
|
+
):
|
|
225
|
+
raise ValueError("worker diagnostic artifact source map identity is invalid")
|
|
226
|
+
|
|
227
|
+
def __repr__(self) -> str:
|
|
228
|
+
return (
|
|
229
|
+
"WorkerDiagnosticArtifact("
|
|
230
|
+
f"logical_name={self.logical_name!r}, revision={self.revision}, "
|
|
231
|
+
f"artifact_sha256={self.artifact_sha256!r}, source=<redacted>)"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@dataclass(frozen=True, slots=True)
|
|
236
|
+
class WorkerRuntimeFrameDiagnostic:
|
|
237
|
+
registration_name: str
|
|
238
|
+
logical_name: str | None
|
|
239
|
+
revision: int | None
|
|
240
|
+
artifact_sha256: str | None
|
|
241
|
+
mapping_confidence: MappingConfidence
|
|
242
|
+
source_unit: SourceUnitRef | None = None
|
|
243
|
+
visible_location: VisibleSourceLocation | None = None
|
|
244
|
+
related_visible_span: SourceSpan | None = None
|
|
245
|
+
lowered_location: LoweredSourceLocation | None = None
|
|
246
|
+
synthetic_region: str | None = None
|
|
247
|
+
dependency_anchor: SourceSpan | None = None
|
|
248
|
+
method_anchor: SourceSpan | None = None
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@dataclass(frozen=True, slots=True)
|
|
252
|
+
class LoweredSourceLocation:
|
|
253
|
+
line: int
|
|
254
|
+
column: int
|
|
255
|
+
offset: int
|
|
256
|
+
span: SourceSpan
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@dataclass(frozen=True, slots=True)
|
|
260
|
+
class NormalizedDiagnostic:
|
|
261
|
+
diagnostic_id: str
|
|
262
|
+
runtime_summary: str
|
|
263
|
+
stage: DiagnosticStage
|
|
264
|
+
mapping_confidence: MappingConfidence
|
|
265
|
+
code: str | None = None
|
|
266
|
+
source_unit: SourceUnitRef | None = None
|
|
267
|
+
visible_location: VisibleSourceLocation | None = None
|
|
268
|
+
related_visible_span: SourceSpan | None = None
|
|
269
|
+
lowered_location: LoweredSourceLocation | None = None
|
|
270
|
+
synthetic_region: str | None = None
|
|
271
|
+
_platform_evidence: _PrivatePlatformEvidence | None = dataclass_field(
|
|
272
|
+
default=None,
|
|
273
|
+
repr=False,
|
|
274
|
+
)
|
|
275
|
+
platform_diagnostic_sha256: str | None = None
|
|
276
|
+
platform_diagnostic_truncated: bool = False
|
|
277
|
+
platform_diagnostic_redacted: bool = False
|
|
278
|
+
execution_artifact_sha256: str | None = None
|
|
279
|
+
source_map_sha256: str | None = None
|
|
280
|
+
dependency_anchor: SourceSpan | None = None
|
|
281
|
+
method_anchor: SourceSpan | None = None
|
|
282
|
+
worker_frames: tuple[WorkerRuntimeFrameDiagnostic, ...] = ()
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def platform_diagnostic(self) -> str | None:
|
|
286
|
+
return (
|
|
287
|
+
self._platform_evidence.text
|
|
288
|
+
if self._platform_evidence is not None
|
|
289
|
+
else None
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class PlatformCoordinateCodec:
|
|
294
|
+
"""Decode one-based 1C coordinates against one exact artifact string."""
|
|
295
|
+
|
|
296
|
+
def __init__(self, executed_source: str) -> None:
|
|
297
|
+
self._index = LineIndex(executed_source)
|
|
298
|
+
|
|
299
|
+
def to_offset(self, line: int, column: int) -> int | None:
|
|
300
|
+
try:
|
|
301
|
+
return self._index.line_column_to_offset(line, column)
|
|
302
|
+
except ValueError:
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def parse_platform_diagnostic(message: str) -> ParsedPlatformDiagnostic:
|
|
307
|
+
"""Structurally parse allowlisted 1C locations from bounded diagnostic text."""
|
|
308
|
+
if type(message) is not str:
|
|
309
|
+
raise ValueError("platform diagnostic must be a string")
|
|
310
|
+
bounded = message[:_PLATFORM_DIAGNOSTIC_LIMIT]
|
|
311
|
+
digest = sha256(message.encode("utf-8")).hexdigest()
|
|
312
|
+
accepted: list[tuple[re.Match[str], PlatformDiagnosticLocation]] = []
|
|
313
|
+
for match in _LOCATION_RE.finditer(bounded):
|
|
314
|
+
module = match.group("module")
|
|
315
|
+
components = (module,) if module in _UNKNOWN_MODULES else tuple(module.split("."))
|
|
316
|
+
if (
|
|
317
|
+
len(module) > _MODULE_LOCATOR_LIMIT
|
|
318
|
+
or len(components) > _MODULE_COMPONENT_LIMIT
|
|
319
|
+
):
|
|
320
|
+
continue
|
|
321
|
+
accepted.append(
|
|
322
|
+
(
|
|
323
|
+
match,
|
|
324
|
+
PlatformDiagnosticLocation(
|
|
325
|
+
module,
|
|
326
|
+
components,
|
|
327
|
+
_parse_worker_artifact_location(components),
|
|
328
|
+
int(match.group("line")),
|
|
329
|
+
int(match.group("column")),
|
|
330
|
+
(
|
|
331
|
+
DiagnosticCoordinateSpace.EXECUTED_BSL
|
|
332
|
+
if module in _UNKNOWN_MODULES
|
|
333
|
+
else DiagnosticCoordinateSpace.HOST_MODULE
|
|
334
|
+
),
|
|
335
|
+
),
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
line_only_locations = tuple(
|
|
339
|
+
(
|
|
340
|
+
match.start(),
|
|
341
|
+
PlatformDiagnosticLocation(
|
|
342
|
+
match.group("module"),
|
|
343
|
+
tuple(match.group("module").split(".")),
|
|
344
|
+
WorkerArtifactPlatformLocation(match.group("registration")),
|
|
345
|
+
int(match.group("line")),
|
|
346
|
+
None,
|
|
347
|
+
DiagnosticCoordinateSpace.HOST_MODULE,
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
for match in _WORKER_LINE_ONLY_LOCATION_RE.finditer(bounded)
|
|
351
|
+
)
|
|
352
|
+
locations = tuple(
|
|
353
|
+
location
|
|
354
|
+
for _offset, location in sorted(
|
|
355
|
+
(
|
|
356
|
+
*((match.start(), location) for match, location in accepted),
|
|
357
|
+
*line_only_locations,
|
|
358
|
+
),
|
|
359
|
+
key=lambda item: item[0],
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
terminal_compilation = _COMPILATION_MARKER_RE.search(bounded) is not None
|
|
363
|
+
primary = accepted[0][0] if accepted and accepted[0][0].start() == 0 else None
|
|
364
|
+
if (
|
|
365
|
+
primary is None
|
|
366
|
+
and terminal_compilation
|
|
367
|
+
and len(accepted) == 1
|
|
368
|
+
and (
|
|
369
|
+
accepted[0][1].coordinate_space is DiagnosticCoordinateSpace.EXECUTED_BSL
|
|
370
|
+
or accepted[0][1].worker_artifact_location is not None
|
|
371
|
+
)
|
|
372
|
+
and _NESTED_COMPILE_CAUSE_PREFIX_RE.search(
|
|
373
|
+
bounded[: accepted[0][0].start()]
|
|
374
|
+
)
|
|
375
|
+
is not None
|
|
376
|
+
):
|
|
377
|
+
primary = accepted[0][0]
|
|
378
|
+
if primary is None:
|
|
379
|
+
module_name = None
|
|
380
|
+
line = None
|
|
381
|
+
column = None
|
|
382
|
+
space = DiagnosticCoordinateSpace.UNKNOWN
|
|
383
|
+
additional: tuple[tuple[int, int], ...] = ()
|
|
384
|
+
else:
|
|
385
|
+
module_name = primary.group("module")
|
|
386
|
+
line = int(primary.group("line"))
|
|
387
|
+
column = int(primary.group("column"))
|
|
388
|
+
space = (
|
|
389
|
+
DiagnosticCoordinateSpace.EXECUTED_BSL
|
|
390
|
+
if module_name in _UNKNOWN_MODULES
|
|
391
|
+
else DiagnosticCoordinateSpace.HOST_MODULE
|
|
392
|
+
)
|
|
393
|
+
additional = tuple(
|
|
394
|
+
(int(match.group("line")), int(match.group("column")))
|
|
395
|
+
for match, _location in accepted[1:]
|
|
396
|
+
)
|
|
397
|
+
return ParsedPlatformDiagnostic(
|
|
398
|
+
_PrivatePlatformEvidence(bounded),
|
|
399
|
+
digest,
|
|
400
|
+
len(message) > _PLATFORM_DIAGNOSTIC_LIMIT,
|
|
401
|
+
False,
|
|
402
|
+
line,
|
|
403
|
+
column,
|
|
404
|
+
space,
|
|
405
|
+
module_name,
|
|
406
|
+
additional,
|
|
407
|
+
primary is not None and terminal_compilation,
|
|
408
|
+
locations,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def remap_worker_stage_diagnostic(
|
|
413
|
+
parsed: ParsedPlatformDiagnostic,
|
|
414
|
+
*,
|
|
415
|
+
artifact_sha256: str,
|
|
416
|
+
phase: str,
|
|
417
|
+
candidate_manifest_sha256: str,
|
|
418
|
+
candidate_artifacts: tuple[WorkerDiagnosticArtifact, ...],
|
|
419
|
+
) -> NormalizedDiagnostic:
|
|
420
|
+
"""Map a stage failure only through one exact candidate artifact."""
|
|
421
|
+
_validate_worker_diagnostic_request(
|
|
422
|
+
parsed,
|
|
423
|
+
candidate_manifest_sha256,
|
|
424
|
+
candidate_artifacts,
|
|
425
|
+
)
|
|
426
|
+
if (
|
|
427
|
+
re.fullmatch(r"[0-9a-f]{64}", artifact_sha256) is None
|
|
428
|
+
or phase not in {"decode", "upload", "connect", "registration-result", "create"}
|
|
429
|
+
):
|
|
430
|
+
raise ValueError("worker artifact stage identity is invalid")
|
|
431
|
+
stage = (
|
|
432
|
+
DiagnosticStage.EXECUTION
|
|
433
|
+
if phase == "create" and not parsed.has_compilation_marker
|
|
434
|
+
else DiagnosticStage.COMPILATION
|
|
435
|
+
)
|
|
436
|
+
matches = tuple(
|
|
437
|
+
artifact
|
|
438
|
+
for artifact in candidate_artifacts
|
|
439
|
+
if artifact.manifest_sha256 == candidate_manifest_sha256
|
|
440
|
+
and artifact.artifact_sha256 == artifact_sha256
|
|
441
|
+
)
|
|
442
|
+
if len(matches) != 1:
|
|
443
|
+
return _unmapped_worker_diagnostic(
|
|
444
|
+
parsed,
|
|
445
|
+
stage=stage,
|
|
446
|
+
code="worker_artifact_identity_unmapped",
|
|
447
|
+
identity=(candidate_manifest_sha256, artifact_sha256, phase),
|
|
448
|
+
)
|
|
449
|
+
artifact = matches[0]
|
|
450
|
+
locations = _worker_stage_locations(parsed, artifact)
|
|
451
|
+
if not locations:
|
|
452
|
+
return _unmapped_worker_diagnostic(
|
|
453
|
+
parsed,
|
|
454
|
+
stage=stage,
|
|
455
|
+
code="worker_artifact_location_unmapped",
|
|
456
|
+
identity=(candidate_manifest_sha256, artifact_sha256, phase),
|
|
457
|
+
)
|
|
458
|
+
if len(locations) != 1:
|
|
459
|
+
return _unmapped_worker_diagnostic(
|
|
460
|
+
parsed,
|
|
461
|
+
stage=stage,
|
|
462
|
+
code="worker_artifact_location_ambiguous",
|
|
463
|
+
identity=(candidate_manifest_sha256, artifact_sha256, phase),
|
|
464
|
+
)
|
|
465
|
+
location = locations[0]
|
|
466
|
+
diagnostic = remap_platform_diagnostic(
|
|
467
|
+
replace(
|
|
468
|
+
parsed,
|
|
469
|
+
line=location.line,
|
|
470
|
+
column=location.column,
|
|
471
|
+
coordinate_space=DiagnosticCoordinateSpace.EXECUTED_BSL,
|
|
472
|
+
module_name=location.module_name,
|
|
473
|
+
),
|
|
474
|
+
artifact.mapped_source,
|
|
475
|
+
stage=stage,
|
|
476
|
+
visible_source_context=artifact.visible_source_context,
|
|
477
|
+
)
|
|
478
|
+
return _with_dependency_binding(diagnostic, artifact.mapped_source)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def remap_worker_runtime_diagnostic(
|
|
482
|
+
parsed: ParsedPlatformDiagnostic,
|
|
483
|
+
*,
|
|
484
|
+
pinned_manifest_sha256: str,
|
|
485
|
+
pinned_artifacts: tuple[WorkerDiagnosticArtifact, ...],
|
|
486
|
+
) -> NormalizedDiagnostic:
|
|
487
|
+
"""Resolve every Worker stack frame through one immutable operation pin."""
|
|
488
|
+
_validate_worker_diagnostic_request(
|
|
489
|
+
parsed,
|
|
490
|
+
pinned_manifest_sha256,
|
|
491
|
+
pinned_artifacts,
|
|
492
|
+
)
|
|
493
|
+
frames: list[WorkerRuntimeFrameDiagnostic] = []
|
|
494
|
+
for location in parsed.locations:
|
|
495
|
+
worker_location = location.worker_artifact_location
|
|
496
|
+
if worker_location is None:
|
|
497
|
+
if location.module_name in _UNKNOWN_MODULES:
|
|
498
|
+
frames.append(
|
|
499
|
+
WorkerRuntimeFrameDiagnostic(
|
|
500
|
+
location.module_name,
|
|
501
|
+
None,
|
|
502
|
+
None,
|
|
503
|
+
None,
|
|
504
|
+
MappingConfidence.UNKNOWN,
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
continue
|
|
508
|
+
matches = tuple(
|
|
509
|
+
artifact
|
|
510
|
+
for artifact in pinned_artifacts
|
|
511
|
+
if artifact.manifest_sha256 == pinned_manifest_sha256
|
|
512
|
+
and worker_location.registration_name.casefold()
|
|
513
|
+
== artifact.registration_name.casefold()
|
|
514
|
+
)
|
|
515
|
+
if len(matches) != 1:
|
|
516
|
+
frames.append(
|
|
517
|
+
WorkerRuntimeFrameDiagnostic(
|
|
518
|
+
worker_location.registration_name,
|
|
519
|
+
None,
|
|
520
|
+
None,
|
|
521
|
+
None,
|
|
522
|
+
MappingConfidence.UNKNOWN,
|
|
523
|
+
)
|
|
524
|
+
)
|
|
525
|
+
continue
|
|
526
|
+
artifact = matches[0]
|
|
527
|
+
frames.append(
|
|
528
|
+
_worker_runtime_frame(
|
|
529
|
+
location,
|
|
530
|
+
artifact,
|
|
531
|
+
worker_location.registration_name,
|
|
532
|
+
)
|
|
533
|
+
)
|
|
534
|
+
if not frames:
|
|
535
|
+
return _unmapped_worker_diagnostic(
|
|
536
|
+
parsed,
|
|
537
|
+
stage=DiagnosticStage.EXECUTION,
|
|
538
|
+
code="worker_runtime_frame_unmapped",
|
|
539
|
+
identity=(pinned_manifest_sha256,),
|
|
540
|
+
)
|
|
541
|
+
primary = frames[0]
|
|
542
|
+
source_map_sha256 = None
|
|
543
|
+
execution_artifact_sha256 = None
|
|
544
|
+
if primary.artifact_sha256 is not None:
|
|
545
|
+
artifact = next(
|
|
546
|
+
(
|
|
547
|
+
item
|
|
548
|
+
for item in pinned_artifacts
|
|
549
|
+
if item.manifest_sha256 == pinned_manifest_sha256
|
|
550
|
+
and item.artifact_sha256 == primary.artifact_sha256
|
|
551
|
+
and item.registration_name.casefold()
|
|
552
|
+
== primary.registration_name.casefold()
|
|
553
|
+
),
|
|
554
|
+
None,
|
|
555
|
+
)
|
|
556
|
+
if artifact is not None:
|
|
557
|
+
source_map_sha256 = artifact.mapped_source.source_map_sha256
|
|
558
|
+
execution_artifact_sha256 = artifact.mapped_source.artifact.source_sha256
|
|
559
|
+
return NormalizedDiagnostic(
|
|
560
|
+
diagnostic_id=_worker_diagnostic_id(
|
|
561
|
+
DiagnosticStage.EXECUTION,
|
|
562
|
+
"worker_runtime_frames",
|
|
563
|
+
parsed.platform_diagnostic_sha256,
|
|
564
|
+
(pinned_manifest_sha256, *(frame.registration_name for frame in frames)),
|
|
565
|
+
),
|
|
566
|
+
runtime_summary=_summary(DiagnosticStage.EXECUTION),
|
|
567
|
+
stage=DiagnosticStage.EXECUTION,
|
|
568
|
+
mapping_confidence=primary.mapping_confidence,
|
|
569
|
+
code=(
|
|
570
|
+
"dependency_binding"
|
|
571
|
+
if primary.synthetic_region == "dependency_binding"
|
|
572
|
+
else (
|
|
573
|
+
"worker_runtime_frame_unmapped"
|
|
574
|
+
if primary.mapping_confidence is MappingConfidence.UNKNOWN
|
|
575
|
+
else None
|
|
576
|
+
)
|
|
577
|
+
),
|
|
578
|
+
source_unit=primary.source_unit,
|
|
579
|
+
visible_location=primary.visible_location,
|
|
580
|
+
related_visible_span=primary.related_visible_span,
|
|
581
|
+
lowered_location=primary.lowered_location,
|
|
582
|
+
synthetic_region=primary.synthetic_region,
|
|
583
|
+
_platform_evidence=parsed._platform_evidence,
|
|
584
|
+
platform_diagnostic_sha256=parsed.platform_diagnostic_sha256,
|
|
585
|
+
platform_diagnostic_truncated=parsed.platform_diagnostic_truncated,
|
|
586
|
+
platform_diagnostic_redacted=parsed.platform_diagnostic_redacted,
|
|
587
|
+
execution_artifact_sha256=execution_artifact_sha256,
|
|
588
|
+
source_map_sha256=source_map_sha256,
|
|
589
|
+
dependency_anchor=primary.dependency_anchor,
|
|
590
|
+
method_anchor=primary.method_anchor,
|
|
591
|
+
worker_frames=tuple(frames),
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def remap_platform_diagnostic(
|
|
596
|
+
parsed: ParsedPlatformDiagnostic,
|
|
597
|
+
executed: MappedSource,
|
|
598
|
+
*,
|
|
599
|
+
stage: DiagnosticStage,
|
|
600
|
+
visible_source_context: VisibleSourceContext | None = None,
|
|
601
|
+
) -> NormalizedDiagnostic:
|
|
602
|
+
if not isinstance(parsed, ParsedPlatformDiagnostic):
|
|
603
|
+
raise ValueError("parsed must be a ParsedPlatformDiagnostic")
|
|
604
|
+
if not isinstance(executed, MappedSource):
|
|
605
|
+
raise ValueError("executed must be a MappedSource")
|
|
606
|
+
if type(stage) is not DiagnosticStage:
|
|
607
|
+
raise ValueError("stage must be a DiagnosticStage")
|
|
608
|
+
if visible_source_context is not None and not isinstance(
|
|
609
|
+
visible_source_context,
|
|
610
|
+
VisibleSourceContext,
|
|
611
|
+
):
|
|
612
|
+
raise ValueError("visible_source_context must be a VisibleSourceContext")
|
|
613
|
+
effective_stage = (
|
|
614
|
+
DiagnosticStage.COMPILATION if parsed.has_compilation_marker else stage
|
|
615
|
+
)
|
|
616
|
+
offset = None
|
|
617
|
+
if (
|
|
618
|
+
parsed.coordinate_space is DiagnosticCoordinateSpace.EXECUTED_BSL
|
|
619
|
+
and parsed.line is not None
|
|
620
|
+
and parsed.column is not None
|
|
621
|
+
):
|
|
622
|
+
offset = PlatformCoordinateCodec(executed.text).to_offset(
|
|
623
|
+
parsed.line,
|
|
624
|
+
parsed.column,
|
|
625
|
+
)
|
|
626
|
+
mapping = _map_executed_offset(executed, offset, visible_source_context)
|
|
627
|
+
lowered = None
|
|
628
|
+
if offset is not None and parsed.line is not None and parsed.column is not None:
|
|
629
|
+
width = 0 if offset == len(executed.text) else 1
|
|
630
|
+
lowered = LoweredSourceLocation(
|
|
631
|
+
parsed.line,
|
|
632
|
+
parsed.column,
|
|
633
|
+
offset,
|
|
634
|
+
SourceSpan(offset, offset + width),
|
|
635
|
+
)
|
|
636
|
+
diagnostic_id = _diagnostic_id(
|
|
637
|
+
effective_stage,
|
|
638
|
+
None,
|
|
639
|
+
parsed.platform_diagnostic_sha256,
|
|
640
|
+
executed,
|
|
641
|
+
offset,
|
|
642
|
+
)
|
|
643
|
+
return NormalizedDiagnostic(
|
|
644
|
+
diagnostic_id=diagnostic_id,
|
|
645
|
+
runtime_summary=_summary(effective_stage),
|
|
646
|
+
stage=effective_stage,
|
|
647
|
+
mapping_confidence=mapping.confidence,
|
|
648
|
+
source_unit=mapping.source_unit,
|
|
649
|
+
visible_location=mapping.visible,
|
|
650
|
+
related_visible_span=mapping.related,
|
|
651
|
+
lowered_location=lowered,
|
|
652
|
+
synthetic_region=mapping.synthetic_region,
|
|
653
|
+
_platform_evidence=parsed._platform_evidence,
|
|
654
|
+
platform_diagnostic_sha256=parsed.platform_diagnostic_sha256,
|
|
655
|
+
platform_diagnostic_truncated=parsed.platform_diagnostic_truncated,
|
|
656
|
+
platform_diagnostic_redacted=parsed.platform_diagnostic_redacted,
|
|
657
|
+
execution_artifact_sha256=executed.artifact.source_sha256,
|
|
658
|
+
source_map_sha256=executed.source_map_sha256,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def normalize_source_error(
|
|
663
|
+
error: Exception,
|
|
664
|
+
source: MappedSource,
|
|
665
|
+
*,
|
|
666
|
+
stage: DiagnosticStage,
|
|
667
|
+
visible_source_context: VisibleSourceContext | None = None,
|
|
668
|
+
) -> NormalizedDiagnostic:
|
|
669
|
+
"""Normalize a deterministic source error without a platform round trip."""
|
|
670
|
+
if not isinstance(source, MappedSource):
|
|
671
|
+
raise ValueError("source must be a MappedSource")
|
|
672
|
+
if type(stage) is not DiagnosticStage:
|
|
673
|
+
raise ValueError("stage must be a DiagnosticStage")
|
|
674
|
+
if visible_source_context is not None and not isinstance(
|
|
675
|
+
visible_source_context,
|
|
676
|
+
VisibleSourceContext,
|
|
677
|
+
):
|
|
678
|
+
raise ValueError("visible_source_context must be a VisibleSourceContext")
|
|
679
|
+
span = getattr(error, "span", None)
|
|
680
|
+
code = getattr(error, "code", None)
|
|
681
|
+
if not isinstance(span, SourceSpan) or type(code) is not str or not code:
|
|
682
|
+
raise ValueError("source error must provide a SourceSpan and stable code")
|
|
683
|
+
offset = span.start if span.end <= len(source.text) else None
|
|
684
|
+
mapping = _map_executed_offset(source, offset, visible_source_context)
|
|
685
|
+
lowered = None
|
|
686
|
+
if offset is not None:
|
|
687
|
+
line, column = LineIndex(source.text).offset_to_line_column(offset)
|
|
688
|
+
lowered = LoweredSourceLocation(line, column, offset, span)
|
|
689
|
+
message_sha = sha256(str(error).encode("utf-8")).hexdigest()
|
|
690
|
+
return NormalizedDiagnostic(
|
|
691
|
+
diagnostic_id=_diagnostic_id(stage, code, message_sha, source, offset),
|
|
692
|
+
runtime_summary=_summary(stage),
|
|
693
|
+
stage=stage,
|
|
694
|
+
mapping_confidence=mapping.confidence,
|
|
695
|
+
code=code,
|
|
696
|
+
source_unit=mapping.source_unit,
|
|
697
|
+
visible_location=mapping.visible,
|
|
698
|
+
related_visible_span=mapping.related,
|
|
699
|
+
lowered_location=lowered,
|
|
700
|
+
synthetic_region=mapping.synthetic_region,
|
|
701
|
+
execution_artifact_sha256=source.artifact.source_sha256,
|
|
702
|
+
source_map_sha256=source.source_map_sha256,
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
@dataclass(frozen=True, slots=True)
|
|
707
|
+
class _MappedDiagnosticOffset:
|
|
708
|
+
confidence: MappingConfidence
|
|
709
|
+
source_unit: SourceUnitRef | None = None
|
|
710
|
+
visible: VisibleSourceLocation | None = None
|
|
711
|
+
related: SourceSpan | None = None
|
|
712
|
+
synthetic_region: str | None = None
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def _map_executed_offset(
|
|
716
|
+
source: MappedSource,
|
|
717
|
+
offset: int | None,
|
|
718
|
+
visible_source_context: VisibleSourceContext | None,
|
|
719
|
+
) -> _MappedDiagnosticOffset:
|
|
720
|
+
if offset is None:
|
|
721
|
+
return _MappedDiagnosticOffset(MappingConfidence.UNKNOWN)
|
|
722
|
+
try:
|
|
723
|
+
mapped = source.source_map.map_offset(offset)
|
|
724
|
+
except ValueError:
|
|
725
|
+
return _MappedDiagnosticOffset(MappingConfidence.UNKNOWN)
|
|
726
|
+
if (
|
|
727
|
+
mapped.relation is MappingRelation.EXACT
|
|
728
|
+
and mapped.unit is not None
|
|
729
|
+
and mapped.origin_span is not None
|
|
730
|
+
):
|
|
731
|
+
coordinates = (
|
|
732
|
+
visible_source_context.line_column(
|
|
733
|
+
mapped.unit,
|
|
734
|
+
mapped.origin_span.start,
|
|
735
|
+
)
|
|
736
|
+
if visible_source_context is not None
|
|
737
|
+
else None
|
|
738
|
+
)
|
|
739
|
+
if coordinates is None:
|
|
740
|
+
return _MappedDiagnosticOffset(
|
|
741
|
+
MappingConfidence.NEAREST,
|
|
742
|
+
mapped.unit,
|
|
743
|
+
related=mapped.origin_span,
|
|
744
|
+
)
|
|
745
|
+
return _MappedDiagnosticOffset(
|
|
746
|
+
MappingConfidence.EXACT,
|
|
747
|
+
mapped.unit,
|
|
748
|
+
VisibleSourceLocation(
|
|
749
|
+
mapped.unit,
|
|
750
|
+
coordinates[0],
|
|
751
|
+
coordinates[1],
|
|
752
|
+
mapped.origin_span,
|
|
753
|
+
),
|
|
754
|
+
)
|
|
755
|
+
if (
|
|
756
|
+
mapped.relation is MappingRelation.DERIVED
|
|
757
|
+
and mapped.unit is not None
|
|
758
|
+
and mapped.origin_span is not None
|
|
759
|
+
):
|
|
760
|
+
return _MappedDiagnosticOffset(
|
|
761
|
+
MappingConfidence.NEAREST,
|
|
762
|
+
mapped.unit,
|
|
763
|
+
related=mapped.origin_span,
|
|
764
|
+
)
|
|
765
|
+
if mapped.relation is MappingRelation.SYNTHETIC:
|
|
766
|
+
return _MappedDiagnosticOffset(
|
|
767
|
+
MappingConfidence.SYNTHETIC,
|
|
768
|
+
mapped.anchor_unit,
|
|
769
|
+
related=mapped.anchor_span,
|
|
770
|
+
synthetic_region=mapped.synthetic_region,
|
|
771
|
+
)
|
|
772
|
+
return _MappedDiagnosticOffset(MappingConfidence.UNKNOWN)
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def _validate_worker_diagnostic_request(
|
|
776
|
+
parsed: ParsedPlatformDiagnostic,
|
|
777
|
+
manifest_sha256: str,
|
|
778
|
+
artifacts: tuple[WorkerDiagnosticArtifact, ...],
|
|
779
|
+
) -> None:
|
|
780
|
+
if not isinstance(parsed, ParsedPlatformDiagnostic):
|
|
781
|
+
raise ValueError("parsed must be a ParsedPlatformDiagnostic")
|
|
782
|
+
if re.fullmatch(r"[0-9a-f]{64}", manifest_sha256) is None:
|
|
783
|
+
raise ValueError("worker diagnostic manifest identity is invalid")
|
|
784
|
+
if type(artifacts) is not tuple or any(
|
|
785
|
+
not isinstance(item, WorkerDiagnosticArtifact) for item in artifacts
|
|
786
|
+
):
|
|
787
|
+
raise ValueError("worker diagnostic artifacts must be an immutable tuple")
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
def _unmapped_worker_diagnostic(
|
|
791
|
+
parsed: ParsedPlatformDiagnostic,
|
|
792
|
+
*,
|
|
793
|
+
stage: DiagnosticStage,
|
|
794
|
+
code: str,
|
|
795
|
+
identity: tuple[str, ...],
|
|
796
|
+
) -> NormalizedDiagnostic:
|
|
797
|
+
return NormalizedDiagnostic(
|
|
798
|
+
diagnostic_id=_worker_diagnostic_id(
|
|
799
|
+
stage,
|
|
800
|
+
code,
|
|
801
|
+
parsed.platform_diagnostic_sha256,
|
|
802
|
+
identity,
|
|
803
|
+
),
|
|
804
|
+
runtime_summary=_summary(stage),
|
|
805
|
+
stage=stage,
|
|
806
|
+
mapping_confidence=MappingConfidence.UNKNOWN,
|
|
807
|
+
code=code,
|
|
808
|
+
_platform_evidence=parsed._platform_evidence,
|
|
809
|
+
platform_diagnostic_sha256=parsed.platform_diagnostic_sha256,
|
|
810
|
+
platform_diagnostic_truncated=parsed.platform_diagnostic_truncated,
|
|
811
|
+
platform_diagnostic_redacted=parsed.platform_diagnostic_redacted,
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def _with_dependency_binding(
|
|
816
|
+
diagnostic: NormalizedDiagnostic,
|
|
817
|
+
source: MappedSource,
|
|
818
|
+
) -> NormalizedDiagnostic:
|
|
819
|
+
lowered = diagnostic.lowered_location
|
|
820
|
+
if lowered is None:
|
|
821
|
+
return diagnostic
|
|
822
|
+
try:
|
|
823
|
+
mapped = source.source_map.map_offset(lowered.offset)
|
|
824
|
+
except ValueError:
|
|
825
|
+
return diagnostic
|
|
826
|
+
segment = _source_map_segment_at(source, lowered.offset)
|
|
827
|
+
region = None if segment is None else segment.synthetic_region
|
|
828
|
+
if region not in {
|
|
829
|
+
"worker_dependency_field",
|
|
830
|
+
"worker_dependency_alias_declaration",
|
|
831
|
+
"worker_dependency_alias_initializer",
|
|
832
|
+
}:
|
|
833
|
+
return diagnostic
|
|
834
|
+
dependency_anchor = (
|
|
835
|
+
mapped.origin_span
|
|
836
|
+
if mapped.relation is MappingRelation.DERIVED
|
|
837
|
+
else mapped.anchor_span
|
|
838
|
+
)
|
|
839
|
+
method_anchor = (
|
|
840
|
+
mapped.anchor_span
|
|
841
|
+
if mapped.relation is MappingRelation.DERIVED
|
|
842
|
+
else None
|
|
843
|
+
)
|
|
844
|
+
return replace(
|
|
845
|
+
diagnostic,
|
|
846
|
+
code="dependency_binding",
|
|
847
|
+
synthetic_region="dependency_binding",
|
|
848
|
+
dependency_anchor=dependency_anchor,
|
|
849
|
+
method_anchor=method_anchor,
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def _line_only_worker_position(
|
|
854
|
+
source: MappedSource,
|
|
855
|
+
line: int,
|
|
856
|
+
) -> tuple[int, int] | None:
|
|
857
|
+
"""Resolve a line-only Worker frame only through one exact code span."""
|
|
858
|
+
if type(line) is not int or line <= 0:
|
|
859
|
+
return None
|
|
860
|
+
index = LineIndex(source.text)
|
|
861
|
+
try:
|
|
862
|
+
line_start = index.line_column_to_offset(line, 1)
|
|
863
|
+
except ValueError:
|
|
864
|
+
return None
|
|
865
|
+
newline = source.text.find("\n", line_start)
|
|
866
|
+
line_end = len(source.text) if newline < 0 else newline
|
|
867
|
+
code_offsets = tuple(
|
|
868
|
+
offset
|
|
869
|
+
for offset in range(line_start, line_end)
|
|
870
|
+
if not source.text[offset].isspace()
|
|
871
|
+
)
|
|
872
|
+
if not code_offsets:
|
|
873
|
+
return None
|
|
874
|
+
first = code_offsets[0]
|
|
875
|
+
last = code_offsets[-1] + 1
|
|
876
|
+
spans = tuple(
|
|
877
|
+
segment
|
|
878
|
+
for segment in source.source_map.segments
|
|
879
|
+
if segment.generated.start < last and first < segment.generated.end
|
|
880
|
+
)
|
|
881
|
+
if (
|
|
882
|
+
len(spans) != 1
|
|
883
|
+
or spans[0].relation is not MappingRelation.EXACT
|
|
884
|
+
or not isinstance(spans[0].origin_ref, SourceUnitRef)
|
|
885
|
+
or spans[0].origin is None
|
|
886
|
+
):
|
|
887
|
+
return None
|
|
888
|
+
try:
|
|
889
|
+
mapped = source.source_map.map_offset(first)
|
|
890
|
+
except ValueError:
|
|
891
|
+
return None
|
|
892
|
+
if (
|
|
893
|
+
mapped.relation is not MappingRelation.EXACT
|
|
894
|
+
or mapped.unit is None
|
|
895
|
+
or mapped.origin_span is None
|
|
896
|
+
):
|
|
897
|
+
return None
|
|
898
|
+
return first - line_start + 1, first
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
def _worker_runtime_frame(
|
|
902
|
+
location: PlatformDiagnosticLocation,
|
|
903
|
+
artifact: WorkerDiagnosticArtifact,
|
|
904
|
+
observed_registration: str,
|
|
905
|
+
) -> WorkerRuntimeFrameDiagnostic:
|
|
906
|
+
if location.column is None:
|
|
907
|
+
normalized = _line_only_worker_position(
|
|
908
|
+
artifact.mapped_source,
|
|
909
|
+
location.line,
|
|
910
|
+
)
|
|
911
|
+
column = None if normalized is None else normalized[0]
|
|
912
|
+
offset = None if normalized is None else normalized[1]
|
|
913
|
+
else:
|
|
914
|
+
column = location.column
|
|
915
|
+
offset = PlatformCoordinateCodec(artifact.mapped_source.text).to_offset(
|
|
916
|
+
location.line,
|
|
917
|
+
location.column,
|
|
918
|
+
)
|
|
919
|
+
mapping = _map_executed_offset(
|
|
920
|
+
artifact.mapped_source,
|
|
921
|
+
offset,
|
|
922
|
+
artifact.visible_source_context,
|
|
923
|
+
)
|
|
924
|
+
lowered = None
|
|
925
|
+
if offset is not None and column is not None:
|
|
926
|
+
width = 0 if offset == len(artifact.mapped_source.text) else 1
|
|
927
|
+
lowered = LoweredSourceLocation(
|
|
928
|
+
location.line,
|
|
929
|
+
column,
|
|
930
|
+
offset,
|
|
931
|
+
SourceSpan(offset, offset + width),
|
|
932
|
+
)
|
|
933
|
+
dependency_anchor = None
|
|
934
|
+
method_anchor = None
|
|
935
|
+
synthetic_region = mapping.synthetic_region
|
|
936
|
+
if offset is not None:
|
|
937
|
+
try:
|
|
938
|
+
mapped = artifact.mapped_source.source_map.map_offset(offset)
|
|
939
|
+
except ValueError:
|
|
940
|
+
mapped = None
|
|
941
|
+
segment = _source_map_segment_at(artifact.mapped_source, offset)
|
|
942
|
+
if mapped is not None and segment is not None and segment.synthetic_region in {
|
|
943
|
+
"worker_dependency_field",
|
|
944
|
+
"worker_dependency_alias_declaration",
|
|
945
|
+
"worker_dependency_alias_initializer",
|
|
946
|
+
}:
|
|
947
|
+
synthetic_region = "dependency_binding"
|
|
948
|
+
dependency_anchor = (
|
|
949
|
+
mapped.origin_span
|
|
950
|
+
if mapped.relation is MappingRelation.DERIVED
|
|
951
|
+
else mapped.anchor_span
|
|
952
|
+
)
|
|
953
|
+
method_anchor = (
|
|
954
|
+
mapped.anchor_span
|
|
955
|
+
if mapped.relation is MappingRelation.DERIVED
|
|
956
|
+
else None
|
|
957
|
+
)
|
|
958
|
+
return WorkerRuntimeFrameDiagnostic(
|
|
959
|
+
observed_registration,
|
|
960
|
+
artifact.logical_name,
|
|
961
|
+
artifact.revision,
|
|
962
|
+
artifact.artifact_sha256,
|
|
963
|
+
mapping.confidence,
|
|
964
|
+
mapping.source_unit,
|
|
965
|
+
mapping.visible,
|
|
966
|
+
mapping.related,
|
|
967
|
+
lowered,
|
|
968
|
+
synthetic_region,
|
|
969
|
+
dependency_anchor,
|
|
970
|
+
method_anchor,
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _worker_stage_locations(
|
|
975
|
+
parsed: ParsedPlatformDiagnostic,
|
|
976
|
+
artifact: WorkerDiagnosticArtifact,
|
|
977
|
+
) -> tuple[PlatformDiagnosticLocation, ...]:
|
|
978
|
+
return tuple(
|
|
979
|
+
location
|
|
980
|
+
for location in parsed.locations
|
|
981
|
+
if location.worker_artifact_location is not None
|
|
982
|
+
and location.worker_artifact_location.registration_name.casefold()
|
|
983
|
+
== artifact.registration_name.casefold()
|
|
984
|
+
)
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _parse_worker_artifact_location(
|
|
988
|
+
components: tuple[str, ...],
|
|
989
|
+
) -> WorkerArtifactPlatformLocation | None:
|
|
990
|
+
if (
|
|
991
|
+
len(components) == 3
|
|
992
|
+
and components[0].casefold() == "внешняяобработка"
|
|
993
|
+
and components[1].casefold().startswith("onecruntime_")
|
|
994
|
+
and components[2].casefold() == "модульобъекта"
|
|
995
|
+
):
|
|
996
|
+
return WorkerArtifactPlatformLocation(components[1])
|
|
997
|
+
return None
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
def _source_map_segment_at(
|
|
1001
|
+
source: MappedSource,
|
|
1002
|
+
offset: int,
|
|
1003
|
+
) -> SourceMapSegment | None:
|
|
1004
|
+
for segment in source.source_map.segments:
|
|
1005
|
+
if segment.generated.start <= offset < segment.generated.end:
|
|
1006
|
+
return segment
|
|
1007
|
+
return None
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
def _worker_diagnostic_id(
|
|
1011
|
+
stage: DiagnosticStage,
|
|
1012
|
+
code: str,
|
|
1013
|
+
message_sha256: str,
|
|
1014
|
+
identity: tuple[str, ...],
|
|
1015
|
+
) -> str:
|
|
1016
|
+
return sha256(
|
|
1017
|
+
"|".join((stage.value, code, message_sha256, *identity)).encode("utf-8")
|
|
1018
|
+
).hexdigest()
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
def _summary(stage: DiagnosticStage) -> str:
|
|
1022
|
+
return f"BSL {stage.value} failed"
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
def _visible_unit_key(unit: SourceUnitRef) -> tuple[object, str, int]:
|
|
1026
|
+
return unit.kind, unit.unit_id, unit.revision
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
def _diagnostic_id(
|
|
1030
|
+
stage: DiagnosticStage,
|
|
1031
|
+
code: str | None,
|
|
1032
|
+
message_sha256: str,
|
|
1033
|
+
source: MappedSource,
|
|
1034
|
+
offset: int | None,
|
|
1035
|
+
) -> str:
|
|
1036
|
+
identity = "|".join(
|
|
1037
|
+
(
|
|
1038
|
+
stage.value,
|
|
1039
|
+
code or "platform",
|
|
1040
|
+
message_sha256,
|
|
1041
|
+
source.artifact.source_sha256,
|
|
1042
|
+
source.source_map_sha256,
|
|
1043
|
+
"unknown" if offset is None else str(offset),
|
|
1044
|
+
)
|
|
1045
|
+
)
|
|
1046
|
+
return sha256(identity.encode("utf-8")).hexdigest()
|