Skip to content

Commit

Permalink
Add basic translation of NSExceptions to Status
Browse files Browse the repository at this point in the history
When the Swift API is misused from within a callback from core a NSException
may escape, which we want to propagate. Since this is always a fatal error that
cannot be programmatically handled by the user, we can always translate this to
UnknownError rather than trying to preserve the original error code.
  • Loading branch information
tgoyne committed Aug 29, 2024
1 parent a02402c commit 34ba317
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Fix crash during client app shutdown when Logger log level is set higher than Info. ([#7969](https://github.com/realm/realm-core/issues/7969), since v13.23.3)
* If File::rw_lock() fails to open a file the exception message does not contain the filename ([#7999](https://github.com/realm/realm-core/issues/7999), since v6.0.21)
* Fallback to hashed filename will fail if length of basename is between 240 and 250 ([#8007](https://github.com/realm/realm-core/issues/8007), since v10.0.0)
* Swift API misuse within a callback from core would result in an internal unreachable error rather than the exception being propagated properly ([#7836](https://github.com/realm/realm-core/issues/7836)).

### Breaking changes
* None.
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ let notSyncServerSources: [String] = [
"realm/disable_sync_to_disk.cpp",
"realm/error_codes.cpp",
"realm/exceptions.cpp",
"realm/exceptions.mm",
"realm/geospatial.cpp",
"realm/global_key.cpp",
"realm/group.cpp",
Expand Down
4 changes: 4 additions & 0 deletions src/realm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ if(NOT MSVC)
list(APPEND REALM_SOURCES util/interprocess_mutex.cpp)
endif()

if(APPLE)
list(APPEND REALM_SOURCES exceptions.mm)
endif()

if (REALM_ENABLE_GEOSPATIAL)
list(APPEND REALM_SOURCES geospatial.cpp)
list(APPEND REALM_INSTALL_HEADERS geospatial.hpp)
Expand Down
3 changes: 3 additions & 0 deletions src/realm/exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Exception::Exception(Status status)
{
}

// Apple implementation in exceptions.mm
#if !REALM_PLATFORM_APPLE
Status exception_to_status() noexcept
{
try {
Expand All @@ -80,6 +82,7 @@ Status exception_to_status() noexcept
REALM_UNREACHABLE();
}
}
#endif // !REALM_PLATFORM_APPLE

UnsupportedFileFormatVersion::UnsupportedFileFormatVersion(int version)
: Exception(ErrorCodes::UnsupportedFileFormatVersion,
Expand Down
45 changes: 45 additions & 0 deletions src/realm/exceptions.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*************************************************************************
*
* Copyright 2024 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**************************************************************************/

#include <realm/exceptions.hpp>

#include <realm/util/demangle.hpp>

#include <Foundation/Foundation.h>

namespace realm {
Status exception_to_status() noexcept
{
try {
throw;
}
catch (NSException* e) {
return Status(ErrorCodes::UnknownError, e.reason.UTF8String);
}
catch (const Exception& e) {
return e.to_status();
}
catch (const std::exception& e) {
return Status(ErrorCodes::UnknownError,
util::format("Caught std::exception of type %1: %2", util::get_type_name(e), e.what()));
}
catch (...) {
REALM_UNREACHABLE();
}
}
} // namespace realm
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ if (REALM_ENABLE_GEOSPATIAL)
list(APPEND CORE_TEST_SOURCES test_query_geo.cpp)
endif()

if (APPLE)
list(APPEND CORE_TEST_SOURCES test_nsexception.mm)
endif()

set(LARGE_TEST_SOURCES
large_tests/test_column_large.cpp
large_tests/test_strings.cpp)
Expand Down
39 changes: 39 additions & 0 deletions test/test_nsexception.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*************************************************************************
*
* Copyright 2024 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**************************************************************************/

#include "test.hpp"

#include "realm/status.hpp"

#include <Foundation/Foundation.h>

namespace realm {
namespace {
TEST(Status_NSException)
{
try {
@throw [NSException exceptionWithName:@"Exception Name" reason:@"Expected reason" userInfo:nil];
}
catch (...) {
auto status = exception_to_status();
CHECK_EQUAL(status.code(), ErrorCodes::UnknownError);
CHECK_EQUAL(status.reason(), "Expected reason");
}
}
} // namespace
} // namespace realm

0 comments on commit 34ba317

Please sign in to comment.