Skip to content

Commit

Permalink
Add basic translation of NSExceptions to Status (#8010)
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 authored Aug 30, 2024
1 parent a02402c commit f9e36f7
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 f9e36f7

Please sign in to comment.