Skip to content

Commit 257dbb0

Browse files
committed
Add zend_call_known_function() API family
This adds the following APIs: void zend_call_known_function( zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, int param_count, zval *params); void zend_call_known_instance_method( zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params); void zend_call_known_instance_method_with_0_params( zend_function *fn, zend_object *object, zval *retval_ptr); void zend_call_known_instance_method_with_1_params( zend_function *fn, zend_object *object, zval *retval_ptr, zval *param); void zend_call_known_instance_method_with_2_params( zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2); These are used to perform a call if you already have the zend_function you want to call. zend_call_known_function() is the base API, the rest are just really thin wrappers around it for the common case of instance method calls. Closes GH-5692.
1 parent bcada03 commit 257dbb0

13 files changed

Lines changed: 133 additions & 275 deletions

Zend/zend_API.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,37 @@ ZEND_API int zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *f
563563

564564
ZEND_API int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache);
565565

566+
/* Call the provided zend_function with the given params.
567+
* If retval_ptr is NULL, the return value is discarded.
568+
* If object is NULL, this must be a free function or static call.
569+
* called_scope must be provided for instance and static method calls. */
570+
ZEND_API void zend_call_known_function(
571+
zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr,
572+
uint32_t param_count, zval *params);
573+
574+
/* Call the provided zend_function instance method on an object. */
575+
static zend_always_inline void zend_call_known_instance_method(
576+
zend_function *fn, zend_object *object, zval *retval_ptr,
577+
uint32_t param_count, zval *params)
578+
{
579+
zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params);
580+
}
581+
582+
static zend_always_inline void zend_call_known_instance_method_with_0_params(
583+
zend_function *fn, zend_object *object, zval *retval_ptr)
584+
{
585+
zend_call_known_instance_method(fn, object, retval_ptr, 0, NULL);
586+
}
587+
588+
static zend_always_inline void zend_call_known_instance_method_with_1_params(
589+
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param)
590+
{
591+
zend_call_known_instance_method(fn, object, retval_ptr, 1, param);
592+
}
593+
594+
ZEND_API void zend_call_known_instance_method_with_2_params(
595+
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
596+
566597
ZEND_API int zend_set_hash_symbol(zval *symbol, const char *name, int name_length, zend_bool is_ref, int num_symbol_tables, ...);
567598

568599
ZEND_API int zend_delete_global_variable(zend_string *name);

Zend/zend_exceptions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
922922
zend_string *str, *file = NULL;
923923
zend_long line = 0;
924924

925-
zend_call_method_with_0_params(Z_OBJ(exception), ce_exception, &ex->ce->__tostring, "__tostring", &tmp);
925+
zend_call_known_instance_method_with_0_params(ex->ce->__tostring, Z_OBJ(exception), &tmp);
926926
if (!EG(exception)) {
927927
if (Z_TYPE(tmp) != IS_STRING) {
928928
zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name));

Zend/zend_execute_API.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,51 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
861861
}
862862
/* }}} */
863863

864+
ZEND_API void zend_call_known_function(
865+
zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr,
866+
uint32_t param_count, zval *params)
867+
{
868+
zval retval;
869+
zend_fcall_info fci;
870+
zend_fcall_info_cache fcic;
871+
872+
ZEND_ASSERT(fn && "zend_function must be passed!");
873+
874+
fci.size = sizeof(fci);
875+
fci.object = object;
876+
fci.retval = retval_ptr ? retval_ptr : &retval;
877+
fci.param_count = param_count;
878+
fci.params = params;
879+
fci.no_separation = 1;
880+
ZVAL_UNDEF(&fci.function_name); /* Unused */
881+
882+
fcic.function_handler = fn;
883+
fcic.object = object;
884+
fcic.called_scope = called_scope;
885+
886+
int result = zend_call_function(&fci, &fcic);
887+
if (UNEXPECTED(result == FAILURE)) {
888+
if (!EG(exception)) {
889+
zend_error_noreturn(E_CORE_ERROR, "Couldn't execute method %s%s%s",
890+
fn->common.scope ? ZSTR_VAL(fn->common.scope->name) : "",
891+
fn->common.scope ? "::" : "", ZSTR_VAL(fn->common.function_name));
892+
}
893+
}
894+
895+
if (!retval_ptr) {
896+
zval_ptr_dtor(&retval);
897+
}
898+
}
899+
900+
ZEND_API void zend_call_known_instance_method_with_2_params(
901+
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2)
902+
{
903+
zval params[2];
904+
ZVAL_COPY_VALUE(&params[0], param1);
905+
ZVAL_COPY_VALUE(&params[1], param2);
906+
zend_call_known_instance_method(fn, object, retval_ptr, 2, params);
907+
}
908+
864909
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */
865910
{
866911
zend_class_entry *ce = NULL;

Zend/zend_interfaces.c

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ ZEND_API zend_class_entry *zend_ce_stringable;
3232

3333
/* {{{ zend_call_method
3434
Only returns the returned zval if retval_ptr != NULL */
35-
ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval_ptr, int param_count, zval* arg1, zval* arg2)
35+
ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval_ptr, uint32_t param_count, zval* arg1, zval* arg2)
3636
{
37-
int result;
38-
zend_fcall_info fci;
39-
zend_fcall_info_cache fcic;
40-
zval retval;
37+
zend_function *fn;
38+
zend_class_entry *called_scope;
4139
zval params[2];
4240

4341
if (param_count > 0) {
@@ -47,68 +45,43 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z
4745
ZVAL_COPY_VALUE(&params[1], arg2);
4846
}
4947

50-
fci.size = sizeof(fci);
51-
fci.object = object;
52-
fci.retval = retval_ptr ? retval_ptr : &retval;
53-
fci.param_count = param_count;
54-
fci.params = params;
55-
fci.no_separation = 1;
56-
ZVAL_UNDEF(&fci.function_name); /* Unused */
57-
5848
if (!obj_ce) {
5949
obj_ce = object ? object->ce : NULL;
6050
}
6151
if (!fn_proxy || !*fn_proxy) {
6252
if (EXPECTED(obj_ce)) {
63-
fcic.function_handler = zend_hash_str_find_ptr(
53+
fn = zend_hash_str_find_ptr(
6454
&obj_ce->function_table, function_name, function_name_len);
65-
if (UNEXPECTED(fcic.function_handler == NULL)) {
55+
if (UNEXPECTED(fn == NULL)) {
6656
/* error at c-level */
6757
zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s::%s", ZSTR_VAL(obj_ce->name), function_name);
6858
}
6959
} else {
70-
fcic.function_handler = zend_fetch_function_str(function_name, function_name_len);
71-
if (UNEXPECTED(fcic.function_handler == NULL)) {
60+
fn = zend_fetch_function_str(function_name, function_name_len);
61+
if (UNEXPECTED(fn == NULL)) {
7262
/* error at c-level */
7363
zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for function %s", function_name);
7464
}
7565
}
7666
if (fn_proxy) {
77-
*fn_proxy = fcic.function_handler;
67+
*fn_proxy = fn;
7868
}
7969
} else {
80-
fcic.function_handler = *fn_proxy;
70+
fn = *fn_proxy;
8171
}
8272

8373
if (object) {
84-
fcic.called_scope = object->ce;
74+
called_scope = object->ce;
8575
} else {
86-
zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
87-
76+
called_scope = zend_get_called_scope(EG(current_execute_data));
8877
if (obj_ce &&
8978
(!called_scope ||
9079
!instanceof_function(called_scope, obj_ce))) {
91-
fcic.called_scope = obj_ce;
92-
} else {
93-
fcic.called_scope = called_scope;
80+
called_scope = obj_ce;
9481
}
9582
}
96-
fcic.object = object;
97-
result = zend_call_function(&fci, &fcic);
9883

99-
if (result == FAILURE) {
100-
/* error at c-level */
101-
if (!obj_ce) {
102-
obj_ce = object ? object->ce : NULL;
103-
}
104-
if (!EG(exception)) {
105-
zend_error_noreturn(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
106-
}
107-
}
108-
if (!retval_ptr) {
109-
zval_ptr_dtor(&retval);
110-
return NULL;
111-
}
84+
zend_call_known_function(fn, object, called_scope, retval_ptr, param_count, params);
11285
return retval_ptr;
11386
}
11487
/* }}} */
@@ -390,8 +363,7 @@ ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *b
390363
zval retval;
391364
int result;
392365

393-
zend_call_method_with_0_params(Z_OBJ_P(object), ce, &ce->serialize_func, "serialize", &retval);
394-
366+
zend_call_known_instance_method_with_0_params(ce->serialize_func, Z_OBJ_P(object), &retval);
395367

396368
if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
397369
result = FAILURE;
@@ -430,9 +402,8 @@ ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const uns
430402
}
431403

432404
ZVAL_STRINGL(&zdata, (char*)buf, buf_len);
433-
434-
zend_call_method_with_1_params(Z_OBJ_P(object), ce, &ce->unserialize_func, "unserialize", NULL, &zdata);
435-
405+
zend_call_known_instance_method_with_1_params(
406+
ce->unserialize_func, Z_OBJ_P(object), NULL, &zdata);
436407
zval_ptr_dtor(&zdata);
437408

438409
if (EG(exception)) {

Zend/zend_interfaces.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ typedef struct _zend_user_iterator {
3838
zval value;
3939
} zend_user_iterator;
4040

41-
ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval, int param_count, zval* arg1, zval* arg2);
41+
ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval, uint32_t param_count, zval* arg1, zval* arg2);
4242

4343
#define zend_call_method_with_0_params(obj, obj_ce, fn_proxy, function_name, retval) \
4444
zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 0, NULL, NULL)

Zend/zend_object_handlers.c

Lines changed: 8 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /
149149
return object->handlers->get_properties(object);
150150
}
151151

152-
zend_call_method_with_0_params(object, ce, &ce->__debugInfo, ZEND_DEBUGINFO_FUNC_NAME, &retval);
152+
zend_call_known_instance_method_with_0_params(ce->__debugInfo, object, &retval);
153153
if (Z_TYPE(retval) == IS_ARRAY) {
154154
if (!Z_REFCOUNTED(retval)) {
155155
*is_temp = 1;
@@ -177,145 +177,56 @@ ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /
177177

178178
static void zend_std_call_getter(zend_object *zobj, zend_string *prop_name, zval *retval) /* {{{ */
179179
{
180-
zend_class_entry *ce = zobj->ce;
181180
zend_class_entry *orig_fake_scope = EG(fake_scope);
182-
zend_fcall_info fci;
183-
zend_fcall_info_cache fcic;
184181
zval member;
185182

186183
EG(fake_scope) = NULL;
187184

188-
/* __get handler is called with one argument:
189-
property name
190-
191-
it should return whether the call was successful or not
192-
*/
193-
194185
ZVAL_STR(&member, prop_name);
195-
196-
fci.size = sizeof(fci);
197-
fci.object = zobj;
198-
fci.retval = retval;
199-
fci.param_count = 1;
200-
fci.params = &member;
201-
fci.no_separation = 1;
202-
ZVAL_UNDEF(&fci.function_name); /* Unused */
203-
204-
fcic.function_handler = ce->__get;
205-
fcic.called_scope = ce;
206-
fcic.object = zobj;
207-
208-
zend_call_function(&fci, &fcic);
186+
zend_call_known_instance_method_with_1_params(zobj->ce->__get, zobj, retval, &member);
209187

210188
EG(fake_scope) = orig_fake_scope;
211189
}
212190
/* }}} */
213191

214192
static void zend_std_call_setter(zend_object *zobj, zend_string *prop_name, zval *value) /* {{{ */
215193
{
216-
zend_class_entry *ce = zobj->ce;
217194
zend_class_entry *orig_fake_scope = EG(fake_scope);
218-
zend_fcall_info fci;
219-
zend_fcall_info_cache fcic;
220-
zval args[2], ret;
195+
zval args[2];
221196

222197
EG(fake_scope) = NULL;
223198

224-
/* __set handler is called with two arguments:
225-
property name
226-
value to be set
227-
*/
228-
229199
ZVAL_STR(&args[0], prop_name);
230200
ZVAL_COPY_VALUE(&args[1], value);
231-
ZVAL_UNDEF(&ret);
232-
233-
fci.size = sizeof(fci);
234-
fci.object = zobj;
235-
fci.retval = &ret;
236-
fci.param_count = 2;
237-
fci.params = args;
238-
fci.no_separation = 1;
239-
ZVAL_UNDEF(&fci.function_name); /* Unused */
240-
241-
fcic.function_handler = ce->__set;
242-
fcic.called_scope = ce;
243-
fcic.object = zobj;
244-
245-
zend_call_function(&fci, &fcic);
246-
zval_ptr_dtor(&ret);
201+
zend_call_known_instance_method(zobj->ce->__set, zobj, NULL, 2, args);
247202

248203
EG(fake_scope) = orig_fake_scope;
249204
}
250205
/* }}} */
251206

252207
static void zend_std_call_unsetter(zend_object *zobj, zend_string *prop_name) /* {{{ */
253208
{
254-
zend_class_entry *ce = zobj->ce;
255209
zend_class_entry *orig_fake_scope = EG(fake_scope);
256-
zend_fcall_info fci;
257-
zend_fcall_info_cache fcic;
258-
zval ret, member;
210+
zval member;
259211

260212
EG(fake_scope) = NULL;
261213

262-
/* __unset handler is called with one argument:
263-
property name
264-
*/
265-
266214
ZVAL_STR(&member, prop_name);
267-
ZVAL_UNDEF(&ret);
268-
269-
fci.size = sizeof(fci);
270-
fci.object = zobj;
271-
fci.retval = &ret;
272-
fci.param_count = 1;
273-
fci.params = &member;
274-
fci.no_separation = 1;
275-
ZVAL_UNDEF(&fci.function_name); /* Unused */
276-
277-
fcic.function_handler = ce->__unset;
278-
fcic.called_scope = ce;
279-
fcic.object = zobj;
280-
281-
zend_call_function(&fci, &fcic);
282-
zval_ptr_dtor(&ret);
215+
zend_call_known_instance_method_with_1_params(zobj->ce->__unset, zobj, NULL, &member);
283216

284217
EG(fake_scope) = orig_fake_scope;
285218
}
286219
/* }}} */
287220

288221
static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zval *retval) /* {{{ */
289222
{
290-
zend_class_entry *ce = zobj->ce;
291223
zend_class_entry *orig_fake_scope = EG(fake_scope);
292-
zend_fcall_info fci;
293-
zend_fcall_info_cache fcic;
294224
zval member;
295225

296226
EG(fake_scope) = NULL;
297227

298-
/* __isset handler is called with one argument:
299-
property name
300-
301-
it should return whether the property is set or not
302-
*/
303-
304228
ZVAL_STR(&member, prop_name);
305-
306-
fci.size = sizeof(fci);
307-
fci.object = zobj;
308-
fci.retval = retval;
309-
fci.param_count = 1;
310-
fci.params = &member;
311-
fci.no_separation = 1;
312-
ZVAL_UNDEF(&fci.function_name); /* Unused */
313-
314-
fcic.function_handler = ce->__isset;
315-
fcic.called_scope = ce;
316-
fcic.object = zobj;
317-
318-
zend_call_function(&fci, &fcic);
229+
zend_call_known_instance_method_with_1_params(zobj->ce->__isset, zobj, retval, &member);
319230

320231
EG(fake_scope) = orig_fake_scope;
321232
}
@@ -1803,7 +1714,7 @@ ZEND_API int zend_std_cast_object_tostring(zend_object *readobj, zval *writeobj,
18031714
zend_class_entry *fake_scope = EG(fake_scope);
18041715
EG(fake_scope) = NULL;
18051716
GC_ADDREF(readobj);
1806-
zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval);
1717+
zend_call_known_instance_method_with_0_params(ce->__tostring, readobj, &retval);
18071718
zend_object_release(readobj);
18081719
EG(fake_scope) = fake_scope;
18091720
if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {

0 commit comments

Comments
 (0)