mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
refactor(native): build debugger and native cancellations with a required reason (#10599)
* refactor(errors): require a reason for every analysis cancellation Add cancelledBy, the one constructor for AnalysisCancelledError. Its reason argument is required: an aborted signal, an earlier cancellation, a reason carried from an inner failure, or an OwnerCancellation when REA closed the work's owner itself. Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and binary-session record owners now report OwnerCancellation.ownerClosed instead of an absent reason. Firmware's predecessor wait returns its cancellation as a Result, removing an unreachable reason-less fallback. An architecture guard rejects new direct constructions, with a counted per-file baseline that can only shrink. * refactor(errors): share one aborted-signal predicate with cancelledBy * refactor(process): return a departing caller's reason from a shared startup wait * refactor(providers): build decompiler-provider cancellations with a required reason (#10587) Migrate every AnalysisCancelledError construction under src/ghidra, src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the guard baseline from 671 to 572. Closed Kuna target commands report OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and JEB close abort their lifetimes with that typed reason. * fix(errors): read the reason of any signal-shaped cancellation source * refactor(native): build debugger and native cancellations with a required reason Migrate every AnalysisCancelledError construction under src/native, src/gdb and src/rizin to cancelledBy and delete Rizin's local rizinCancellation constructor; its reason choice becomes a CancellationReason. GDB close aborts active startups with OwnerCancellation.ownerClosed. * test(ci): cover the counted-baseline comparison behind the architecture guard * refactor(native): drop an unused cancellation import from native UI observation
This commit is contained in:
1 parent
f6752bbe2c
commit
57f75bb723
16 files changed
+229
-252
No files matched your search
@@ -79,22 +79,8 @@
|
||||
"src/application/releaseExposure/ReleaseExposureService.ts": 3,
|
||||
"src/application/reverse/ReverseEngineeringService.ts": 12,
|
||||
"src/application/wasm/WasmInterfaceComparisonService.ts": 2,
|
||||
"src/gdb/GdbSessionManager.ts": 13,
|
||||
"src/native/AppleDispatchMetadata.ts": 3,
|
||||
"src/native/LldbCallTracer.ts": 2,
|
||||
"src/native/NativeCallObservation.ts": 2,
|
||||
"src/native/NativeCallTarget.ts": 2,
|
||||
"src/native/NativeCommandAnalysisError.ts": 1,
|
||||
"src/native/NativeMacOSProvider.ts": 8,
|
||||
"src/native/NativePlistSource.ts": 2,
|
||||
"src/native/NativeUiInterruptionControl.ts": 1,
|
||||
"src/native/NativeUiObservation.ts": 1,
|
||||
"src/native/pe/PeBinaryFailure.ts": 1,
|
||||
"src/native/pwndbg/PwndbgCoreContext.ts": 3,
|
||||
"src/native/pwntools/PwntoolsDecoder.ts": 8,
|
||||
"src/native/pwntools/PwntoolsRecordedCrashProvider.ts": 1,
|
||||
"src/process/ownedDebuggerSession.ts": 1,
|
||||
"src/rizin/RizinDebugSessionManager.ts": 1
|
||||
"src/gdb/GdbSessionManager.ts": 1,
|
||||
"src/process/ownedDebuggerSession.ts": 1
|
||||
},
|
||||
"parser-format-failure-class": {
|
||||
"src/browser/assets/WebArtifactReads.ts": 1,
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
OwnerCancellation,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
GDB_SESSION_TOOLS,
|
||||
@@ -137,8 +138,7 @@ const cancelledWithCallerReason = (
|
||||
signal: AbortSignal,
|
||||
previous?: AnalysisCancelledError,
|
||||
): AnalysisCancelledError => {
|
||||
const cancellation = new AnalysisCancelledError(operation, {
|
||||
cause: signal.reason,
|
||||
const cancellation = cancelledBy(operation, signal, {
|
||||
cleanupPrimary: previous,
|
||||
...(previous?.diagnostics === undefined
|
||||
? {}
|
||||
@@ -550,12 +550,7 @@ export class GdbSessionManager {
|
||||
AnalysisError
|
||||
>
|
||||
> {
|
||||
if (signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError("start_gdb_session", {
|
||||
cause: signal.reason,
|
||||
}),
|
||||
);
|
||||
if (signal?.aborted) return err(cancelledBy("start_gdb_session", signal));
|
||||
const occupied = this.#sessions.occupied();
|
||||
if (occupied >= this.#maxSessions.value)
|
||||
return err(
|
||||
@@ -639,7 +634,7 @@ export class GdbSessionManager {
|
||||
(signal?.aborted && cause === signal.reason) ||
|
||||
cause instanceof AnalysisCancelledError
|
||||
)
|
||||
return err(new AnalysisCancelledError("start_gdb_session", { cause }));
|
||||
return err(cancelledBy("start_gdb_session", carriedReason(cause)));
|
||||
const refusal = classifyLaunchFailure(cause);
|
||||
if (refusal !== undefined && cause instanceof Error)
|
||||
return err(
|
||||
@@ -743,11 +738,7 @@ export class GdbSessionManager {
|
||||
session.startupResolve = resolve;
|
||||
session.startupReject = reject;
|
||||
startupAbortListener = () =>
|
||||
reject(
|
||||
new AnalysisCancelledError("start_gdb_session", {
|
||||
cause: signal?.reason,
|
||||
}),
|
||||
);
|
||||
reject(cancelledBy("start_gdb_session", signal));
|
||||
signal?.addEventListener("abort", startupAbortListener, {
|
||||
once: true,
|
||||
});
|
||||
@@ -773,10 +764,7 @@ export class GdbSessionManager {
|
||||
);
|
||||
startupTimer.unref();
|
||||
});
|
||||
if (signal?.aborted)
|
||||
throw new AnalysisCancelledError("start_gdb_session", {
|
||||
cause: signal.reason,
|
||||
});
|
||||
if (signal?.aborted) throw cancelledBy("start_gdb_session", signal);
|
||||
// -gdb-version loads nothing, so it may precede the auto-load setting.
|
||||
const reported = await this.#request(
|
||||
session,
|
||||
@@ -847,16 +835,10 @@ export class GdbSessionManager {
|
||||
);
|
||||
session.inferiorTtyVerified = true;
|
||||
}
|
||||
if (signal?.aborted)
|
||||
throw new AnalysisCancelledError("start_gdb_session", {
|
||||
cause: signal.reason,
|
||||
});
|
||||
if (signal?.aborted) throw cancelledBy("start_gdb_session", signal);
|
||||
// Give queued exit notifications a turn before publishing startup.
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
if (signal?.aborted)
|
||||
throw new AnalysisCancelledError("start_gdb_session", {
|
||||
cause: signal.reason,
|
||||
});
|
||||
if (signal?.aborted) throw cancelledBy("start_gdb_session", signal);
|
||||
if (session.exited || session.protocolError !== undefined)
|
||||
throw new Error(
|
||||
session.protocolError ?? "GDB exited during startup readiness.",
|
||||
@@ -965,14 +947,14 @@ export class GdbSessionManager {
|
||||
cleanupResources: [session.id],
|
||||
});
|
||||
const cleanup = { reason: stopped.reason, resources: [session.id] };
|
||||
const details = {
|
||||
cause,
|
||||
const detailsWithoutCause = {
|
||||
capturedOutput,
|
||||
cleanup,
|
||||
...(startupEvidence === undefined
|
||||
? {}
|
||||
: { partialObservation: startupEvidence }),
|
||||
};
|
||||
const details = { cause, ...detailsWithoutCause };
|
||||
if (cause instanceof AnalysisTimeoutError)
|
||||
return err(
|
||||
retainCleanup(
|
||||
@@ -984,7 +966,13 @@ export class GdbSessionManager {
|
||||
);
|
||||
if (cause instanceof AnalysisCancelledError)
|
||||
return err(
|
||||
retainCleanup(new AnalysisCancelledError(cause.operation, details)),
|
||||
retainCleanup(
|
||||
cancelledBy(
|
||||
cause.operation,
|
||||
carriedReason(cause),
|
||||
detailsWithoutCause,
|
||||
),
|
||||
),
|
||||
);
|
||||
const startupFailureMessage = gdbErrorMessage(cause);
|
||||
return err(
|
||||
@@ -998,16 +986,20 @@ export class GdbSessionManager {
|
||||
),
|
||||
);
|
||||
}
|
||||
const errorDetails = {
|
||||
cause,
|
||||
const errorDetailsWithoutCause = {
|
||||
capturedOutput,
|
||||
...(startupEvidence === undefined
|
||||
? {}
|
||||
: { partialObservation: startupEvidence }),
|
||||
};
|
||||
const errorDetails = { cause, ...errorDetailsWithoutCause };
|
||||
const error =
|
||||
cause instanceof AnalysisCancelledError
|
||||
? new AnalysisCancelledError("start_gdb_session", errorDetails)
|
||||
? cancelledBy(
|
||||
"start_gdb_session",
|
||||
carriedReason(cause),
|
||||
errorDetailsWithoutCause,
|
||||
)
|
||||
: cause instanceof AnalysisTimeoutError
|
||||
? new AnalysisTimeoutError(
|
||||
cause instanceof GdbCommandTimeoutError
|
||||
@@ -1224,7 +1216,7 @@ export class GdbSessionManager {
|
||||
const error =
|
||||
cause instanceof AnalysisError
|
||||
? cause
|
||||
: new AnalysisCancelledError("gdb_console", { cause });
|
||||
: cancelledBy("gdb_console", carriedReason(cause));
|
||||
if (error.cleanupIncomplete) this.#sessions.retain(session, error);
|
||||
return err(error);
|
||||
}
|
||||
@@ -2060,8 +2052,7 @@ export class GdbSessionManager {
|
||||
void this.#stopDebuggerOnly(session).then((stopped) => {
|
||||
if (stopped.status !== "incomplete") {
|
||||
pending.reject(
|
||||
new AnalysisCancelledError("gdb_console", {
|
||||
cause: signal?.reason,
|
||||
cancelledBy("gdb_console", carriedReason(signal?.reason), {
|
||||
partialObservation: evidence,
|
||||
}),
|
||||
);
|
||||
@@ -2078,8 +2069,7 @@ export class GdbSessionManager {
|
||||
{ cause: cancellation },
|
||||
);
|
||||
pending.reject(
|
||||
new AnalysisCancelledError("gdb_console", {
|
||||
cause: signal?.reason,
|
||||
cancelledBy("gdb_console", carriedReason(signal?.reason), {
|
||||
cleanup: {
|
||||
reason: stopped.reason,
|
||||
resources: [session.id],
|
||||
|
||||
@@ -18,12 +18,13 @@ import {
|
||||
type BinaryTarget,
|
||||
} from "../domain/binaryTargetTypes.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisAccessDeniedError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisResourceConstraintError,
|
||||
AnalysisUnsupportedTargetError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
ArtifactFileCleanupFailure,
|
||||
@@ -369,10 +370,8 @@ export const inspectAppleDispatchMetadata = async (
|
||||
}
|
||||
: { maximum_bytes: options.maxInputBytes, source: "caller" as const };
|
||||
const maximumBytes = admission.maximum_bytes;
|
||||
const cancelled = () =>
|
||||
new AnalysisCancelledError(OPERATION, {
|
||||
cause: signal?.reason,
|
||||
});
|
||||
// Called only after the caller's signal aborted.
|
||||
const cancelled = () => cancelledBy(OPERATION, carriedReason(signal?.reason));
|
||||
const { bytes, digest } = await withArtifactRegularFile(
|
||||
target.path,
|
||||
resources,
|
||||
@@ -438,9 +437,10 @@ export const appleDispatchMetadataFailure = (
|
||||
)
|
||||
: physical?.completionCancellation === undefined
|
||||
? undefined
|
||||
: new AnalysisCancelledError(OPERATION, {
|
||||
cause: physical.completionCancellation.reason,
|
||||
});
|
||||
: cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(physical.completionCancellation.reason),
|
||||
);
|
||||
const failure = new ProviderCleanupError(
|
||||
IDENTITY.id,
|
||||
cause.cleanup.resources,
|
||||
@@ -487,9 +487,10 @@ const appleDispatchMetadataPrimaryFailure = (
|
||||
{ cause },
|
||||
);
|
||||
if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled")
|
||||
return new AnalysisCancelledError(OPERATION, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
});
|
||||
return cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
);
|
||||
if (
|
||||
cause instanceof ArtifactReaderFailure &&
|
||||
cause.reason === "unavailable"
|
||||
|
||||
@@ -15,10 +15,11 @@ import {
|
||||
} from "../domain/native/nativeCallCaptureLimits.js";
|
||||
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisResourceConstraintError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
|
||||
import type {
|
||||
@@ -788,15 +789,13 @@ export class LldbCallTracer implements NativeCallTracer {
|
||||
);
|
||||
};
|
||||
if (exited.kind === "cancelled")
|
||||
return failureWithPartial(
|
||||
(partialObservation) =>
|
||||
new AnalysisCancelledError(OPERATION, {
|
||||
cause: signal?.reason,
|
||||
...(lifecycleCleanup === undefined
|
||||
? {}
|
||||
: { cleanup: lifecycleCleanup }),
|
||||
partialObservation,
|
||||
}),
|
||||
return failureWithPartial((partialObservation) =>
|
||||
cancelledBy(OPERATION, carriedReason(signal?.reason), {
|
||||
...(lifecycleCleanup === undefined
|
||||
? {}
|
||||
: { cleanup: lifecycleCleanup }),
|
||||
partialObservation,
|
||||
}),
|
||||
);
|
||||
if (exited.kind === "timeout")
|
||||
return failureWithPartial(
|
||||
@@ -1287,8 +1286,7 @@ const lldbPreparationFailure = (
|
||||
): AnalysisError => {
|
||||
const marker = cause.cleanupFailure;
|
||||
const compiler = cause.compilerOutcome;
|
||||
const options = {
|
||||
cause,
|
||||
const optionsWithoutCause = {
|
||||
...(cause.capturedOutput === undefined
|
||||
? {}
|
||||
: { capturedOutput: cause.capturedOutput }),
|
||||
@@ -1318,13 +1316,15 @@ const lldbPreparationFailure = (
|
||||
}),
|
||||
},
|
||||
};
|
||||
const options = { cause, ...optionsWithoutCause };
|
||||
const error =
|
||||
cause.cancellation === undefined
|
||||
? new ProviderAdapterError(PROVIDER, OPERATION, options)
|
||||
: new AnalysisCancelledError(OPERATION, {
|
||||
...options,
|
||||
cause: cause.cancellation.reason,
|
||||
});
|
||||
: cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(cause.cancellation.reason),
|
||||
optionsWithoutCause,
|
||||
);
|
||||
return marker === undefined ? error : error.bindCleanupDependencies([marker]);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { nativeCallCaptureLimits } from "../domain/native/nativeCallCaptureLimits.js";
|
||||
import {
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisCancelledError,
|
||||
AnalysisInputError,
|
||||
cancelledBy,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import {
|
||||
@@ -70,10 +70,7 @@ export const observeNativeCalls = async (
|
||||
return await resources.run(async () => {
|
||||
const selected = await verifyNativeCallTarget(target, resources, signal);
|
||||
if (!selected.ok) return selected;
|
||||
if (signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError(OPERATION, { cause: signal.reason }),
|
||||
);
|
||||
if (signal?.aborted) return err(cancelledBy(OPERATION, signal));
|
||||
try {
|
||||
const traced = await tracer.trace(
|
||||
{
|
||||
@@ -87,8 +84,7 @@ export const observeNativeCalls = async (
|
||||
if (!traced.ok) return traced;
|
||||
return signal?.aborted
|
||||
? err(
|
||||
new AnalysisCancelledError(OPERATION, {
|
||||
cause: signal.reason,
|
||||
cancelledBy(OPERATION, signal, {
|
||||
partialObservation: projectPartialNativeCalls(
|
||||
target,
|
||||
parsed.data,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NATIVE_MACOS_PROVIDER_IDENTITY as IDENTITY } from "./NativeMacosProviderIdentity.js";
|
||||
import type { BinaryTarget } from "../domain/binaryTargetTypes.js";
|
||||
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
||||
import { cancelledBy, carriedReason } from "../domain/analysisErrorCore.js";
|
||||
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
||||
@@ -54,16 +54,17 @@ export const nativeCallTargetFailure = (
|
||||
cause instanceof RegularFileOperationCancelled ||
|
||||
cause instanceof RegularFileCancelled
|
||||
)
|
||||
return new AnalysisCancelledError(OPERATION, {
|
||||
cause: cause.reason,
|
||||
...(cause instanceof RegularFileCancelled
|
||||
return cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(cause.reason),
|
||||
cause instanceof RegularFileCancelled
|
||||
? {
|
||||
diagnostics: {
|
||||
completed_hash: jsonValueSchema.parse(cause.outcome.value),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
: {},
|
||||
);
|
||||
const physical = stableFileHashCause(cause);
|
||||
if (physical instanceof RegularFileChangedError)
|
||||
return new EvidenceIntegrityError(
|
||||
@@ -125,8 +126,7 @@ export const verifyNativeCallTarget = async (
|
||||
? nativeCallTargetFailure(cause.outcome.cause, target.path)
|
||||
: cancellation === undefined
|
||||
? undefined
|
||||
: new AnalysisCancelledError(OPERATION, {
|
||||
cause: cancellation.reason,
|
||||
: cancelledBy(OPERATION, carriedReason(cancellation.reason), {
|
||||
diagnostics: completed,
|
||||
});
|
||||
const framed = new ArtifactFileCleanupFailure(cause, cleanupLease);
|
||||
|
||||
@@ -10,10 +10,11 @@ import {
|
||||
} from "../providers/OwnedCommandCleanup.js";
|
||||
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisResourceConstraintError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { NATIVE_MACOS_PROVIDER_IDENTITY as IDENTITY } from "./NativeMacosProviderIdentity.js";
|
||||
@@ -163,11 +164,11 @@ export const translateNativeCommandFailure = (
|
||||
reason: failure.cleanupFailure,
|
||||
resources: [...failure.cleanupResources],
|
||||
};
|
||||
const errorOptions = {
|
||||
cause: failure,
|
||||
const errorOptionsWithoutCause = {
|
||||
...(capturedOutput === undefined ? {} : { capturedOutput }),
|
||||
...(cleanup === undefined ? {} : { cleanup }),
|
||||
};
|
||||
const errorOptions = { cause: failure, ...errorOptionsWithoutCause };
|
||||
if (failure.reason === "unavailable") {
|
||||
const reason =
|
||||
presentation.unavailableMessage ??
|
||||
@@ -181,12 +182,17 @@ export const translateNativeCommandFailure = (
|
||||
}
|
||||
if (failure.reason === "cancelled")
|
||||
return bindCleanup(
|
||||
new AnalysisCancelledError(operation, {
|
||||
...errorOptions,
|
||||
...(failure.cancellation === undefined
|
||||
? {}
|
||||
: { cause: failure.cancellation.reason }),
|
||||
}),
|
||||
cancelledBy(
|
||||
operation,
|
||||
carriedReason(
|
||||
failure.cancellation === undefined
|
||||
? failure
|
||||
: failure.cancellation.reason,
|
||||
),
|
||||
{
|
||||
...errorOptionsWithoutCause,
|
||||
},
|
||||
),
|
||||
);
|
||||
if (failure.reason === "timeout")
|
||||
return bindCleanup(
|
||||
|
||||
@@ -76,6 +76,8 @@ import {
|
||||
AnalysisOutputError,
|
||||
AnalysisUnsupportedTargetError,
|
||||
AnalysisResourceConstraintError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
DEFAULT_NATIVE_COMMAND_TIMEOUT_MS,
|
||||
@@ -287,9 +289,7 @@ class NativeMacOSClient implements AnalysisClient {
|
||||
),
|
||||
);
|
||||
if (options?.signal?.aborted === true)
|
||||
return err(
|
||||
new AnalysisCancelledError(operation, { cause: options.signal.reason }),
|
||||
);
|
||||
return err(cancelledBy(operation, options.signal));
|
||||
if (operation === "health")
|
||||
return ok(createAnalysisExecution(null, IDENTITY));
|
||||
if (this.activeTarget === undefined && operation !== "inspect_plist")
|
||||
@@ -1487,12 +1487,7 @@ class NativeMacOSClient implements AnalysisClient {
|
||||
): Promise<Result<NativeObservation, AnalysisError>> {
|
||||
try {
|
||||
return await this.#machoOperandResources.run(async () => {
|
||||
if (signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError("inspect_macho", {
|
||||
cause: signal.reason,
|
||||
}),
|
||||
);
|
||||
if (signal?.aborted) return err(cancelledBy("inspect_macho", signal));
|
||||
let root: Pick<PrivateRuntimeRoot, "path" | "close">;
|
||||
try {
|
||||
root = await (this.privateRuntimeRootFactory?.() ??
|
||||
@@ -1524,11 +1519,7 @@ class NativeMacOSClient implements AnalysisClient {
|
||||
const operand = join(root.path, "artifact.macho");
|
||||
await symlink(this.target.path, operand);
|
||||
result = signal?.aborted
|
||||
? err(
|
||||
new AnalysisCancelledError("inspect_macho", {
|
||||
cause: signal.reason,
|
||||
}),
|
||||
)
|
||||
? err(cancelledBy("inspect_macho", signal))
|
||||
: await inspect(operand);
|
||||
} catch (cause: unknown) {
|
||||
result = err(
|
||||
@@ -1558,8 +1549,7 @@ class NativeMacOSClient implements AnalysisClient {
|
||||
new AnalysisCleanupLeaseGroup(cleanup, [released.lease]);
|
||||
const primary = result.ok
|
||||
? signal?.aborted
|
||||
? new AnalysisCancelledError("inspect_macho", {
|
||||
cause: signal.reason,
|
||||
? cancelledBy("inspect_macho", signal, {
|
||||
partialObservation: inspectMachoSchema.parse(
|
||||
result.value.result,
|
||||
),
|
||||
@@ -1578,8 +1568,7 @@ class NativeMacOSClient implements AnalysisClient {
|
||||
}
|
||||
if (signal?.aborted && result.ok)
|
||||
return err(
|
||||
new AnalysisCancelledError("inspect_macho", {
|
||||
cause: signal.reason,
|
||||
cancelledBy("inspect_macho", signal, {
|
||||
partialObservation: inspectMachoSchema.parse(result.value.result),
|
||||
}),
|
||||
);
|
||||
@@ -1765,9 +1754,8 @@ const nativeMachoSourcePrimary = (
|
||||
: undefined;
|
||||
const completedFailure = nativeMachoCompletedFailure(completed);
|
||||
const previous = failed ?? completedFailure;
|
||||
const options = {
|
||||
cause,
|
||||
...(previous === undefined
|
||||
const optionsWithoutCause =
|
||||
previous === undefined
|
||||
? {}
|
||||
: {
|
||||
diagnostics: {
|
||||
@@ -1775,22 +1763,24 @@ const nativeMachoSourcePrimary = (
|
||||
projectAnalysisError(previous),
|
||||
),
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
const options = { cause, ...optionsWithoutCause };
|
||||
if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled") {
|
||||
// A returned failure is already the producer's physical outcome. A later
|
||||
// cancelled identity check must not replace its output or cleanup owner.
|
||||
if (completedFailure !== undefined)
|
||||
return analysisErrorWithCancellation(
|
||||
completedFailure,
|
||||
new AnalysisCancelledError(operation, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
}),
|
||||
cancelledBy(
|
||||
operation,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
),
|
||||
);
|
||||
return new AnalysisCancelledError(operation, {
|
||||
...options,
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
});
|
||||
return cancelledBy(
|
||||
operation,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
optionsWithoutCause,
|
||||
);
|
||||
}
|
||||
if (cause instanceof ArtifactReaderFailure && cause.reason === "format")
|
||||
return new AnalysisOutputError(operation, cause.message, options);
|
||||
@@ -1841,12 +1831,13 @@ const nativeMachoCompletion = (
|
||||
const completionCancellation =
|
||||
completion?.cancellation === undefined
|
||||
? undefined
|
||||
: new AnalysisCancelledError(operation, {
|
||||
cause: completion.cancellation.reason,
|
||||
...(completedFailure === undefined ||
|
||||
completedFailure.cleanupIncomplete ||
|
||||
(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined)
|
||||
: cancelledBy(
|
||||
operation,
|
||||
carriedReason(completion.cancellation.reason),
|
||||
completedFailure === undefined ||
|
||||
completedFailure.cleanupIncomplete ||
|
||||
(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined)
|
||||
? {}
|
||||
: {
|
||||
diagnostics: {
|
||||
@@ -1854,8 +1845,8 @@ const nativeMachoCompletion = (
|
||||
projectAnalysisError(completedFailure),
|
||||
),
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
);
|
||||
return { observation, completedFailure, completionCancellation };
|
||||
};
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ import type {
|
||||
} from "../domain/analysisErrorBase.js";
|
||||
import {
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisInputError,
|
||||
AnalysisResourceConstraintError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { BinaryTargetError } from "../domain/configurationErrors.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
@@ -155,9 +156,10 @@ export const nativePlistFailure = (
|
||||
? nativePlistPrimaryFailure(artifactFilePrimaryFacts(cause), selection)
|
||||
: physical?.completionCancellation === undefined
|
||||
? undefined
|
||||
: new AnalysisCancelledError("inspect_plist", {
|
||||
cause: physical.completionCancellation.reason,
|
||||
});
|
||||
: cancelledBy(
|
||||
"inspect_plist",
|
||||
carriedReason(physical.completionCancellation.reason),
|
||||
);
|
||||
const previousProjection =
|
||||
previous === undefined ? null : projectAnalysisError(previous);
|
||||
const failure = new ProviderCleanupError(
|
||||
@@ -185,9 +187,10 @@ const nativePlistPrimaryFailure = (
|
||||
): AnalysisError => {
|
||||
if (origin === "owned-or-classified") {
|
||||
if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled")
|
||||
return new AnalysisCancelledError("inspect_plist", {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
});
|
||||
return cancelledBy(
|
||||
"inspect_plist",
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
);
|
||||
if (
|
||||
cause instanceof ArtifactReaderFailure &&
|
||||
cause.reason === "unavailable"
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { AnalysisPartialObservation } from "../domain/analysisErrorBase.js"
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
DEFAULT_NATIVE_UI_DEADLINE_MS,
|
||||
@@ -90,8 +92,7 @@ export class NativeUiInterruptionControl {
|
||||
"The 67108864-byte scenario output allowance and the 180000 ms cap on one wait step have no runtime override.",
|
||||
}),
|
||||
})
|
||||
: new AnalysisCancelledError(operation, {
|
||||
cause: interruption.reason,
|
||||
: cancelledBy(operation, carriedReason(interruption.reason), {
|
||||
partialObservation,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
nativeUiResultSchema,
|
||||
} from "../domain/native/nativeUiObservation.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisInputError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
|
||||
import { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
@@ -278,7 +279,7 @@ const observeWithHelper = async (
|
||||
? ((signal.aborted && cause.cause === signal.reason
|
||||
? interruption.error(operation)
|
||||
: undefined) ??
|
||||
new AnalysisCancelledError(operation, { cause: cause.cause }))
|
||||
cancelledBy(operation, carriedReason(cause.cause)))
|
||||
: new ProviderAdapterError("native-macos", operation, {
|
||||
cause,
|
||||
diagnostics: {
|
||||
|
||||
@@ -14,9 +14,10 @@ import {
|
||||
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import {
|
||||
AnalysisArtifactChangedError,
|
||||
AnalysisCancelledError,
|
||||
AnalysisResourceConstraintError,
|
||||
type ResourceConstraintRecovery,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
||||
import { BinaryResourceFailure } from "../metadata/BinaryContainer.js";
|
||||
@@ -58,9 +59,10 @@ const peBinaryFailurePrimary = <Limit extends string>(
|
||||
cause instanceof ArtifactReaderFailure &&
|
||||
cause.reason === "cancelled"
|
||||
)
|
||||
return new AnalysisCancelledError(operation, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
});
|
||||
return cancelledBy(
|
||||
operation,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
);
|
||||
if (cause instanceof BinaryResourceFailure) {
|
||||
const limit = options.limit(cause);
|
||||
return new AnalysisResourceConstraintError(
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisOutputError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
||||
import {
|
||||
@@ -99,8 +101,7 @@ export const inspectPwndbgCore = async ({
|
||||
});
|
||||
const checkCancelled = () => {
|
||||
if (options?.signal?.aborted)
|
||||
throw new AnalysisCancelledError(OPERATION, {
|
||||
cause: options.signal.reason,
|
||||
throw cancelledBy(OPERATION, options.signal, {
|
||||
capturedOutput: diagnostics,
|
||||
});
|
||||
};
|
||||
@@ -232,14 +233,17 @@ export const inspectPwndbgCore = async ({
|
||||
cause instanceof ArtifactReaderFailure &&
|
||||
cause.reason === "cancelled"
|
||||
) {
|
||||
const error = new AnalysisCancelledError(OPERATION, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
capturedOutput: recordedCrashStageOutput(diagnostics, output),
|
||||
...(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined
|
||||
? { cleanup: cause.cleanup }
|
||||
: {}),
|
||||
});
|
||||
const error = cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
{
|
||||
capturedOutput: recordedCrashStageOutput(diagnostics, output),
|
||||
...(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined
|
||||
? { cleanup: cause.cleanup }
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
bindArtifactFileCleanup(cause, error);
|
||||
throw pwndbgContextFinalizationFailure(error, finalization);
|
||||
}
|
||||
@@ -334,10 +338,11 @@ const completedReplyFailure = (
|
||||
): AnalysisError => {
|
||||
const cancellation =
|
||||
cause.reason === "cancelled"
|
||||
? new AnalysisCancelledError(OPERATION, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
capturedOutput,
|
||||
})
|
||||
? cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
{ capturedOutput },
|
||||
)
|
||||
: undefined;
|
||||
if (cause.cleanup === undefined) {
|
||||
if (cancellation !== undefined) return cancellation;
|
||||
|
||||
@@ -37,12 +37,13 @@ import { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js"
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisUnsupportedTargetError,
|
||||
AnalysisInputError,
|
||||
AnalysisOutputError,
|
||||
AnalysisResourceConstraintError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import {
|
||||
projectAnalysisError,
|
||||
@@ -214,11 +215,7 @@ export class PwntoolsDecoder<
|
||||
): Promise<Result<Report, AnalysisError>> {
|
||||
if (options?.signal?.aborted)
|
||||
return Promise.resolve(
|
||||
err(
|
||||
new AnalysisCancelledError(this.profile.operation, {
|
||||
cause: options?.signal?.reason,
|
||||
}),
|
||||
),
|
||||
err(cancelledBy(this.profile.operation, options?.signal)),
|
||||
);
|
||||
if (this.#closing) return Promise.resolve(err(this.#closedError()));
|
||||
const key = Symbol();
|
||||
@@ -229,11 +226,7 @@ export class PwntoolsDecoder<
|
||||
const operation = this.#tail.then(() => {
|
||||
this.#admissions.settle(key, "admitted");
|
||||
if (options?.signal?.aborted)
|
||||
return err(
|
||||
new AnalysisCancelledError(this.profile.operation, {
|
||||
cause: options?.signal?.reason,
|
||||
}),
|
||||
);
|
||||
return err(cancelledBy(this.profile.operation, options?.signal));
|
||||
return this.#closing ? err(this.#closedError()) : inspect();
|
||||
});
|
||||
this.#tail = operation.then(
|
||||
@@ -243,9 +236,10 @@ export class PwntoolsDecoder<
|
||||
return admission.then((state) =>
|
||||
state === "cancelled"
|
||||
? err(
|
||||
new AnalysisCancelledError(this.profile.operation, {
|
||||
cause: options?.signal?.reason,
|
||||
}),
|
||||
cancelledBy(
|
||||
this.profile.operation,
|
||||
carriedReason(options?.signal?.reason),
|
||||
),
|
||||
)
|
||||
: operation,
|
||||
);
|
||||
@@ -337,9 +331,7 @@ export class PwntoolsDecoder<
|
||||
);
|
||||
try {
|
||||
if (options?.signal?.aborted)
|
||||
throw new AnalysisCancelledError(OPERATION, {
|
||||
cause: options.signal.reason,
|
||||
});
|
||||
throw cancelledBy(OPERATION, options.signal);
|
||||
if (!pwntoolsHostSupported(process.platform, process.arch)) {
|
||||
const requirement = pwntoolsHostRequirement(
|
||||
process.platform,
|
||||
@@ -430,9 +422,7 @@ export class PwntoolsDecoder<
|
||||
cause.code === "ABORT_ERR" &&
|
||||
cause.cause === options.signal.reason
|
||||
)
|
||||
throw new AnalysisCancelledError(OPERATION, {
|
||||
cause: options.signal.reason,
|
||||
});
|
||||
throw cancelledBy(OPERATION, options.signal);
|
||||
throw cause;
|
||||
}
|
||||
await writeFile(
|
||||
@@ -501,10 +491,11 @@ export class PwntoolsDecoder<
|
||||
if (cleanup !== undefined) bindArtifactFileCleanup(cause, cleanup);
|
||||
const primary =
|
||||
cause.reason === "cancelled"
|
||||
? new AnalysisCancelledError(OPERATION, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
capturedOutput,
|
||||
})
|
||||
? cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
{ capturedOutput },
|
||||
)
|
||||
: (cleanup ??
|
||||
new AnalysisOutputError(OPERATION, cause.message, {
|
||||
cause,
|
||||
@@ -527,14 +518,17 @@ export class PwntoolsDecoder<
|
||||
cause instanceof ArtifactReaderFailure &&
|
||||
cause.reason === "cancelled"
|
||||
) {
|
||||
const error = new AnalysisCancelledError(OPERATION, {
|
||||
cause: artifactFileCancellationCause(cause),
|
||||
capturedOutput,
|
||||
...(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined
|
||||
? { cleanup: cause.cleanup }
|
||||
: {}),
|
||||
});
|
||||
const error = cancelledBy(
|
||||
OPERATION,
|
||||
carriedReason(artifactFileCancellationCause(cause)),
|
||||
{
|
||||
capturedOutput,
|
||||
...(cause instanceof ArtifactReaderFailure &&
|
||||
cause.cleanup !== undefined
|
||||
? { cleanup: cause.cleanup }
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
bindArtifactFileCleanup(cause, error);
|
||||
throw error;
|
||||
}
|
||||
@@ -706,8 +700,7 @@ export class PwntoolsDecoder<
|
||||
const cleanup = await this.#completeCleanup(() => this.#retire());
|
||||
if (result.ok && options?.signal?.aborted)
|
||||
result = err(
|
||||
new AnalysisCancelledError(OPERATION, {
|
||||
cause: options.signal.reason,
|
||||
cancelledBy(OPERATION, options.signal, {
|
||||
partialObservation: result.value,
|
||||
capturedOutput: result.value.diagnostics,
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisOutputError,
|
||||
cancelledBy,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import {
|
||||
@@ -145,8 +145,7 @@ export class PwntoolsRecordedCrashProvider
|
||||
});
|
||||
try {
|
||||
if (options?.signal?.aborted)
|
||||
throw new AnalysisCancelledError("inspect_recorded_crash", {
|
||||
cause: options.signal.reason,
|
||||
throw cancelledBy("inspect_recorded_crash", options.signal, {
|
||||
capturedOutput: diagnostics,
|
||||
});
|
||||
const authority = validateCoreFileMappingAuthority(
|
||||
|
||||
@@ -22,6 +22,10 @@ import {
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisInputError,
|
||||
AnalysisTimeoutError,
|
||||
cancelledBy,
|
||||
carriedReason,
|
||||
isAborted,
|
||||
type CancellationReason,
|
||||
} from "../domain/analysisErrorCore.js";
|
||||
import {
|
||||
RIZIN_DEBUG_SESSION_TOOLS,
|
||||
@@ -422,7 +426,7 @@ export class RizinDebugSessionManager {
|
||||
>
|
||||
> {
|
||||
if (signal?.aborted)
|
||||
return err(rizinCancellation("start_rizin_debug_session", signal));
|
||||
return err(cancelledBy("start_rizin_debug_session", signal));
|
||||
if (!persistentDebuggerHostSupported(this.#platform))
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
@@ -506,10 +510,9 @@ export class RizinDebugSessionManager {
|
||||
},
|
||||
],
|
||||
)
|
||||
: rizinCancellation(
|
||||
: cancelledBy(
|
||||
"start_rizin_debug_session",
|
||||
undefined,
|
||||
cancellation.reason,
|
||||
carriedReason(cancellation.reason),
|
||||
);
|
||||
if (!(cause instanceof RegularFileCleanupFailure)) return err(primary);
|
||||
const cleanup = new ProviderCleanupError(
|
||||
@@ -533,7 +536,7 @@ export class RizinDebugSessionManager {
|
||||
// TTY identity validation is asynchronous. Cancellation may arrive while
|
||||
// realpath/open/stat are pending, and must prevent the debugger spawn.
|
||||
if (signal?.aborted)
|
||||
return err(rizinCancellation("start_rizin_debug_session", signal));
|
||||
return err(cancelledBy("start_rizin_debug_session", signal));
|
||||
const id = randomUUID();
|
||||
const resolvedPath = absoluteCallerPath(input.path);
|
||||
const launchProvenance: RizinLaunchProvenance = {
|
||||
@@ -570,7 +573,10 @@ export class RizinDebugSessionManager {
|
||||
} catch (cause) {
|
||||
if (signal?.aborted)
|
||||
return err(
|
||||
rizinCancellation("start_rizin_debug_session", signal, cause),
|
||||
cancelledBy(
|
||||
"start_rizin_debug_session",
|
||||
rizinCancellationReason(signal, cause),
|
||||
),
|
||||
);
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
@@ -663,7 +669,7 @@ export class RizinDebugSessionManager {
|
||||
// initial frame run before publishing the session as ready.
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
if (signal?.aborted)
|
||||
throw rizinCancellation("start_rizin_debug_session", signal);
|
||||
throw cancelledBy("start_rizin_debug_session", signal);
|
||||
if (session.exited)
|
||||
throw new RizinProcessExitError("Rizin exited during startup.");
|
||||
if (session.protocolError !== undefined)
|
||||
@@ -675,19 +681,19 @@ export class RizinDebugSessionManager {
|
||||
const protocolFailure = session.protocolError;
|
||||
const cancellation =
|
||||
signal?.aborted || caught instanceof AnalysisCancelledError
|
||||
? rizinCancellation("start_rizin_debug_session", signal, caught)
|
||||
? cancelledBy(
|
||||
"start_rizin_debug_session",
|
||||
rizinCancellationReason(signal, caught),
|
||||
)
|
||||
: undefined;
|
||||
const stopped = await this.#stopDebuggerOnly(session);
|
||||
const capturedOutput = this.#startupCapturedOutput(session);
|
||||
if (stopped.status !== "incomplete") this.#sessions.remove(session);
|
||||
let primary: AnalysisError;
|
||||
if (cancellation !== undefined)
|
||||
primary = rizinCancellation(
|
||||
"start_rizin_debug_session",
|
||||
undefined,
|
||||
cancellation,
|
||||
{ capturedOutput },
|
||||
);
|
||||
primary = cancelledBy("start_rizin_debug_session", cancellation, {
|
||||
capturedOutput,
|
||||
});
|
||||
else if (protocolFailure !== undefined)
|
||||
primary = new AnalysisCapabilityUnavailableError(
|
||||
"rizin",
|
||||
@@ -754,8 +760,7 @@ export class RizinDebugSessionManager {
|
||||
stopped,
|
||||
),
|
||||
);
|
||||
if (signal?.aborted)
|
||||
return err(rizinCancellation("rizin_debug_command", signal));
|
||||
if (signal?.aborted) return err(cancelledBy("rizin_debug_command", signal));
|
||||
if (session.pendingCommandCount >= this.#maxPendingCommands.value)
|
||||
return err(
|
||||
debuggerResourceConstraintError(
|
||||
@@ -807,7 +812,7 @@ export class RizinDebugSessionManager {
|
||||
await waitForTurn(previous, signal);
|
||||
acquired = true;
|
||||
if (signal?.aborted)
|
||||
return err(rizinCancellation("rizin_debug_command", signal));
|
||||
return err(cancelledBy("rizin_debug_command", signal));
|
||||
const stoppedWhileQueued = this.#stopReason(session);
|
||||
if (stoppedWhileQueued !== undefined)
|
||||
return err(
|
||||
@@ -877,8 +882,7 @@ export class RizinDebugSessionManager {
|
||||
throw interruption.cause;
|
||||
}
|
||||
const captured = received.value;
|
||||
if (signal?.aborted)
|
||||
throw rizinCancellation("rizin_debug_command", signal);
|
||||
if (signal?.aborted) throw cancelledBy("rizin_debug_command", signal);
|
||||
let stopped: Awaited<ReturnType<typeof stopOwnedDebuggerProcess>> = {
|
||||
status: "stopped",
|
||||
};
|
||||
@@ -948,7 +952,12 @@ export class RizinDebugSessionManager {
|
||||
});
|
||||
if (signal?.aborted || cause instanceof AnalysisCancelledError) {
|
||||
if (!commandSent)
|
||||
return err(rizinCancellation("rizin_debug_command", signal, cause));
|
||||
return err(
|
||||
cancelledBy(
|
||||
"rizin_debug_command",
|
||||
rizinCancellationReason(signal, cause),
|
||||
),
|
||||
);
|
||||
const evidence = partialEvidence([
|
||||
"The Rizin command was cancelled; collected frame output is partial and command completion is unknown.",
|
||||
"The owned debugger session was stopped because command output can no longer be correlated safely.",
|
||||
@@ -956,10 +965,9 @@ export class RizinDebugSessionManager {
|
||||
]);
|
||||
session.protocolError =
|
||||
"A Rizin command was cancelled; the owned session was stopped because command output can no longer be correlated safely.";
|
||||
const primary = rizinCancellation(
|
||||
const primary = cancelledBy(
|
||||
"rizin_debug_command",
|
||||
signal,
|
||||
cause,
|
||||
rizinCancellationReason(signal, cause),
|
||||
{
|
||||
...(capturedOutput === undefined ? {} : { capturedOutput }),
|
||||
...(evidence === undefined ? {} : { partialObservation: evidence }),
|
||||
@@ -1600,10 +1608,9 @@ export class RizinDebugSessionManager {
|
||||
signal?: AbortSignal,
|
||||
): Result<typeof observation, AnalysisError> {
|
||||
if (signal?.aborted) {
|
||||
const primary = rizinCancellation(
|
||||
const primary = cancelledBy(
|
||||
"rizin_debug_command",
|
||||
signal,
|
||||
undefined,
|
||||
rizinCancellationReason(signal, undefined),
|
||||
{
|
||||
capturedOutput: {
|
||||
stdout: observation.result.output,
|
||||
@@ -1643,7 +1650,7 @@ export class RizinDebugSessionManager {
|
||||
signal?: AbortSignal,
|
||||
): AnalysisError {
|
||||
if (signal?.aborted && !(primary instanceof AnalysisCancelledError))
|
||||
primary.retainSecondaryFailure(rizinCancellation(operation, signal), {
|
||||
primary.retainSecondaryFailure(cancelledBy(operation, signal), {
|
||||
finalization_cancelled: true,
|
||||
});
|
||||
if (stopped.status !== "incomplete") return primary;
|
||||
@@ -1956,7 +1963,7 @@ const waitForFrame = (
|
||||
signal?: AbortSignal,
|
||||
): Promise<RizinFrame> => {
|
||||
if (signal?.aborted)
|
||||
return Promise.reject(rizinCancellation("rizin_debug_command", signal));
|
||||
return Promise.reject(cancelledBy("rizin_debug_command", signal));
|
||||
const queued = session.frames.shift();
|
||||
if (queued !== undefined) {
|
||||
session.queuedFrameBytes -= queued.queueChargeBytes;
|
||||
@@ -1973,7 +1980,7 @@ const waitForFrame = (
|
||||
};
|
||||
const onAbort = (): void => {
|
||||
cleanup();
|
||||
reject(rizinCancellation("rizin_debug_command", signal));
|
||||
reject(cancelledBy("rizin_debug_command", carriedReason(signal?.reason)));
|
||||
};
|
||||
const timer = setTimeout(() => {
|
||||
cleanup();
|
||||
@@ -2002,21 +2009,17 @@ const waitForTurn = (
|
||||
if (signal === undefined) return previous;
|
||||
return waitForAbortable(previous, signal).then((outcome) => {
|
||||
if (outcome instanceof AbortedWait)
|
||||
throw rizinCancellation("rizin_debug_command", signal);
|
||||
throw cancelledBy("rizin_debug_command", outcome);
|
||||
});
|
||||
};
|
||||
/** Translate only this transport's typed cancellation, preserving opaque/null caller reasons. */
|
||||
const rizinCancellation = (
|
||||
operation: string,
|
||||
signal?: AbortSignal,
|
||||
cause?: unknown,
|
||||
options: AnalysisErrorOptions = {},
|
||||
): AnalysisCancelledError =>
|
||||
new AnalysisCancelledError(operation, {
|
||||
...options,
|
||||
cause: signal?.aborted
|
||||
? signal.reason
|
||||
: cause instanceof AnalysisCancelledError
|
||||
? cause.cause
|
||||
: cause,
|
||||
});
|
||||
/** The caller's reason when it aborted, else the reason an earlier failure carried. */
|
||||
const rizinCancellationReason = (
|
||||
signal: AbortSignal | undefined,
|
||||
cause: unknown,
|
||||
): CancellationReason =>
|
||||
isAborted(signal)
|
||||
? signal
|
||||
: cause instanceof AnalysisCancelledError
|
||||
? cause
|
||||
: carriedReason(cause);
|
||||
Reference in new issue
Block a user