diff --git a/cpp/src/arrow/util/nameof.h b/cpp/src/arrow/util/nameof.h new file mode 100644 index 000000000000..a46ac7628695 --- /dev/null +++ b/cpp/src/arrow/util/nameof.h @@ -0,0 +1,86 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 + +namespace arrow { +namespace util { +namespace detail { + +#ifdef _MSC_VER +#define ARROW_PRETTY_FUNCTION __FUNCSIG__ +#else +#define ARROW_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#endif + +template +const char* raw() { + return ARROW_PRETTY_FUNCTION; +} + +template +size_t raw_sizeof() { + return sizeof(ARROW_PRETTY_FUNCTION); +} + +#undef ARROW_PRETTY_FUNCTION + +constexpr bool starts_with(char const* haystack, char const* needle) { + return needle[0] == '\0' || + (haystack[0] == needle[0] && starts_with(haystack + 1, needle + 1)); +} + +constexpr size_t search(char const* haystack, char const* needle) { + return haystack[0] == '\0' || starts_with(haystack, needle) + ? 0 + : search(haystack + 1, needle) + 1; +} + +const size_t typename_prefix = search(raw(), "void"); + +template +size_t struct_class_prefix() { +#ifdef _MSC_VER + return starts_with(raw() + typename_prefix, "struct ") + ? 7 + : starts_with(raw() + typename_prefix, "class ") ? 6 : 0; +#else + return 0; +#endif +} + +template +size_t typename_length() { + // raw_sizeof() - raw_sizeof() == (length of T's name) - strlen("void") + // (length of T's name) == raw_sizeof() - raw_sizeof() + strlen("void") + return raw_sizeof() - raw_sizeof() + 4; +} + +template +const char* typename_begin() { + return raw() + struct_class_prefix() + typename_prefix; +} + +} // namespace detail + +template +std::string nameof() { + return {detail::typename_begin(), detail::typename_length()}; +} + +} // namespace util +} // namespace arrow diff --git a/r/src/arrow_cpp11.h b/r/src/arrow_cpp11.h index 1aa3d582e858..f5d0cbc1a9c9 100644 --- a/r/src/arrow_cpp11.h +++ b/r/src/arrow_cpp11.h @@ -22,6 +22,8 @@ #include #undef Free +#include "arrow/util/nameof.h" + namespace cpp11 { template @@ -157,8 +159,17 @@ struct ns { template Pointer r6_to_pointer(SEXP self) { - return reinterpret_cast( - R_ExternalPtrAddr(Rf_findVarInFrame(self, arrow::r::symbols::xp))); + if (!Rf_inherits(self, "ArrowObject")) { + std::string type_name = + arrow::util::nameof::type>(); + cpp11::stop("Invalid R object for %s, must be an ArrowObject", type_name.c_str()); + } + void* p = R_ExternalPtrAddr(Rf_findVarInFrame(self, arrow::r::symbols::xp)); + if (p == nullptr) { + SEXP klass = Rf_getAttrib(self, R_ClassSymbol); + cpp11::stop("Invalid <%s>, external pointer to null", CHAR(STRING_ELT(klass, 0))); + } + return reinterpret_cast(p); } // T is either std::shared_ptr or std::unique_ptr diff --git a/r/tests/testthat/test-arrow.R b/r/tests/testthat/test-arrow.R index 7ef25ac672e5..f1b70e478c8f 100644 --- a/r/tests/testthat/test-arrow.R +++ b/r/tests/testthat/test-arrow.R @@ -47,3 +47,16 @@ r_only({ ) }) }) + +test_that("arrow gracefully fails to load objects from other sessions (ARROW-10071)", { + a <- Array$create(1:10) + tf <- tempfile(); on.exit(unlink(tf)) + saveRDS(a, tf) + + b <- readRDS(tf) + expect_error(b$length(), "Invalid ") +}) + +test_that("check for an ArrowObject in functions use std::shared_ptr", { + expect_error(Array__length(1), "Invalid R object") +})