Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions runtime/runtime.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ prepend(KPHP_RUNTIME_SOURCES ${BASE_DIR}/runtime/
string_buffer.cpp
string_cache.cpp
string_functions.cpp
to-array-processor.cpp
tl/rpc_tl_query.cpp
tl/rpc_response.cpp
tl/rpc_server.cpp
Expand Down
7 changes: 7 additions & 0 deletions runtime/to-array-processor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2020 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#include "runtime/to-array-processor.h"

bool ArrayProcessorError::recursion_depth_exeeded = false;
51 changes: 42 additions & 9 deletions runtime/to-array-processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#pragma once

#include <string_view>
#include <tuple>
#include <unordered_map>

#include "common/mixin/not_copyable.h"
#include "common/smart_ptrs/singleton.h"
Expand Down Expand Up @@ -35,8 +37,9 @@ class ShapeKeyDemangle : vk::not_copyable {

class ToArrayVisitor {
public:
explicit ToArrayVisitor(bool with_class_names)
: with_class_names_(with_class_names) {}
explicit ToArrayVisitor(bool with_class_names, std::size_t depth)
: with_class_names_(with_class_names)
, depth_(++depth) {}

array<mixed> flush_result() && noexcept {
return std::move(result_);
Expand Down Expand Up @@ -83,12 +86,12 @@ class ToArrayVisitor {

template<class I>
void process_impl(const char *field_name, const class_instance<I> &instance) {
add_value(field_name, instance.is_null() ? mixed{} : f$to_array_debug(instance, with_class_names_));
add_value(field_name, instance.is_null() ? mixed{} : to_array_debug_impl(instance, with_class_names_, depth_));
}

template<class ...Args>
void process_impl(const char *field_name, const std::tuple<Args...> &value) {
ToArrayVisitor tuple_processor{with_class_names_};
ToArrayVisitor tuple_processor{with_class_names_, 0};
tuple_processor.result_.reserve(sizeof...(Args), 0, true);

process_tuple(value, tuple_processor, std::index_sequence_for<Args...>{});
Expand All @@ -97,7 +100,7 @@ class ToArrayVisitor {

template<size_t ...Is, typename ...T>
void process_impl(const char *field_name, const shape<std::index_sequence<Is...>, T...> &value) {
ToArrayVisitor shape_processor{with_class_names_};
ToArrayVisitor shape_processor{with_class_names_, 0};
shape_processor.result_.reserve(sizeof...(Is), 0, true);

process_shape(value, shape_processor);
Expand All @@ -115,17 +118,26 @@ class ToArrayVisitor {

array<mixed> result_;
bool with_class_names_{false};
std::size_t depth_{0};
};

struct ArrayProcessorError {
static bool recursion_depth_exeeded;
};

template<class T>
array<mixed> f$to_array_debug(const class_instance<T> &klass, bool with_class_names = false) {
array<mixed> to_array_debug_impl(const class_instance<T> &klass, bool with_class_names = false, std::size_t depth = 0) {
array<mixed> result;
if (depth > 64) {
ArrayProcessorError::recursion_depth_exeeded = true;
return result;
}
if (klass.is_null()) {
return result;
}

if constexpr (!std::is_empty_v<T>) {
ToArrayVisitor visitor{with_class_names};
ToArrayVisitor visitor{with_class_names, depth};
klass.get()->accept(visitor);
result = std::move(visitor).flush_result();
}
Expand All @@ -136,17 +148,38 @@ array<mixed> f$to_array_debug(const class_instance<T> &klass, bool with_class_na
return result;
}

template<class T>
array<mixed> f$to_array_debug(const class_instance<T> &klass, bool with_class_names = false) {
ArrayProcessorError::recursion_depth_exeeded = false;
auto result = to_array_debug_impl(klass, with_class_names);
if (ArrayProcessorError::recursion_depth_exeeded) {
ArrayProcessorError::recursion_depth_exeeded = false;
return {};
}
return result;
}

template<class... Args>
array<mixed> f$to_array_debug(const std::tuple<Args...> &tuple, bool with_class_names = false) {
ToArrayVisitor visitor{with_class_names};
ArrayProcessorError::recursion_depth_exeeded = false;
ToArrayVisitor visitor{with_class_names, 0};
ToArrayVisitor::process_tuple(tuple, visitor, std::index_sequence_for<Args...>{});
if (ArrayProcessorError::recursion_depth_exeeded) {
ArrayProcessorError::recursion_depth_exeeded = false;
return {};
}
return std::move(visitor).flush_result();
}

template<size_t... Indexes, typename... T>
array<mixed> f$to_array_debug(const shape<std::index_sequence<Indexes...>, T...> &shape, bool with_class_names = false) {
ToArrayVisitor visitor{with_class_names};
ArrayProcessorError::recursion_depth_exeeded = false;
ToArrayVisitor visitor{with_class_names, 0};
ToArrayVisitor::process_shape(shape, visitor);
if (ArrayProcessorError::recursion_depth_exeeded) {
ArrayProcessorError::recursion_depth_exeeded = false;
return {};
}
return std::move(visitor).flush_result();
}

Expand Down
8 changes: 0 additions & 8 deletions tests/phpt/dl/1034_to_array_debug_shape.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,7 @@ function __construct() {

function to_array_debug_shape(bool $with_class_names) {
$shape = shape(['foo' => 42, 'bar' => new A]);

$dump = to_array_debug($shape, $with_class_names);
#ifndef KPHP
$dump = ['foo' => 42, 'bar' => ['a_obj' => ['i' => 88, 'b_shape' => ['baz' => 'qax']]]];
if ($with_class_names) {
$dump['bar']['__class_name'] = 'A';
$dump['bar']['a_obj']['__class_name'] = 'B';
}
#endif
var_dump($dump);
}

Expand Down
8 changes: 0 additions & 8 deletions tests/phpt/dl/1035_to_array_debug_tuple.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,7 @@ function __construct() {

function to_array_debug_tuple(bool $with_class_names) {
$tuple = tuple(42, new A);

$dump = to_array_debug($tuple, $with_class_names);
#ifndef KPHP
$dump = [42, ['a_obj' => ['i' => 88, 'b_tuple' => [77, 'qax']]]];
if ($with_class_names) {
$dump[1]['__class_name'] = 'A';
$dump[1]['a_obj']['__class_name'] = 'B';
}
#endif
var_dump($dump);
}

Expand Down
33 changes: 33 additions & 0 deletions tests/phpt/dl/1042_to_array_debug_reursion_limit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@ok
<?php
require_once 'kphp_tester_include.php';

class Chain {
public ?Chain $next = null;
}

function test_chain_objects() {
$obj = new Chain;
$obj->next = new Chain;
var_dump(instance_to_array($obj));
}

function test_max_depth_level() {
$obj = new Chain;
$ptr = $obj;
for ($i = 0; $i < 64; ++$i) {
$ptr->next = new Chain;
$ptr = $ptr->next;
}

// 64 lvl depth is ok
var_dump(count(instance_to_array($obj)));

$ptr->next = new Chain;

// allowed depth exceeded
var_dump(instance_to_array($obj));
}

test_chain_objects();
test_max_depth_level();