diff --git a/compiler/compiler.cmake b/compiler/compiler.cmake index 04a06d73f..d5e4f23b5 100644 --- a/compiler/compiler.cmake +++ b/compiler/compiler.cmake @@ -150,7 +150,7 @@ prepend(KPHP_COMPILER_PIPES_SOURCES pipes/ file-to-tokens.cpp filter-only-actually-used.cpp final-check.cpp - fix-chaining-assignment-for-array-access.cpp + fix-array-access.cpp fix-returns.cpp gen-tree-postprocess.cpp generate-virtual-methods.cpp diff --git a/compiler/compiler.cpp b/compiler/compiler.cpp index 829d2a76a..f9c2ffb32 100644 --- a/compiler/compiler.cpp +++ b/compiler/compiler.cpp @@ -64,7 +64,7 @@ #include "compiler/pipes/file-to-tokens.h" #include "compiler/pipes/filter-only-actually-used.h" #include "compiler/pipes/final-check.h" -#include "compiler/pipes/fix-chaining-assignment-for-array-access.h" +#include "compiler/pipes/fix-array-access.h" #include "compiler/pipes/fix-returns.h" #include "compiler/pipes/gen-tree-postprocess.h" #include "compiler/pipes/generate-virtual-methods.h" @@ -285,7 +285,7 @@ bool compiler_execute(CompilerSettings *settings) { >> PassC{} >> PassC{} >> PassC{} - >> PassC{} + >> PassC{} >> PassC{} >> PassC{} >> PassC{} diff --git a/compiler/pipes/fix-array-access.cpp b/compiler/pipes/fix-array-access.cpp new file mode 100644 index 000000000..5bf45accc --- /dev/null +++ b/compiler/pipes/fix-array-access.cpp @@ -0,0 +1,143 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2024 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#include "compiler/pipes/fix-array-access.h" + +#include "auto/compiler/vertex/vertex-types.h" +#include "common/algorithms/contains.h" +#include "compiler/data/class-data.h" +#include "compiler/data/vertex-adaptor.h" +#include "compiler/inferring/primitive-type.h" +#include "compiler/inferring/public.h" +#include + +static VertexPtr on_set(VertexAdaptor set) { + if (set->lhs()->type() != op_func_call) { + return set; + } + + auto func_call = set->lhs().try_as(); + if (func_call->auto_inserted) { + if (func_call->func_id->local_name() == "offsetGet") { + auto obj_arg = func_call->args()[0]; + const auto *tpe = tinf::get_type(obj_arg); // funny + + assert(tpe); + assert(tpe->get_real_ptype() == tp_Class); + + auto klass = tpe->class_type(); + + const auto *method = klass->get_instance_method("offsetSet"); + + if (!method) { + kphp_error(method, fmt_format("Class {} does not implement offsetSet", klass->name).c_str()); + return set; + } + + auto sub_val = set->rhs(); + + auto key = func_call->args()[1]; + + auto zzz = VertexAdaptor::create(key, sub_val, obj_arg); + zzz->set_method = method->function; + + return zzz; + } + } + return set; +} + +static VertexPtr on_unset(VertexAdaptor unset) { + if (auto func_call = unset->expr().try_as()) { + if (func_call->func_id->name.find("offsetGet") != std::string::npos) { + auto klass = func_call->func_id->class_id; + // TODO assume here that all is good + assert(klass && "bad klass for unset"); + + const auto *unset_method = klass->get_instance_method("offsetUnset"); + assert(unset_method && "bad method for unset"); + + func_call->str_val = unset_method->global_name(); + func_call->func_id = unset_method->function; + return func_call; + } + } + + return unset; +} + +static VertexPtr on_isset(VertexAdaptor isset) { + if (auto func_call = isset->expr().try_as()) { + if (func_call->func_id->name.find("offsetGet") != std::string::npos) { + auto klass = func_call->func_id->class_id; + // TODO assume here that all is good + assert(klass && "bad klass for isset"); + + const auto *isset_method = klass->get_instance_method("offsetExists"); + assert(isset_method && "bad method for isset"); + + func_call->str_val = isset_method->global_name(); + func_call->func_id = isset_method->function; + return func_call; + } + } + + return isset; +} + +static VertexPtr on_empty(VertexAdaptor empty_call) { + if (auto offset_get_call = empty_call->args()[0].try_as()) { + if (offset_get_call->func_id->name.find("offsetGet") != std::string::npos) { + auto klass = offset_get_call->func_id->class_id; + // TODO assume here that all is good + assert(klass && "bad klass for isset"); + + const auto *isset_method = klass->get_instance_method("offsetExists"); + assert(isset_method && "bad method for isset"); + + auto exists_call = offset_get_call.clone(); + + exists_call->str_val = isset_method->global_name(); + exists_call->func_id = isset_method->function; + /* + for empty smth like: + op_log_or + op_log_not + op_func_call f$ClassName$exists + ... + op_func_call empty + op_func_call f$ClassName$get + ... + */ + + // TODO fix locations + auto as_or = VertexAdaptor::create(VertexAdaptor::create(exists_call), empty_call); + + return as_or; + } + } + + return empty_call; +} + +// TODO maybe on_enter? Think about it +// on_exit now generates x2 warnings, but may be the only correct way (may be not xD) +VertexPtr FixArrayAccessPass::on_exit_vertex(VertexPtr root) { + if (auto set = root.try_as()) { + return on_set(set); + } + if (auto unset = root.try_as()) { + return on_unset(unset); + } + if (auto isset = root.try_as()) { + return on_isset(isset); + } + if (auto func_call = root.try_as()) { + if (func_call->func_id->is_extern() && func_call->func_id->name == "empty") { + return on_empty(func_call); + } + } + + return root; +} diff --git a/compiler/pipes/fix-chaining-assignment-for-array-access.h b/compiler/pipes/fix-array-access.h similarity index 68% rename from compiler/pipes/fix-chaining-assignment-for-array-access.h rename to compiler/pipes/fix-array-access.h index 6fd64b677..f909d5ada 100644 --- a/compiler/pipes/fix-chaining-assignment-for-array-access.h +++ b/compiler/pipes/fix-array-access.h @@ -6,10 +6,10 @@ #include "compiler/function-pass.h" -class FixChainingAssignmentForArrayAccessPass final : public FunctionPassBase { +class FixArrayAccessPass final : public FunctionPassBase { public: std::string get_description() override { - return "Fix chaining assignment for ArrayAccess"; + return "Fix functions related to ArrayAccess"; } VertexPtr on_exit_vertex(VertexPtr root) final; diff --git a/compiler/pipes/fix-chaining-assignment-for-array-access.cpp b/compiler/pipes/fix-chaining-assignment-for-array-access.cpp deleted file mode 100644 index cdf470188..000000000 --- a/compiler/pipes/fix-chaining-assignment-for-array-access.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Compiler for PHP (aka KPHP) -// Copyright (c) 2020 LLC «V Kontakte» -// Distributed under the GPL v3 License, see LICENSE.notice.txt - -#include "compiler/pipes/fix-chaining-assignment-for-array-access.h" - -#include "auto/compiler/vertex/vertex-types.h" -#include "compiler/data/class-data.h" -#include "compiler/data/vertex-adaptor.h" -#include "compiler/inferring/primitive-type.h" -#include "compiler/inferring/public.h" -#include - -static VertexPtr on_set(VertexAdaptor set) { - if (set->lhs()->type() != op_func_call) { - return set; - } - - auto func_call = set->lhs().try_as(); - if (func_call->auto_inserted) { - if (func_call->func_id->local_name() == "offsetGet") { - auto obj_arg = func_call->args()[0]; - const auto *tpe = tinf::get_type(obj_arg); // funny - - assert(tpe); - assert(tpe->get_real_ptype() == tp_Class); - - auto klass = tpe->class_type(); - - const auto *method = klass->get_instance_method("offsetSet"); - - if (!method) { - kphp_error(method, fmt_format("Class {} does not implement offsetSet", klass->name).c_str()); - return set; - } - - auto sub_val = set->rhs(); - - auto key = func_call->args()[1]; - - auto zzz = VertexAdaptor::create(key, sub_val, obj_arg); - zzz->set_method = method->function; - - return zzz; - } - } - return set; -} - -// TODO maybe on_enter? Think about it -// on_exit now generates x2 warnings, but may be the only correct way (may be not xD) -VertexPtr FixChainingAssignmentForArrayAccessPass::on_exit_vertex(VertexPtr root) { - if (auto set = root.try_as()) { - return on_set(set); - } - - return root; -}