cii-text-analysis/vendor/rubix/tensor/ext/kernel/fcall.c
Jéremy Munsch f68d2ac333
premier poc
2026-02-11 08:24:04 +01:00

632 lines
16 KiB
C

/*
* This file is part of the Zephir.
*
* (c) Phalcon Team <team@zephir-lang.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. If you did not receive
* a copy of the license it is available through the world-wide-web at the
* following url: https://docs.zephir-lang.com/en/latest/license
*/
#include <php.h>
#include "php_ext.h"
#include <Zend/zend_API.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_execute.h>
#include "kernel/main.h"
#include "kernel/fcall.h"
#include "kernel/memory.h"
#include "kernel/operators.h"
#include "kernel/exception.h"
#include "kernel/backtrace.h"
#include "kernel/variables.h"
int zephir_has_constructor_ce(const zend_class_entry *ce)
{
do {
if (ce->constructor != NULL) {
return 1;
}
ce = ce->parent;
} while (ce);
return 0;
}
/**
* Creates a unique key to cache the current method/function call address for the current scope
*/
static int zephir_make_fcall_key(zend_string* s, zephir_call_type type, zend_class_entry *ce, zval *function, const zend_class_entry* called_scope)
{
const zend_class_entry *calling_scope;
unsigned char t;
#if PHP_VERSION_ID >= 70100
calling_scope = zend_get_executed_scope();
#else
calling_scope = EG(scope);
#endif
switch (type) {
case zephir_fcall_parent:
if (UNEXPECTED(!calling_scope || !calling_scope->parent)) {
return FAILURE;
}
calling_scope = calling_scope->parent;
break;
case zephir_fcall_static:
calling_scope = called_scope;
if (UNEXPECTED(!calling_scope)) {
return FAILURE;
}
break;
case zephir_fcall_self:
/* EG(scope) */
break;
case zephir_fcall_function:
if (Z_TYPE_P(function) == IS_OBJECT) {
return FAILURE;
}
calling_scope = NULL;
called_scope = NULL;
break;
case zephir_fcall_ce:
calling_scope = ce;
called_scope = ce;
break;
case zephir_fcall_method:
if (Z_TYPE_P(function) == IS_OBJECT) {
return FAILURE;
}
calling_scope = ce;
called_scope = ce;
break;
default:
return FAILURE;
}
if (called_scope == calling_scope) {
/* Calls within the same scope, this won't trigger magic methods or failures due to restricted visibility */
t = 0;
}
else if (called_scope && calling_scope && (instanceof_function(called_scope, calling_scope) || instanceof_function(calling_scope, called_scope))) {
/* Calls within the same chain of inheritance; can call protected methods */
t = 1;
}
else {
/* Can safely call only public methods */
t = 2;
}
{
char* cls = calling_scope ? ZSTR_VAL(calling_scope->name) : "";
size_t cls_len = calling_scope ? ZSTR_LEN(calling_scope->name) : 0;
char* mth = NULL;
size_t mth_len = 0;
char* buf;
if (Z_TYPE_P(function) == IS_STRING) {
mth = Z_STRVAL_P(function);
mth_len = Z_STRLEN_P(function);
}
else if (Z_TYPE_P(function) == IS_ARRAY) {
zval *method;
HashTable *function_hash = Z_ARRVAL_P(function);
if (
function_hash->nNumOfElements == 2
&& ((method = zend_hash_index_find(function_hash, 1)) != NULL)
&& Z_TYPE_P(method) == IS_STRING
) {
mth = Z_STRVAL_P(method);
mth_len = Z_STRLEN_P(method);
}
}
if (cls_len + 1 + mth_len + sizeof(unsigned char) > 255) {
return FAILURE;
}
ZSTR_LEN(s) = cls_len + 1 + mth_len + sizeof(unsigned char);
buf = ZSTR_VAL(s);
zend_str_tolower_copy(buf, cls, cls_len + 1);
zend_str_tolower_copy(buf + cls_len + 1, mth, mth_len);
buf[cls_len + 1 + mth_len] = t;
buf[cls_len + 1 + mth_len + sizeof(t)] = '\0';
}
ZSTR_H(s) = zend_hash_func(ZSTR_VAL(s), ZSTR_LEN(s));
return SUCCESS;
}
static void resolve_callable(zval* retval, zephir_call_type type, zend_class_entry *ce, zval *object, zval *function)
{
if (type == zephir_fcall_function || IS_ARRAY == Z_TYPE_P(function) || IS_OBJECT == Z_TYPE_P(function)) {
ZVAL_COPY(retval, function);
return;
}
array_init_size(retval, 2);
zend_hash_real_init(Z_ARRVAL_P(retval), 1);
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(retval)) {
zval q;
switch (type) {
case zephir_fcall_parent:
zend_string_addref(i_parent);
ZVAL_STR(&q, i_parent);
ZEND_HASH_FILL_ADD(&q);
break;
case zephir_fcall_self:
zend_string_addref(i_self);
ZVAL_STR(&q, i_self);
ZEND_HASH_FILL_ADD(&q);
break;
case zephir_fcall_static:
zend_string_addref(i_static);
ZVAL_STR(&q, i_static);
ZEND_HASH_FILL_ADD(&q);
break;
case zephir_fcall_ce:
assert(ce);
zend_string_addref(ce->name);
ZVAL_STR(&q, ce->name);
ZEND_HASH_FILL_ADD(&q);
break;
default:
assert(object);
Z_TRY_ADDREF_P(object);
ZEND_HASH_FILL_ADD(object);
break;
}
Z_TRY_ADDREF_P(function);
ZEND_HASH_FILL_ADD(function);
} ZEND_HASH_FILL_END();
}
static void populate_fcic(zend_fcall_info_cache* fcic, zephir_call_type type, zend_class_entry* ce, zval *this_ptr, zval *func, zend_class_entry* called_scope)
{
zend_class_entry* calling_scope;
#if PHP_VERSION_ID < 70300
fcic->initialized = 0;
#endif
fcic->function_handler = NULL;
if (type == zephir_fcall_function && Z_TYPE_P(func) == IS_STRING) {
#if PHP_VERSION_ID < 70300
fcic->initialized = 1;
#endif
fcic->called_scope = NULL;
fcic->calling_scope = NULL;
fcic->object = NULL;
return;
}
fcic->called_scope = called_scope;
#if PHP_VERSION_ID >= 70100
calling_scope = zend_get_executed_scope();
#else
calling_scope = EG(scope);
#endif
fcic->object = this_ptr ? Z_OBJ_P(this_ptr) : NULL;
switch (type) {
case zephir_fcall_parent:
if (UNEXPECTED(!calling_scope || !calling_scope->parent)) {
return;
}
fcic->calling_scope = calling_scope->parent;
break;
case zephir_fcall_static:
fcic->calling_scope = fcic->called_scope;
if (UNEXPECTED(!calling_scope)) {
return;
}
break;
case zephir_fcall_self:
fcic->calling_scope = calling_scope;
break;
case zephir_fcall_ce:
fcic->calling_scope = ce;
fcic->called_scope = ce;
break;
case zephir_fcall_function:
case zephir_fcall_method:
if (Z_TYPE_P(func) == IS_OBJECT) {
if (Z_OBJ_HANDLER_P(func, get_closure) && Z_OBJ_HANDLER_P(func, get_closure)(func, &fcic->calling_scope, &fcic->function_handler, &fcic->object) == SUCCESS) {
fcic->called_scope = fcic->calling_scope;
break;
}
return;
}
fcic->calling_scope = this_ptr ? Z_OBJCE_P(this_ptr) : NULL;
fcic->called_scope = fcic->calling_scope;
break;
default:
return;
}
#if PHP_VERSION_ID < 70300
fcic->initialized = 1;
#endif
}
/**
* Calls a function/method in the PHP userland
*/
int zephir_call_user_function(zval *object_pp, zend_class_entry *obj_ce, zephir_call_type type,
zval *function_name, zval *retval_ptr, zephir_fcall_cache_entry **cache_entry, int cache_slot, uint32_t param_count,
zval *params[])
{
zval local_retval_ptr;
int status;
zend_fcall_info fci;
zend_fcall_info_cache fcic;
zend_zephir_globals_def *zephir_globals_ptr = ZEPHIR_VGLOBAL;
char fcall_key[sizeof(zend_string) + 256];
int key_ok = FAILURE;
zephir_fcall_cache_entry *temp_cache_entry = NULL;
zval callable;
zend_class_entry* called_scope = zend_get_called_scope(EG(current_execute_data));
assert(obj_ce || !object_pp);
ZVAL_UNDEF(&callable);
ZVAL_UNDEF(&local_retval_ptr);
if ((!cache_entry || !*cache_entry) && zephir_globals_ptr->cache_enabled) {
int reload_cache = 1;
if (cache_slot > 0 && zephir_globals_ptr->scache[cache_slot]) {
reload_cache = 0;
temp_cache_entry = zephir_globals_ptr->scache[cache_slot];
if (cache_entry) {
*cache_entry = temp_cache_entry;
}
}
if (reload_cache) {
key_ok = zephir_make_fcall_key((zend_string*)fcall_key, type, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_P(object_pp) : obj_ce), function_name, called_scope);
if (SUCCESS == key_ok) {
zend_string* zs = (zend_string*)fcall_key;
GC_SET_REFCOUNT(zs, 1);
GC_TYPE_INFO(zs) = IS_STRING;
temp_cache_entry = zend_hash_find_ptr(zephir_globals_ptr->fcache, zs);
if (temp_cache_entry) {
cache_entry = &temp_cache_entry;
}
}
}
}
fci.size = sizeof(fci);
#if PHP_VERSION_ID < 70100
fci.function_table = obj_ce ? &obj_ce->function_table : EG(function_table);
fci.symbol_table = NULL;
#endif
fci.object = object_pp ? Z_OBJ_P(object_pp) : NULL;
fci.retval = retval_ptr ? retval_ptr : &local_retval_ptr;
fci.param_count = param_count;
fci.params = NULL;
fci.no_separation = 1;
#if PHP_VERSION_ID < 70300
fcic.initialized = 0;
#endif
if (cache_entry && *cache_entry) {
/* We have a cache record, initialize scope */
populate_fcic(&fcic, type, obj_ce, object_pp, function_name, called_scope);
if (!fcic.function_handler) {
fcic.function_handler = *cache_entry;
}
ZVAL_UNDEF(&fci.function_name);
}
else if ((cache_entry && !*cache_entry) || zephir_globals_ptr->cache_enabled) {
/* The caller is interested in caching OR we have the call cache enabled */
resolve_callable(&callable, type, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_P(object_pp) : obj_ce), object_pp, function_name);
zend_is_callable_ex(&callable, fci.object, IS_CALLABLE_CHECK_SILENT, NULL, &fcic, NULL);
}
#if PHP_VERSION_ID < 70300
if (!fcic.initialized) {
resolve_callable(&callable, type, (object_pp && type != zephir_fcall_ce ? Z_OBJCE_P(object_pp) : obj_ce), object_pp, function_name);
ZVAL_COPY_VALUE(&fci.function_name, &callable);
}
#endif
#ifdef _MSC_VER
zval *p = emalloc(sizeof(zval) * (fci.param_count + 1));
#else
zval p[fci.param_count];
#endif
uint32_t i;
for (i = 0; i < fci.param_count; ++i) {
ZVAL_COPY_VALUE(&p[i], params[i]);
}
fci.params = p;
status = zend_call_function(&fci, &fcic);
#ifdef _MSC_VER
efree(p);
#endif
if (Z_TYPE(callable) != IS_UNDEF) {
zval_ptr_dtor(&callable);
}
/* Skip caching IF:
* call failed OR there was an exception (to be safe) OR cache key is not defined OR
* fcall cache was deinitialized OR we have a slot cache
*/
int initialized = 1;
#if PHP_VERSION_ID < 70300
initialized = fcic.initialized;
#endif
if (EXPECTED(status != FAILURE) && !EG(exception) && SUCCESS == key_ok && initialized && !temp_cache_entry) {
zephir_fcall_cache_entry *cache_entry_temp = fcic.function_handler;
if (cache_entry) {
*cache_entry = cache_entry_temp;
if (cache_slot > 0) {
zephir_globals_ptr->scache[cache_slot] = *cache_entry;
}
}
if (zephir_globals_ptr->cache_enabled) {
zend_string *zs = (zend_string*)fcall_key;
zend_hash_str_add_ptr(zephir_globals_ptr->fcache, ZSTR_VAL(zs), ZSTR_LEN(zs), cache_entry_temp);
}
}
if (!retval_ptr) {
zval_ptr_dtor(&local_retval_ptr);
}
else if (FAILURE == status || EG(exception)) {
ZVAL_NULL(retval_ptr);
} else if (Z_TYPE_P(retval_ptr) == IS_ARRAY) {
SEPARATE_ARRAY(retval_ptr);
}
return status;
}
int zephir_call_func_aparams(zval *return_value_ptr, const char *func_name, uint32_t func_length,
zephir_fcall_cache_entry **cache_entry, int cache_slot,
uint32_t param_count, zval **params)
{
int status;
zval rv, *rvp = return_value_ptr ? return_value_ptr : &rv;
ZVAL_UNDEF(&rv);
#ifndef ZEPHIR_RELEASE
if (return_value_ptr != NULL && Z_TYPE_P(return_value_ptr) > IS_NULL) {
fprintf(stderr, "%s: *return_value_ptr must be NULL\n", __func__);
zephir_print_backtrace();
abort();
}
#endif
zval f;
ZVAL_STRINGL(&f, func_name, func_length);
status = zephir_call_user_function(NULL, NULL, zephir_fcall_function, &f, rvp, cache_entry, cache_slot, param_count, params);
zval_ptr_dtor(&f);
if (status == FAILURE && !EG(exception)) {
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined function %s()", func_name);
} else if (EG(exception)) {
status = FAILURE;
}
if (!return_value_ptr) {
zval_ptr_dtor(&rv);
}
return status;
}
int zephir_call_zval_func_aparams(zval *return_value_ptr, zval *func_name,
zephir_fcall_cache_entry **cache_entry, int cache_slot,
uint32_t param_count, zval **params)
{
int status;
zval rv, *rvp = return_value_ptr ? return_value_ptr : &rv;
ZVAL_UNDEF(&rv);
#ifndef ZEPHIR_RELEASE
if (return_value_ptr != NULL && Z_TYPE_P(return_value_ptr) > IS_NULL) {
fprintf(stderr, "%s: *return_value_ptr must be NULL\n", __func__);
zephir_print_backtrace();
abort();
}
#endif
status = zephir_call_user_function(NULL, NULL, zephir_fcall_function, func_name, rvp, cache_entry, cache_slot, param_count, params);
if (status == FAILURE && !EG(exception)) {
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined function %s()", Z_TYPE_P(func_name) == IS_STRING ? Z_STRVAL_P(func_name) : "undefined");
} else if (EG(exception)) {
status = FAILURE;
}
if (!return_value_ptr) {
zval_ptr_dtor(&rv);
}
return status;
}
int zephir_call_class_method_aparams(zval *return_value, zend_class_entry *ce, zephir_call_type type, zval *object,
const char *method_name, uint32_t method_len,
zephir_fcall_cache_entry **cache_entry, int cache_slot,
uint32_t param_count, zval **params)
{
int status;
#ifndef ZEPHIR_RELEASE
if (return_value != NULL && Z_TYPE_P(return_value) > IS_NULL) {
fprintf(stderr, "%s: *return_value must be IS_NULL or IS_UNDEF\n", __func__);
zephir_print_backtrace();
abort();
}
#endif
if (object && Z_TYPE_P(object) != IS_OBJECT) {
zephir_throw_exception_format(spl_ce_RuntimeException, "Trying to call method %s on a non-object", method_name);
if (return_value) {
ZVAL_NULL(return_value);
}
return FAILURE;
}
zval method;
ZVAL_STRINGL(&method, method_name, method_len);
status = zephir_call_user_function(object, ce, type, &method, return_value, cache_entry, cache_slot, param_count, params);
zval_ptr_dtor(&method);
if (status == FAILURE && !EG(exception)) {
switch (type) {
case zephir_fcall_parent:
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined method parent::%s()", method_name);
break;
case zephir_fcall_self:
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined method self::%s()", method_name);
break;
case zephir_fcall_static:
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined method static::%s()", method_name);
break;
case zephir_fcall_ce:
case zephir_fcall_method:
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), method_name);
break;
default:
zephir_throw_exception_format(spl_ce_RuntimeException, "Call to undefined method ?::%s()", method_name);
}
} else if (EG(exception)) {
status = FAILURE;
}
return status;
}
/**
* Replaces call_user_func_array avoiding function lookup
* This function does not return FAILURE if an exception has ocurred
*/
int zephir_call_user_func_array_noex(zval *return_value, zval *handler, zval *params)
{
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
char *is_callable_error = NULL;
int status = FAILURE;
if (params && Z_TYPE_P(params) != IS_ARRAY) {
ZVAL_NULL(return_value);
php_error_docref(NULL, E_WARNING, "Invalid arguments supplied for zephir_call_user_func_array_noex()");
return FAILURE;
}
zend_fcall_info_init(handler, 0, &fci, &fci_cache, NULL, &is_callable_error);
if (is_callable_error) {
zend_error(E_WARNING, "%s", is_callable_error);
efree(is_callable_error);
} else {
status = SUCCESS;
}
if (status == SUCCESS) {
zend_fcall_info_args(&fci, params);
fci.retval = return_value;
zend_call_function(&fci, &fci_cache);
zend_fcall_info_args_clear(&fci, 1);
}
if (EG(exception)) {
status = SUCCESS;
}
return status;
}
/**
* If a retval_ptr is specified, PHP's implementation of zend_eval_stringl
* simply prepends a "return " which causes only the first statement to be executed
*/
void zephir_eval_php(zval *str, zval *retval_ptr, char *context)
{
zval local_retval;
zend_op_array *new_op_array = NULL;
uint32_t original_compiler_options;
ZVAL_UNDEF(&local_retval);
original_compiler_options = CG(compiler_options);
CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
new_op_array = zend_compile_string(str, context);
CG(compiler_options) = original_compiler_options;
if (new_op_array)
{
EG(no_extensions) = 1;
zend_try {
zend_execute(new_op_array, &local_retval);
} zend_catch {
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
zend_bailout();
} zend_end_try();
EG(no_extensions) = 0;
if (Z_TYPE(local_retval) != IS_UNDEF) {
if (retval_ptr) {
ZVAL_COPY_VALUE(retval_ptr, &local_retval);
} else {
zval_ptr_dtor(&local_retval);
}
} else if (retval_ptr) {
ZVAL_NULL(retval_ptr);
}
destroy_op_array(new_op_array);
efree_size(new_op_array, sizeof(zend_op_array));
}
}