Skip to content

Commit

Permalink
improve mixed files
Browse files Browse the repository at this point in the history
  • Loading branch information
mkornaukhov03 committed Nov 7, 2024
1 parent 7d6a0d4 commit 8fcfd6a
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 83 deletions.
2 changes: 1 addition & 1 deletion runtime-common/core/array_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ struct C$ArrayAccess : public may_be_mixed_base {
bool f$ArrayAccess$$offsetExists(class_instance<C$ArrayAccess> const & /*v$this*/, mixed const & /*v$offset*/) noexcept;
mixed f$ArrayAccess$$offsetGet(class_instance<C$ArrayAccess> const & /*v$this*/, mixed const & /*v$offset*/) noexcept;
void f$ArrayAccess$$offsetSet(class_instance<C$ArrayAccess> const & /*v$this*/, mixed const & /*v$offset*/, mixed const & /*v$value*/) noexcept;
void f$ArrayAccess$$offsetUnset(class_instance<C$ArrayAccess> const & /*v$this*/, mixed const & /*v$offset*/) noexcept;
void f$ArrayAccess$$offsetUnset(class_instance<C$ArrayAccess> const & /*v$this*/, mixed const & /*v$offset*/) noexcept;
19 changes: 17 additions & 2 deletions runtime-common/core/core-types/decl/mixed_decl.inl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ public:
mixed &operator[](const array<mixed>::const_iterator &it);
mixed &operator[](const array<mixed>::iterator &it);

/*
* The `set_value_return()` method is used in assignment chains like `$mix[0] = $mix[1] = foo();`.
* Normally, this could be transpiled to `v$mix[0] = v$mix[1] = f$foo()`. However, when `$mix` is an object
* implementing ArrayAccess, this doesn't work because `offsetGet()` returns by value, not by reference.
* This is why `mixed &operator[]` cannot be expressed using `offsetGet()`.
* Since returning by reference is not supported, we call `offsetSet($offset, $value)` and return `$value`.
*/

template<typename T>
inline mixed set_value_return(T key, const mixed &val);
mixed set_value_return(const mixed &key, const mixed &val);
mixed set_value_return(const string &key, const mixed &val);
mixed set_value_return(const array<mixed>::iterator &key, const mixed &val);
mixed set_value_return(const array<mixed>::const_iterator &key, const mixed &val);

void set_value(int64_t int_key, const mixed &v);
void set_value(int32_t key, const mixed &value) { set_value(int64_t{key}, value); }
void set_value(const string &string_key, const mixed &v);
Expand All @@ -109,7 +124,6 @@ public:
void set_value(const array<mixed>::const_iterator &it);
void set_value(const array<mixed>::iterator &it);

mixed set_by_index_return(const mixed &key, const mixed &val);

const mixed get_value(int64_t int_key) const;
const mixed get_value(int32_t key) const { return get_value(int64_t{key}); }
Expand Down Expand Up @@ -306,4 +320,5 @@ inline bool less_string_number_as_php8(bool php7_result, const string &lhs, T rh
template<class InputClass>
mixed f$to_mixed(const class_instance<InputClass> &instance) noexcept;
template<class ResultClass>
ResultClass from_mixed(const mixed &m, const string &) noexcept;
ResultClass from_mixed(const mixed &m, const string &) noexcept;
std::pair<class_instance<C$ArrayAccess>, bool> try_as_array_access(const mixed &) noexcept;
169 changes: 98 additions & 71 deletions runtime-common/core/core-types/definition/mixed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,33 @@

#include "common/wrappers/likely.h"
#include "runtime-common/core/runtime-core.h"
#include <cassert>

namespace {

string to_string_without_warning(const mixed &m) {
switch (m.get_type()) {
case mixed::type::NUL:
return string();
case mixed::type::BOOLEAN:
return (m.as_bool() ? string("1", 1) : string());
case mixed::type::INTEGER:
return string(m.as_int());
case mixed::type::FLOAT:
return string(m.as_double());
case mixed::type::STRING:
return m.as_string();
case mixed::type::ARRAY:
return string("Array", 5);
case mixed::type::OBJECT: {
const char *s = m.get_type_or_class_name();
return string(s, strlen(s));
}
default:
__builtin_unreachable();
}
}

} // namespace

void mixed::copy_from(const mixed &other) {
switch (other.get_type()) {
Expand Down Expand Up @@ -537,28 +563,6 @@ double mixed::to_float() const {
}
}

static string to_string_without_warning(const mixed &m) {
switch (m.get_type()) {
case mixed::type::NUL:
return string();
case mixed::type::BOOLEAN:
return (m.as_bool() ? string("1", 1) : string());
case mixed::type::INTEGER:
return string(m.as_int());
case mixed::type::FLOAT:
return string(m.as_double());
case mixed::type::STRING:
return m.as_string();
case mixed::type::ARRAY:
return string("Array", 5);
case mixed::type::OBJECT: {
const char *s = m.get_type_or_class_name();
return string(s, strlen(s));
}
default:
__builtin_unreachable();
}
}

const string mixed::to_string() const {
switch (get_type()) {
Expand Down Expand Up @@ -976,12 +980,29 @@ const string mixed::get_type_str() const {
return string(get_type_c_str());
}

// TODO
// Should we warn more precisely: "Class XXX does not implement \\ArrayAccess" or just
// "Cannot use XXX as array, index = YYY" will be OK?
std::pair<class_instance<C$ArrayAccess>, bool> try_as_array_access(const mixed &m) noexcept {
using T = class_instance<C$ArrayAccess>;

// For now, it does dynamic cast twice
// We can get rid of one of them
if (likely(m.is_a<C$ArrayAccess>())) {
return {from_mixed<T>(m, string()), true};
}

return {T{}, false};
}

bool mixed::empty_on(const mixed &key) const {
// 1) `if (type_ == type::OBJECT)` is semantically redundant
// 2) it may be ok becuase of fast check
if (type_ == type::OBJECT) {
// todo f$is_a
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
return !f$ArrayAccess$$offsetExists(xxx, key) || f$ArrayAccess$$offsetGet(xxx, key).empty();
};
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
return !f$ArrayAccess$$offsetExists(as_aa, key) || f$ArrayAccess$$offsetGet(as_aa, key).empty();
}
}

return get_value(key).empty();
}
Expand Down Expand Up @@ -1060,9 +1081,6 @@ int64_t mixed::compare(const mixed &rhs) const {
return three_way_comparison(to_float(), rhs.to_float());
}




mixed &mixed::operator[](int64_t int_key) {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::STRING) {
Expand All @@ -1073,8 +1091,7 @@ mixed &mixed::operator[](int64_t int_key) {
if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
type_ = type::ARRAY;
new(&as_array()) array<mixed>();
}
else {
} else {
php_warning("Cannot use a value \"%s\" of type %s as an array, index = %" PRIi64, to_string_without_warning(*this).c_str(), get_type_or_class_name(), int_key);
return empty_value<mixed>();
}
Expand Down Expand Up @@ -1143,15 +1160,27 @@ mixed &mixed::operator[](const array<mixed>::iterator &it) {
return as_array()[it];
}

mixed mixed::set_value_return(const mixed &key, const mixed &val) {
if (get_type() == type::OBJECT) {
set_value(key, val);
return val;
}
return (*this)[key] = val;
}

mixed mixed::set_by_index_return(const mixed &key, const mixed &val) {
mixed mixed::set_value_return(const string &key, const mixed &val) {
if (get_type() == type::OBJECT) {
// TODO check with f$is_a
// TODO may be more efficient way?
set_value(key, val);
return val;
}
return (*this)[key] = val;
}

mixed mixed::set_value_return(const array<mixed>::iterator &key, const mixed &val) {
return (*this)[key] = val;
}

mixed mixed::set_value_return(const array<mixed>::const_iterator &key, const mixed &val) {
return (*this)[key] = val;
}

Expand Down Expand Up @@ -1181,12 +1210,11 @@ void mixed::set_value(int64_t int_key, const mixed &v) {
return;
}

// TODO don't forget to use f$is_a !!!
// It'll look like an instance cast
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetSet(xxx, int_key, v);
return;
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetSet(as_aa, int_key, v);
return;
}
}

if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
Expand Down Expand Up @@ -1225,12 +1253,11 @@ void mixed::set_value(const string &string_key, const mixed &v) {
return;
}

// TODO don't forget to use f$is_a !!!
// It'll look like an instance cast
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetSet(xxx, string_key, v);
return;
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetSet(as_aa, string_key, v);
return;
}
}
if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
type_ = type::ARRAY;
Expand Down Expand Up @@ -1298,10 +1325,10 @@ const mixed mixed::get_value(int64_t int_key) const {
return string(1, as_string()[static_cast<string::size_type>(int_key)]);
}

// TODO check with f$is_a
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
return f$ArrayAccess$$offsetGet(xxx, int_key);
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
return f$ArrayAccess$$offsetGet(as_aa, int_key);
}
}

if (get_type() != type::NUL && (get_type() != type::BOOLEAN || as_bool())) {
Expand All @@ -1327,10 +1354,10 @@ const mixed mixed::get_value(const string &string_key) const {
return string(1, as_string()[static_cast<string::size_type>(int_val)]);
}

// TODO check with f$is_a
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
return f$ArrayAccess$$offsetGet(xxx, string_key);
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
return f$ArrayAccess$$offsetGet(as_aa, string_key);
}
}

if (get_type() != type::NUL && (get_type() != type::BOOLEAN || as_bool())) {
Expand Down Expand Up @@ -1390,15 +1417,14 @@ const mixed mixed::get_value(const array<mixed>::iterator &it) const {
return as_array().get_value(it);
}

// TODO USE f$is_a before every `from_mixed()` !!!
void mixed::push_back(const mixed &v) {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetSet(xxx, Optional<bool>{}, v);
return;
}
else if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetSet(as_aa, Optional<bool>{}, v);
return;
}
} else if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
type_ = type::ARRAY;
new(&as_array()) array<mixed>();
} else {
Expand All @@ -1413,11 +1439,11 @@ void mixed::push_back(const mixed &v) {
const mixed mixed::push_back_return(const mixed &v) {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetSet(xxx, Optional<bool>{}, v);
return v;
}
else if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetSet(as_aa, Optional<bool>{}, v);
return v;
}
} else if (get_type() == type::NUL || (get_type() == type::BOOLEAN && !as_bool())) {
type_ = type::ARRAY;
new(&as_array()) array<mixed>();
} else {
Expand All @@ -1432,9 +1458,9 @@ const mixed mixed::push_back_return(const mixed &v) {
bool mixed::isset(int64_t int_key) const {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
// TODO think about numeric-like string
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
return f$ArrayAccess$$offsetExists(xxx, int_key);
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
return f$ArrayAccess$$offsetExists(as_aa, int_key);
}
}
if (get_type() == type::STRING) {
int_key = as_string().get_correct_index(int_key);
Expand Down Expand Up @@ -1480,11 +1506,11 @@ bool mixed::isset(double double_key) const {

void mixed::unset(int64_t int_key) {
if (unlikely (get_type() != type::ARRAY)) {
// TODO f$is_a
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetUnset(xxx, int_key);
return;
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetUnset(as_aa, int_key);
return;
}
}

if (get_type() != type::NUL && (get_type() != type::BOOLEAN || as_bool())) {
Expand All @@ -1500,8 +1526,9 @@ void mixed::unset(int64_t int_key) {
void mixed::unset(const mixed &v) {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetUnset(xxx, v);
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetUnset(as_aa, v);
}
return;
}

Expand Down
23 changes: 16 additions & 7 deletions runtime-common/core/core-types/definition/mixed.inl
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ template <class ...MaybeHash>
bool mixed::isset(const string &string_key, MaybeHash ...maybe_hash) const {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
// TODO think about numeric-like string
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
return f$ArrayAccess$$offsetExists(xxx, string_key);
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
return f$ArrayAccess$$offsetExists(as_aa, string_key);
}
}

int64_t int_key{std::numeric_limits<int64_t>::max()};
Expand All @@ -117,9 +117,10 @@ template <class ...MaybeHash>
void mixed::unset(const string &string_key, MaybeHash ...maybe_hash) {
if (unlikely (get_type() != type::ARRAY)) {
if (get_type() == type::OBJECT) {
auto xxx = from_mixed<class_instance<C$ArrayAccess>>(*this, string());
f$ArrayAccess$$offsetUnset(xxx, string_key);
return;
if (auto [as_aa, succ] = try_as_array_access(*this); succ) {
f$ArrayAccess$$offsetUnset(as_aa, string_key);
return;
}
}

if (get_type() != type::NUL && (get_type() != type::BOOLEAN || as_bool())) {
Expand All @@ -131,7 +132,6 @@ void mixed::unset(const string &string_key, MaybeHash ...maybe_hash) {
as_array().unset(string_key, maybe_hash...);
}


inline mixed::type mixed::get_type() const {
return type_;
}
Expand Down Expand Up @@ -340,3 +340,12 @@ ResultClass from_mixed(const mixed &m, const string &) noexcept {
return ResultClass::create_from_base_raw_ptr(dynamic_cast<abstract_refcountable_php_interface *>(m.as_object_ptr<ResultClass>()));
}
}

template<typename T>
inline mixed mixed::set_value_return(T key, const mixed &val) {
if (get_type() == type::OBJECT) {
set_value(key, val);
return val;
}
return (*this)[key] = val;
}
4 changes: 2 additions & 2 deletions runtime-common/core/runtime-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
#define FFI_CALL(call) ({ dl::CriticalSectionGuard critical_section___; (call); })
#define FFI_INVOKE_CALLBACK(call) ({ dl::NonCriticalSectionGuard non_critical_section___; (call); })

#define SET_MIXED_BY_INDEX(mix, idx, val) mix.set_by_index_return(idx, val)
#define SAFE_SET_MIXED_BY_INDEX(mix, idx, val, val_type) ({ val_type val_tmp___ = val; mix.set_by_index_return(idx, std::move(val_tmp___)); })
#define SET_MIXED_BY_INDEX(mix, idx, val) mix.set_value_return(idx, val)
#define SAFE_SET_MIXED_BY_INDEX(mix, idx, val, val_type) ({ val_type val_tmp___ = val; mix.set_value_return(idx, std::move(val_tmp___)); })
#define SET_ARR_ACC_BY_INDEX(obj, idx, val, method) ({mixed val_tmp___ = val; method(obj, idx, val_tmp___); val_tmp___;}) // it's always safe

#define SAFE_SET_OP(a, op, b, b_type) ({b_type b_tmp___ = b; a op std::move(b_tmp___);})
Expand Down

0 comments on commit 8fcfd6a

Please sign in to comment.