Prevent segfaults on orphaned subview objects.
This commit is contained in:
parent
aeba61a787
commit
7faf5d6fd6
|
@ -181,8 +181,9 @@ namespace php_arma
|
|||
fci.retval = &retval;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
native->for_each([&fci, &fcc](auto&& val) {
|
||||
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(&val));
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
native->for_each([&fci, &fcc, parent](auto&& val) {
|
||||
ZVAL_OBJ(&fci.params[0], mapval_dense<T>::create(parent, &val));
|
||||
zend_call_function(&fci, &fcc);
|
||||
zval_ptr_dtor(&fci.params[0]);
|
||||
zval_ptr_dtor(fci.retval);
|
||||
|
|
|
@ -10,11 +10,11 @@ namespace php_arma
|
|||
{
|
||||
template <typename T, const char *Name>
|
||||
zend_always_inline
|
||||
void property_declare(zend_class_entry *ce)
|
||||
void property_declare_scalar(zend_class_entry *ce)
|
||||
{
|
||||
zval property;
|
||||
zval_set_scalar(&property, static_cast<T>(0));
|
||||
zend_declare_property(ce, Name, str::get_len(Name), &property, ZEND_ACC_PUBLIC);
|
||||
property_declare<Name, ZEND_ACC_PUBLIC>(ce, &property);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -393,8 +393,9 @@ namespace php_arma
|
|||
void complex<T>::ce_init(zend_class_entry *parent_ce)
|
||||
{
|
||||
ce = class_register<php_name::val>(parent_ce, me);
|
||||
property_declare<T, real_property_name>(ce);
|
||||
property_declare<T, imag_property_name>(ce);
|
||||
property_declare_scalar<T, real_property_name>(ce);
|
||||
property_declare_scalar<T, imag_property_name>(ce);
|
||||
|
||||
object_handlers_init(&handlers);
|
||||
handlers.read_dimension = read_dimension;
|
||||
handlers.write_property = write_property;
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
namespace php_arma
|
||||
{
|
||||
template <typename T, typename T1>
|
||||
PHP_ARMA_METHOD(dense_matrix, __invoke, T, T1)
|
||||
template <typename T, typename ChildT>
|
||||
PHP_ARMA_METHOD(dense_matrix, __invoke, T, ChildT)
|
||||
{
|
||||
zend_long i, j;
|
||||
ZEND_PARSE_PARAMETERS_START(1, 2)
|
||||
|
@ -25,13 +25,14 @@ namespace php_arma
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
|
||||
zend_object *zobj;
|
||||
try {
|
||||
if (EX_NUM_ARGS() == 1) {
|
||||
zobj = mapval_dense<T>::create(&native->operator()(i));
|
||||
zobj = mapval_dense<T>::create(parent, &native->operator()(i));
|
||||
} else {
|
||||
zobj = mapval_dense<T>::create(&native->operator()(i, j));
|
||||
zobj = mapval_dense<T>::create(parent, &native->operator()(i, j));
|
||||
}
|
||||
} catch (const std::logic_error& err) {
|
||||
// Index out of bounds.
|
||||
|
@ -42,8 +43,8 @@ namespace php_arma
|
|||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
template <typename T, typename T1>
|
||||
PHP_ARMA_METHOD(dense_matrix, at, T, T1)
|
||||
template <typename T, typename ChildT>
|
||||
PHP_ARMA_METHOD(dense_matrix, at, T, ChildT)
|
||||
{
|
||||
zend_long i, j;
|
||||
ZEND_PARSE_PARAMETERS_START(1, 2)
|
||||
|
@ -53,12 +54,13 @@ namespace php_arma
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
|
||||
zend_object *zobj;
|
||||
if (EX_NUM_ARGS() == 1) {
|
||||
zobj = mapval_dense<T>::create(&native->operator[](i));
|
||||
zobj = mapval_dense<T>::create(parent, &native->operator[](i));
|
||||
} else {
|
||||
zobj = mapval_dense<T>::create(&native->at(i, j));
|
||||
zobj = mapval_dense<T>::create(parent, &native->at(i, j));
|
||||
}
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
@ -87,7 +89,8 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->cols(first_col, last_col)));
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
auto zobj = ret_t::create(parent, std::move(native->cols(first_col, last_col)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -103,7 +106,8 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->rows(first_row, last_row)));
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
auto zobj = ret_t::create(parent, std::move(native->rows(first_row, last_row)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -121,12 +125,13 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->submat(first_row, first_col, last_row, last_col)));
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
auto zobj = ret_t::create(parent, std::move(native->submat(first_row, first_col, last_row, last_col)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
template <typename T, typename T1>
|
||||
PHP_ARMA_METHOD(dense_matrix, diag, T, T1)
|
||||
template <typename T, typename ChildT>
|
||||
PHP_ARMA_METHOD(dense_matrix, diag, T, ChildT)
|
||||
{
|
||||
zend_long k = 0;
|
||||
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||
|
@ -135,10 +140,11 @@ namespace php_arma
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
|
||||
zend_object *zobj;
|
||||
try {
|
||||
zobj = diagview<T>::create(std::move(native->diag(k)));
|
||||
zobj = diagview<T>::create(parent, std::move(native->diag(k)));
|
||||
} catch (const std::logic_error& err) {
|
||||
// requested diagonal out of bounds
|
||||
throw_exception(err.what());
|
||||
|
|
|
@ -175,7 +175,7 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->head_cols(n_cols)));
|
||||
auto zobj = ret_t::create(&EX(This), std::move(native->head_cols(n_cols)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->head_rows(n_rows)));
|
||||
auto zobj = ret_t::create(&EX(This), std::move(native->head_rows(n_rows)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->tail_cols(n_cols)));
|
||||
auto zobj = ret_t::create(&EX(This), std::move(native->tail_cols(n_cols)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ namespace php_arma
|
|||
using ret_t = typename ChildT::subview_t;
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto zobj = ret_t::create(std::move(native->tail_rows(n_rows)));
|
||||
auto zobj = ret_t::create(&EX(This), std::move(native->tail_rows(n_rows)));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace php_arma
|
|||
PHP_ARMA_FENTRY(me)
|
||||
));
|
||||
ce->create_object = object_non_constructible;
|
||||
declare_subview_parent(ce);
|
||||
|
||||
object_handlers_init(&handlers);
|
||||
handlers.offset = sizeof(native_t);
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
#define PHP_ARMA_DIAGONAL_HH
|
||||
|
||||
#include "php_arma.hh"
|
||||
#include "instantiable.hh"
|
||||
#include "complex.hh"
|
||||
#include "shared_methods.hh"
|
||||
|
||||
namespace php_arma
|
||||
{
|
||||
template <typename T> struct mat;
|
||||
template <typename T, typename T1> struct linear;
|
||||
|
||||
template <typename T, bool IsSparse>
|
||||
struct diagonal
|
||||
struct diagonal : instantiable<diagonal<T, IsSparse>, true>
|
||||
{
|
||||
using native_t = std::conditional_t<IsSparse, arma::spdiagview<T>, arma::diagview<T>>;
|
||||
using orig_t = mat<T>;
|
||||
|
@ -26,13 +26,6 @@ namespace php_arma
|
|||
|
||||
friend void diagonal_init();
|
||||
|
||||
template <typename... Ts>
|
||||
zend_always_inline
|
||||
static zend_object *create(Ts&&... args)
|
||||
{
|
||||
return object_create_ctor<native_t>(ce, &handlers, args...);
|
||||
}
|
||||
|
||||
PHP_ARMA_CE_HANDLRES_DECLARE();
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// php-armadillo/instantiable.hh
|
||||
//
|
||||
// @Author CismonX
|
||||
//
|
||||
|
||||
#ifndef PHP_ARMA_INSTANTIABLE_HH
|
||||
#define PHP_ARMA_INSTANTIABLE_HH
|
||||
|
||||
#include "php_arma.hh"
|
||||
|
||||
namespace php_arma
|
||||
{
|
||||
template <typename ChildT, bool IsSubview> struct instantiable;
|
||||
|
||||
template <typename ChildT>
|
||||
struct instantiable<ChildT, false>
|
||||
{
|
||||
template <typename... Ts>
|
||||
zend_always_inline
|
||||
static zend_object *create(Ts&&... args)
|
||||
{
|
||||
return object_create_ctor<typename ChildT::native_t>(ChildT::ce, &ChildT::handlers, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ChildT>
|
||||
struct instantiable<ChildT, true>
|
||||
{
|
||||
template <typename... Ts>
|
||||
zend_always_inline
|
||||
static zend_object *create(zval *parent, Ts&&... args)
|
||||
{
|
||||
auto zobj = object_create_ctor<typename ChildT::native_t>(ChildT::ce, &ChildT::handlers, args...);
|
||||
object_set_property(zobj, 0, parent);
|
||||
return zobj;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !PHP_ARMA_INSTANTIABLE_HH
|
|
@ -44,6 +44,8 @@ namespace php_arma
|
|||
{
|
||||
ce = class_register<php_name::val>(parent_ce, me);
|
||||
ce->create_object = object_non_constructible;
|
||||
declare_subview_parent(ce);
|
||||
|
||||
object_handlers_init(&handlers);
|
||||
handlers.offset = sizeof(native_t);
|
||||
handlers.clone_obj = nullptr;
|
||||
|
|
|
@ -56,19 +56,22 @@ namespace php_arma
|
|||
}
|
||||
|
||||
zend_always_inline
|
||||
static zend_object *create(native_t init_val)
|
||||
static zend_object *create(zval *parent, native_t init_val)
|
||||
{
|
||||
zend_object *zobj;
|
||||
if constexpr (IsSparse) {
|
||||
return object_create<native_t>(ce, [&init_val](auto obj) {
|
||||
zobj = object_create<native_t>(ce, [&init_val](auto obj) {
|
||||
memcpy(obj, &init_val, sizeof(native_t));
|
||||
return &handlers;
|
||||
});
|
||||
} else {
|
||||
return object_create<native_t>(ce, [init_val](auto obj) {
|
||||
zobj = object_create<native_t>(ce, [init_val](auto obj) {
|
||||
*obj = init_val;
|
||||
return &handlers;
|
||||
});
|
||||
}
|
||||
object_set_property(zobj, 0, parent);
|
||||
return zobj;
|
||||
}
|
||||
|
||||
PHP_ARMA_CE_HANDLRES_DECLARE();
|
||||
|
|
10
src/mat.cc
10
src/mat.cc
|
@ -29,7 +29,7 @@ namespace php_arma
|
|||
Z_PARAM_LONG(fill)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto zobj = create(n_rows, n_cols);
|
||||
auto zobj = mat::create(n_rows, n_cols);
|
||||
fill::invoke(ZOBJ_NATIVE(zobj), fill);
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace php_arma
|
|||
Z_PARAM_STR(text)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto zobj = create(ZSTR_VAL(text));
|
||||
auto zobj = mat::create(ZSTR_VAL(text));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ namespace php_arma
|
|||
|
||||
auto num_rows = zend_hash_num_elements(Z_ARR_P(arr));
|
||||
if (UNEXPECTED(num_rows == 0)) {
|
||||
RETURN_OBJ(create());
|
||||
RETURN_OBJ(mat::create());
|
||||
}
|
||||
auto first_row = zend_hash_index_find(Z_ARR_P(arr), 0);
|
||||
if (UNEXPECTED(first_row == nullptr || Z_TYPE_P(first_row) != IS_ARRAY)) {
|
||||
|
@ -65,10 +65,10 @@ namespace php_arma
|
|||
}
|
||||
auto num_cols = zend_hash_num_elements(Z_ARR_P(first_row));
|
||||
if (UNEXPECTED(num_rows == 0)) {
|
||||
RETURN_OBJ(create());
|
||||
RETURN_OBJ(mat::create());
|
||||
}
|
||||
|
||||
auto zobj = create(num_rows, num_cols);
|
||||
auto zobj = mat::create(num_rows, num_cols);
|
||||
auto native = ZOBJ_NATIVE(zobj);
|
||||
size_t idx_row = 0;
|
||||
size_t idx_col = 0;
|
||||
|
|
10
src/mat.hh
10
src/mat.hh
|
@ -8,6 +8,7 @@
|
|||
#define PHP_ARMA_MAT_HH
|
||||
|
||||
#include "php_arma.hh"
|
||||
#include "instantiable.hh"
|
||||
#include "complex.hh"
|
||||
|
||||
namespace php_arma
|
||||
|
@ -15,7 +16,7 @@ namespace php_arma
|
|||
template <typename T> struct subview_mat;
|
||||
|
||||
template <typename T>
|
||||
struct mat
|
||||
struct mat : instantiable<mat<T>, false>
|
||||
{
|
||||
using native_t = arma::Mat<T>;
|
||||
using orig_t = mat<T>;
|
||||
|
@ -25,13 +26,6 @@ namespace php_arma
|
|||
|
||||
friend void mat_init();
|
||||
|
||||
template <typename... Ts>
|
||||
zend_always_inline
|
||||
static zend_object *create(Ts&&... args)
|
||||
{
|
||||
return object_create_ctor<native_t>(ce, &handlers, args...);
|
||||
}
|
||||
|
||||
PHP_ARMA_CE_HANDLRES_DECLARE();
|
||||
|
||||
private:
|
||||
|
|
|
@ -180,6 +180,7 @@ namespace php_arma
|
|||
|
||||
constexpr const char namespace_prefix[] = "Arma\\";
|
||||
constexpr const char internal_prefix[] = "Internal\\";
|
||||
constexpr const char empty[] = "";
|
||||
|
||||
template <const char *Str>
|
||||
constexpr auto with_arma_prefix
|
||||
|
@ -334,6 +335,19 @@ namespace php_arma
|
|||
return ce;
|
||||
}
|
||||
|
||||
template <const char *Name, int AccessType>
|
||||
zend_always_inline
|
||||
void property_declare(zend_class_entry *ce, zval *zv)
|
||||
{
|
||||
zend_declare_property(ce, Name, str::get_len(Name), zv, AccessType);
|
||||
}
|
||||
|
||||
zend_always_inline
|
||||
void declare_subview_parent(zend_class_entry *ce)
|
||||
{
|
||||
property_declare<str::empty, ZEND_ACC_PRIVATE>(ce, &EG(uninitialized_zval));
|
||||
}
|
||||
|
||||
zend_always_inline
|
||||
void object_handlers_init(zend_object_handlers *handlers)
|
||||
{
|
||||
|
@ -354,6 +368,17 @@ namespace php_arma
|
|||
return reinterpret_cast<T*>(zobj) - 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
zend_always_inline
|
||||
zval *get_parent_zval(zval *current)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, typename T::orig_t>) {
|
||||
return current;
|
||||
} else {
|
||||
return OBJ_PROP_NUM(Z_OBJ_P(current), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper functions for function entry.
|
||||
|
||||
zend_always_inline
|
||||
|
@ -398,10 +423,12 @@ namespace php_arma
|
|||
zend_always_inline
|
||||
zend_object *object_create(zend_class_entry *ce, F&& init)
|
||||
{
|
||||
auto obj = reinterpret_cast<T*>(emalloc(sizeof(T) + sizeof(zend_object) - sizeof(zval)));
|
||||
auto obj = reinterpret_cast<T*>(emalloc(sizeof(T) +
|
||||
sizeof(zend_object) + zend_object_properties_size(ce)));
|
||||
auto zobj = to_zend_object(obj);
|
||||
zobj->handlers = init(obj);
|
||||
zend_object_std_init(zobj, ce);
|
||||
object_properties_init(zobj, ce);
|
||||
return zobj;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,11 @@ namespace php_arma
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
|
||||
zend_object *zobj;
|
||||
try {
|
||||
zobj = mapval_dense<T>::create(&native->operator()(i));
|
||||
zobj = mapval_dense<T>::create(parent, &native->operator()(i));
|
||||
} catch (const std::logic_error& err) {
|
||||
// Index out of bounds.
|
||||
throw_exception(err.what());
|
||||
|
@ -41,8 +42,9 @@ namespace php_arma
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
auto native = THIS_NATIVE;
|
||||
auto parent = get_parent_zval<ChildT>(&EX(This));
|
||||
|
||||
auto zobj = mapval_dense<T>::create(&native->operator[](i));
|
||||
auto zobj = mapval_dense<T>::create(parent, &native->operator[](i));
|
||||
RETVAL_OBJ(zobj);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace php_arma
|
|||
PHP_ARMA_FENTRY(me)
|
||||
));
|
||||
ce->create_object = object_non_constructible;
|
||||
declare_subview_parent(ce);
|
||||
|
||||
object_handlers_init(&handlers);
|
||||
handlers.offset = sizeof(native_t);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define PHP_ARMA_SUBVIEW_MAT_HH
|
||||
|
||||
#include "php_arma.hh"
|
||||
#include "instantiable.hh"
|
||||
#include "complex.hh"
|
||||
|
||||
namespace php_arma
|
||||
|
@ -15,7 +16,7 @@ namespace php_arma
|
|||
template <typename T> struct mat;
|
||||
|
||||
template <typename T>
|
||||
struct subview_mat
|
||||
struct subview_mat : instantiable<subview_mat<T>, true>
|
||||
{
|
||||
using native_t = arma::subview<T>;
|
||||
using orig_t = mat<T>;
|
||||
|
@ -25,13 +26,6 @@ namespace php_arma
|
|||
|
||||
friend void subview_mat_init();
|
||||
|
||||
template <typename... Ts>
|
||||
zend_always_inline
|
||||
static zend_object *create(Ts&&... args)
|
||||
{
|
||||
return object_create_ctor<native_t>(ce, &handlers, args...);
|
||||
}
|
||||
|
||||
PHP_ARMA_CE_HANDLRES_DECLARE();
|
||||
|
||||
private:
|
||||
|
|
|
@ -18,5 +18,15 @@ batch_assert('read/write access of `MapVal`',
|
|||
[7, $mat(0, 1)->val()]
|
||||
);
|
||||
|
||||
// When parent class is destroyed...
|
||||
$orphaned_mapval = Arma\IMat::fromString('1 2; 3 4')->at(1, 1);
|
||||
batch_assert('orphaned `MapVal`',
|
||||
[$orphaned_mapval->val(), 4]
|
||||
);
|
||||
$orphaned_mapval->setTo(7);
|
||||
batch_assert('orphaned `MapVal`',
|
||||
[$orphaned_mapval->val(), 7]
|
||||
);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
|
|
|
@ -27,5 +27,13 @@ batch_assert('`SvMat`',
|
|||
[PHP_INT_MAX, $mat->at(1, 2)->val()]
|
||||
);
|
||||
|
||||
// When parent class is destroyed...
|
||||
$orphaned_submat = Arma\IMat::fromString('1 2; 3 4')->cols(0, 0);
|
||||
|
||||
batch_assert('orphaned `SvMat`',
|
||||
[$orphaned_submat->at(0, 0)->val(), 1],
|
||||
[$orphaned_submat->at(1, 0)->val(), 3]
|
||||
);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
|
|
|
@ -18,9 +18,13 @@ $diag->rawPrint();
|
|||
$diag->at(1)->setTo($diag->at(0)->val());
|
||||
$mat->rawPrint();
|
||||
|
||||
// When parent class is destroyed...
|
||||
Arma\IMat::fromString("1 2; 3 4")->diag(-1)->rawPrint();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
4
|
||||
1 2
|
||||
3 1
|
||||
3
|
||||
|
|
Reference in New Issue