Skip to content

Commit

Permalink
Add SwiftRestrictedErrorInfo to preserve Swift errors across WinRT bo…
Browse files Browse the repository at this point in the history
…undaries.
  • Loading branch information
tristanlabelle committed Aug 30, 2024
1 parent 3252fb8 commit e66a03a
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 5 deletions.
39 changes: 39 additions & 0 deletions Support/Sources/WindowsRuntime/SwiftRestrictedErrorInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import COM

// Wraps a Swift Error object into an `IRestrictedErrorInfo` to preserve it across WinRT boundaries.
internal final class SwiftRestrictedErrorInfo: COMPrimaryExport<IRestrictedErrorInfoProjection>,
IRestrictedErrorInfoProtocol, IErrorInfoProtocol {
override class var implements: [COMImplements] { [
.init(IErrorInfoProjection.self)
] }

public let error: any Error

public init(error: any Error) {
self.error = error
}

public var hresult: HResult { (self.error as? COMError)?.hresult ?? HResult.fail }
public var message: String { String(describing: error) }

// IErrorInfo
public var guid: GUID { get throws { throw HResult.Error.fail } }
public var source: String? { get throws { throw HResult.Error.fail } }
public var description: String? { self.message }
public var helpFile: String? { get throws { throw HResult.Error.fail } }
public var helpContext: UInt32 { get throws { throw HResult.Error.fail } }

// IRestrictedErrorInfo
public func getErrorDetails(
description: inout String?,
error: inout HResult,
restrictedDescription: inout String?,
capabilitySid: inout String?) throws {
description = self.description
error = self.hresult
restrictedDescription = description
capabilitySid = nil
}

public var reference: String? { get throws { throw HResult.Error.fail } }
}
27 changes: 23 additions & 4 deletions Support/Sources/WindowsRuntime/WinRTError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public struct WinRTError: COMError, CustomStringConvertible {
public static func throwIfFailed(_ hresult: WindowsRuntime_ABI.SWRT_HResult) throws {
let hresult = HResultProjection.toSwift(hresult)
guard let error = WinRTError(hresult: hresult, captureErrorInfo: true) else { return }
if let swiftErrorInfo = error.errorInfo as? SwiftRestrictedErrorInfo, swiftErrorInfo.hresult == hresult {
// This was originally a Swift error, throw it as such.
throw swiftErrorInfo.error
}
throw error
}

Expand All @@ -29,14 +33,29 @@ public struct WinRTError: COMError, CustomStringConvertible {
return HResult.ok.value
}
catch let error {
let hresult = (error as? COMError)?.hresult ?? HResult.fail
var message = (try? PrimitiveProjection.String.toABI(error.localizedDescription)) ?? nil
let errorInfo = SwiftRestrictedErrorInfo(error: error)
let hresult = errorInfo.hresult.value
var message = (try? PrimitiveProjection.String.toABI(errorInfo.message)) ?? nil
defer { PrimitiveProjection.String.release(&message) }
WindowsRuntime_ABI.SWRT_RoOriginateError(hresult.value, message)
return hresult.value
var iunknown = try? IUnknownProjection.toABI(errorInfo)
defer { IUnknownProjection.release(&iunknown) }
WindowsRuntime_ABI.SWRT_RoOriginateLanguageException(hresult, message, iunknown)
return hresult
}
}

public static func clear() {
WindowsRuntime_ABI.SWRT_RoClearError()
}

public static func captureErrorContext(hresult: HResult) throws {
try HResult.throwIfFailed(SWRT_RoCaptureErrorContext(hresult.value))
}

public static func failFastWithErrorContext(hresult: HResult) throws {
SWRT_RoFailFastWithErrorContext(hresult.value)
}

public static func getRestrictedErrorInfo() throws -> IRestrictedErrorInfo? {
var restrictedErrorInfo: UnsafeMutablePointer<SWRT_IRestrictedErrorInfo>?
defer { IRestrictedErrorInfoProjection.release(&restrictedErrorInfo) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
#pragma once

#include <stdbool.h>
#include "SWRT/windows/unknwn.h"
#include "SWRT/windows/restrictederrorinfo.h"
#include "SWRT/windows/winstring.h"

typedef enum {
SWRT_RO_ERROR_REPORTING_NONE = 0x00000000,
SWRT_RO_ERROR_REPORTING_SUPPRESSEXCEPTIONS = 0x00000001,
SWRT_RO_ERROR_REPORTING_FORCEEXCEPTIONS = 0x00000002,
SWRT_RO_ERROR_REPORTING_USESETERRORINFO = 0x00000004,
SWRT_RO_ERROR_REPORTING_SUPPRESSSETERRORINFO = 0x00000008,
} SWRT_RO_ERROR_REPORTING_FLAGS;

SWRT_HResult SWRT_GetRestrictedErrorInfo(SWRT_IRestrictedErrorInfo** ppRestrictedErrorInfo);
SWRT_HResult SWRT_RoCaptureErrorContext(SWRT_HResult hr);
void SWRT_RoClearError();
void SWRT_RoFailFastWithErrorContext(SWRT_HResult hrError);
SWRT_HResult SWRT_RoGetErrorReportingFlags(uint32_t* pflags);
SWRT_HResult SWRT_RoGetMatchingRestrictedErrorInfo(SWRT_HResult hrIn, SWRT_IRestrictedErrorInfo** ppRestrictedErrorInfo);
bool SWRT_RoOriginateError(SWRT_HResult error, SWRT_HString message);
bool SWRT_RoOriginateErrorW(SWRT_HResult error, uint32_t cchMax, const char16_t* message);
bool SWRT_RoOriginateErrorW(SWRT_HResult error, uint32_t cchMax, const char16_t* message);
bool SWRT_RoOriginateLanguageException(SWRT_HResult error, SWRT_HString message, SWRT_IUnknown* languageException);
SWRT_HResult SWRT_RoReportUnhandledError(SWRT_IRestrictedErrorInfo* pRestrictedErrorInfo);
SWRT_HResult SWRT_RoSetErrorReportingFlags(uint32_t flags);
SWRT_HResult SWRT_RoTransformError(SWRT_HResult oldError, SWRT_HResult newError, SWRT_HString message);
28 changes: 28 additions & 0 deletions Support/Sources/WindowsRuntime_ABI/roerrorapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ SWRT_HResult SWRT_GetRestrictedErrorInfo(SWRT_IRestrictedErrorInfo** ppRestricte
return (SWRT_HResult)GetRestrictedErrorInfo((IRestrictedErrorInfo**)ppRestrictedErrorInfo);
}

SWRT_HResult SWRT_RoCaptureErrorContext(SWRT_HResult hr) {
return (SWRT_HResult)RoCaptureErrorContext((HRESULT)hr);
}

void SWRT_RoClearError() {
RoClearError();
}

void SWRT_RoFailFastWithErrorContext(SWRT_HResult hrError) {
RoFailFastWithErrorContext((HRESULT)hrError);
}

SWRT_HResult SWRT_RoGetErrorReportingFlags(uint32_t* pflags) {
return (SWRT_HResult)RoGetErrorReportingFlags(pflags);
}

SWRT_HResult SWRT_RoGetMatchingRestrictedErrorInfo(SWRT_HResult hrIn, SWRT_IRestrictedErrorInfo** ppRestrictedErrorInfo) {
return (SWRT_HResult)RoGetMatchingRestrictedErrorInfo((HRESULT)hrIn, (IRestrictedErrorInfo**)ppRestrictedErrorInfo);
}
Expand All @@ -21,4 +33,20 @@ bool SWRT_RoOriginateError(SWRT_HResult error, SWRT_HString message) {

bool SWRT_RoOriginateErrorW(SWRT_HResult error, uint32_t cchMax, const char16_t* message) {
return RoOriginateErrorW((HRESULT)error, (UINT)cchMax, (PCWSTR)message);
}

bool SWRT_RoOriginateLanguageException(SWRT_HResult error, SWRT_HString message, SWRT_IUnknown* languageException) {
return RoOriginateLanguageException((HRESULT)error, (HSTRING)message, (IUnknown*)languageException);
}

SWRT_HResult SWRT_RoReportUnhandledError(SWRT_IRestrictedErrorInfo* pRestrictedErrorInfo) {
return (SWRT_HResult)RoReportUnhandledError((IRestrictedErrorInfo*)pRestrictedErrorInfo);
}

SWRT_HResult SWRT_RoSetErrorReportingFlags(uint32_t flags) {
return (SWRT_HResult)RoSetErrorReportingFlags(flags);
}

SWRT_HResult SWRT_RoTransformError(SWRT_HResult oldError, SWRT_HResult newError, SWRT_HString message) {
return (SWRT_HResult)RoTransformError((HRESULT)oldError, (HRESULT)newError, (HSTRING)message);
}

0 comments on commit e66a03a

Please sign in to comment.