@@ -532,7 +532,7 @@ static zval *create_php_instance(const char *class_name) {
532532 return ci;
533533}
534534
535- static zval *make_query_result_or_error (zval ** r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header = nullptr , int extra_flags = 0 );
535+ static zval *make_query_result_or_error (zval *r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header = nullptr , int extra_flags = 0 );
536536
537537/* *
538538 * This function extracts ORIGINAL tl name from given php class name.
@@ -924,6 +924,39 @@ inline void tl_debug(const char *s __attribute__((unused)), int n __attribute__(
924924
925925/* {{{ Interface functions */
926926
927+ // returns 0 if no typedStore was found, otherwise returns allocated ZVAL with fetcher instance
928+ bool store_function2 (VK_ZVAL_API_P arr, zval *fetcher) {
929+ ADD_CNT (store_function2)
930+ START_TIMER (store_function2)
931+ assert (arr);
932+ if (Z_TYPE_P (arr) != IS_OBJECT ) {
933+ END_TIMER (store_function2)
934+ return false ;
935+ }
936+ vk_zend_call_known_instance_method (arr, " typedStore" , strlen (" typedStore" ), fetcher, 0 , NULL );
937+ if (EG (exception)) {
938+ // This behavior is consistent with old code, in this case, query_one will return qid 0
939+ fprintf (stderr, " typedStore exception\n " );
940+ _zend_object* old_exception = EG (exception);
941+ EG (exception) = NULL ;
942+ OBJ_RELEASE (old_exception);
943+ END_TIMER (store_function2)
944+ return false ;
945+ }
946+ if (Z_TYPE_P (fetcher) != IS_OBJECT ) {
947+ // returned null or function not found (undef) or function returned something unexpected
948+ if (Z_TYPE_P (fetcher) != IS_NULL ) {
949+ fprintf (stderr, " typedStore fetcher unexpected type is %d\n " , Z_TYPE_P (fetcher));
950+ }
951+ END_TIMER (store_function2)
952+ return false ;
953+ }
954+ // when using fetcher, tl_current_function_name will not be accessed. But we set it anyway in case we forgot something.
955+ tl_current_function_name = " typedStore" ;
956+ END_TIMER (store_function2)
957+ return true ;
958+ }
959+
927960struct tl_tree *store_function (VK_ZVAL_API_P arr) {
928961 ADD_CNT (store_function)
929962 START_TIMER (store_function)
@@ -1034,7 +1067,7 @@ struct tl_tree *store_function(VK_ZVAL_API_P arr) {
10341067 return reinterpret_cast <tl_tree *>(res);
10351068}
10361069
1037- zval ** fetch_function (struct tl_tree *T) {
1070+ zval *fetch_function (struct tl_tree *T) {
10381071 ADD_CNT (fetch_function)
10391072 START_TIMER (fetch_function)
10401073#ifdef VLOG
@@ -1057,10 +1090,10 @@ zval **fetch_function(struct tl_tree *T) {
10571090 vkext_rpc::RpcError rpc_error;
10581091 rpc_error.try_fetch ();
10591092 if (rpc_error.error .has_value ()) {
1060- *_arr = make_query_result_or_error (NULL , rpc_error.error .value (), rpc_error.header .has_value () ? &rpc_error.header .value () : nullptr , rpc_error.flags );
1093+ zval *ret = make_query_result_or_error (NULL , rpc_error.error .value (), rpc_error.header .has_value () ? &rpc_error.header .value () : nullptr , rpc_error.flags );
10611094 DEC_REF (T);
10621095 END_TIMER (fetch_function)
1063- return _arr ;
1096+ return ret ;
10641097 }
10651098 tl_parse_restore_pos (pos);
10661099
@@ -1078,28 +1111,55 @@ zval **fetch_function(struct tl_tree *T) {
10781111 VK_ALLOC_INIT_ZVAL (*_arr);
10791112 ZVAL_BOOL (*_arr, 1 );
10801113 }
1081- return _arr;
1114+ return * _arr;
10821115 } else {
10831116 if (*_arr) {
10841117 zval_dtor (*_arr);
10851118 }
10861119 *_arr = make_query_result_or_error (NULL , {TL_ERROR_RESPONSE_SYNTAX , " Can't parse response" });
1087- return _arr;
1120+ return * _arr;
10881121 }
10891122}
10901123
10911124void _extra_dec_ref (struct rpc_query *q) {
1092- if (q->extra ) {
1093- total_tl_working-- ;
1125+ if (! q->extra_free ) {
1126+ return ;
10941127 }
1095- DEC_REF (q->extra );
1096- q->extra = 0 ;
10971128 q->extra_free = 0 ;
1129+ total_tl_working--;
1130+ if (q->extra ) {
1131+ DEC_REF (q->extra );
1132+ q->extra = 0 ;
1133+ }
1134+ zval_ptr_dtor (&q->fetcher );
10981135}
10991136
11001137struct rpc_query *vk_rpc_tl_query_one_impl (struct rpc_connection *c, double timeout, VK_ZVAL_API_P arr, int ignore_answer) {
11011138 do_rpc_clean ();
11021139 START_TIMER (tmp);
1140+ zval fetcher;
1141+ ZVAL_NULL (&fetcher);
1142+ bool fetcher_found = store_function2 (arr, &fetcher);
1143+ END_TIMER (tmp);
1144+ if (fetcher_found) {
1145+ struct rpc_query *q;
1146+ if (!(q = do_rpc_send_noflush (c, timeout, ignore_answer))) {
1147+ zval_ptr_dtor (&fetcher);
1148+ vkext_error (VKEXT_ERROR_NETWORK , " Can't send packet" );
1149+ return 0 ;
1150+ }
1151+ if (q == (struct rpc_query *)1 ) { // answer is ignored
1152+ assert (ignore_answer);
1153+ zval_ptr_dtor (&fetcher);
1154+ return q;
1155+ }
1156+ assert (!ignore_answer);
1157+ ZVAL_COPY_VALUE (&q->fetcher , &fetcher);
1158+ q->extra_free = _extra_dec_ref;
1159+ total_tl_working++;
1160+ return q;
1161+ }
1162+ START_TIMER (tmp);
11031163 void *res = store_function (arr);
11041164 END_TIMER (tmp);
11051165 if (!res) {
@@ -1118,15 +1178,23 @@ struct rpc_query *vk_rpc_tl_query_one_impl(struct rpc_connection *c, double time
11181178 }
11191179 assert (!ignore_answer);
11201180 q->extra = res;
1181+ ZVAL_NULL (&q->fetcher );
11211182 q->extra_free = _extra_dec_ref;
11221183 total_tl_working++;
11231184 return q;
11241185}
11251186
1126- zval **vk_rpc_tl_query_result_one_impl (struct tl_tree *T) {
1187+ zval *fetch_function2 (zval *fetcher);
1188+
1189+ zval *vk_rpc_tl_query_result_one_impl (struct tl_tree *T, zval *fetcher) {
11271190 tl_parse_init ();
11281191 START_TIMER (tmp);
1129- zval **r = fetch_function (T);
1192+ zval *r = NULL ;
1193+ if (T) {
1194+ r = fetch_function (T);
1195+ }else {
1196+ r = fetch_function2 (fetcher);
1197+ }
11301198 // fprintf(stderr, "~~~~ after fetch:\n");
11311199 // php_debug_zval_dump(*r, 1);
11321200 END_TIMER (tmp);
@@ -1352,9 +1420,80 @@ static zval *convert_rpc_extra_header_to_php_repr(const vkext_rpc::tl::RpcReqRes
13521420 return res;
13531421}
13541422
1355- static zval *make_query_result_or_error (zval **r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header, int extra_flags) {
1423+ zval *fetch_function2 (zval *fetcher) {
1424+ ADD_CNT (fetch_function2)
1425+ START_TIMER (fetch_function2)
1426+
1427+ assert (fetcher);
1428+ assert (Z_TYPE_P (fetcher) == IS_OBJECT );
1429+
1430+ vkext_rpc::RpcError rpc_error;
1431+ rpc_error.try_fetch ();
1432+ if (rpc_error.error .has_value ()) {
1433+ zval *ret = make_query_result_or_error (NULL , rpc_error.error .value (), rpc_error.header .has_value () ? &rpc_error.header .value () : nullptr , rpc_error.flags );
1434+ END_TIMER (fetch_function2)
1435+ return ret;
1436+ }
1437+
1438+ zval* return_value;
1439+ VK_ALLOC_INIT_ZVAL (return_value);
1440+ ZVAL_UNDEF (return_value);
1441+ vk_zend_call_known_instance_method (fetcher, " typedFetch" , strlen (" typedFetch" ), return_value, 0 , NULL );
1442+ if (EG (exception)) {
1443+ efree (return_value); // it is UNDEF
1444+
1445+ _zend_object * old_exception = EG (exception);
1446+ EG (exception) = NULL ;
1447+
1448+ zval exception_zval;
1449+ ZVAL_OBJ (&exception_zval, old_exception);
1450+
1451+ zval *message;
1452+ VK_ALLOC_INIT_ZVAL (message);
1453+ ZVAL_UNDEF (message);
1454+ vk_zend_call_known_instance_method (&exception_zval, " getMessage" , strlen (" getMessage" ), message, 0 , NULL );
1455+ // fprintf(stderr, "getMessage after call %d\n", Z_TYPE(message));
1456+ assert (Z_TYPE_P (message) == IS_STRING );
1457+
1458+ OBJ_RELEASE (old_exception);
1459+
1460+ zval *_err = create_php_instance (reqResult_error_class_name);
1461+
1462+ vk_zend_update_public_property_nod (_err, " error" , message);
1463+
1464+ // vk_zend_update_public_property_string(_err, "error", "hren");
1465+ vk_zend_update_public_property_long (_err, " error_code" , -1000 );
1466+ END_TIMER (fetch_function2)
1467+ return _err;
1468+ }
1469+ // TODO - will remove later when everything works
1470+ // fprintf(stderr, "typedFetch after call %d\n", Z_TYPE_P(return_value));
1471+ if (Z_TYPE_P (return_value) != IS_OBJECT ) { // should be never, but that is user code
1472+ zval *_err = create_php_instance (reqResult_error_class_name);
1473+ vk_zend_update_public_property_string (_err, " error" , " fetcher->typedFetch() did not return object, as expected" );
1474+ vk_zend_update_public_property_long (_err, " error_code" , -1000 );
1475+ END_TIMER (fetch_function2)
1476+ return _err;
1477+ }
1478+ if (rpc_error.header .has_value ()) {
1479+ zval *wrapped_err = create_php_instance (reqResult_header_class_name);
1480+ zval *header_php_repr = convert_rpc_extra_header_to_php_repr (rpc_error.header .value ());
1481+
1482+ set_field_int (&wrapped_err, rpc_error.flags , " flags" , -1 );
1483+ set_field (&wrapped_err, header_php_repr, " extra" , -1 );
1484+ set_field (&wrapped_err, return_value, " result" , -1 );
1485+ END_TIMER (fetch_function2)
1486+ return wrapped_err;
1487+ }
1488+ zval *wrapped_err = create_php_instance (reqResult_underscore_class_name);
1489+ set_field (&wrapped_err, return_value, " result" , -1 );
1490+ END_TIMER (fetch_function2)
1491+ return wrapped_err;
1492+ }
1493+
1494+ static zval *make_query_result_or_error (zval *r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header, int extra_flags) {
13561495 if (r) {
1357- return * r;
1496+ return r;
13581497 }
13591498 zval *_err;
13601499 switch (typed_mode) {
@@ -1404,13 +1543,22 @@ void vk_rpc_tl_query_result_impl(struct rpc_queue *Q, double timeout, zval **r)
14041543 }
14051544 struct rpc_query *q = rpc_query_get (qid);
14061545 tl_tree *T = reinterpret_cast <tl_tree *>(q->extra );
1546+ zval fetcher;
1547+ ZVAL_COPY (&fetcher, &q->fetcher );
14071548 tl_current_function_name = q->fun_name ;
1408- INC_REF (T);
1549+ if (T) {
1550+ INC_REF (T);
1551+ }
14091552
14101553 if (do_rpc_get_and_parse (qid, timeout - precise_now) < 0 ) {
1554+ // TODO - most likely. leak here (of both T and fetcher).
1555+ // But it is difficult to simulate this situation, so
1556+ // we decided to keep leak to avoid double delete in case we
1557+ // failed to completely understand this code.
14111558 continue ;
14121559 }
1413- zval *res = make_query_result_or_error (vk_rpc_tl_query_result_one_impl (T), {TL_ERROR_RESPONSE_NOT_FOUND , " Response not found, probably timed out" });
1560+ zval *res = make_query_result_or_error (vk_rpc_tl_query_result_one_impl (T, &fetcher), {TL_ERROR_RESPONSE_NOT_FOUND , " Response not found, probably timed out" });
1561+ zval_ptr_dtor (&fetcher);
14141562 vk_add_index_zval_nod (*r, qid, res);
14151563 }
14161564}
@@ -1441,14 +1589,19 @@ void vk_rpc_tl_query_result_one(INTERNAL_FUNCTION_PARAMETERS) {
14411589 double timeout = (argc < 2 ) ? q->timeout : precise_now + parse_zend_double (VK_ZVAL_ARRAY_TO_API_P (z[1 ]));
14421590 END_TIMER (parse);
14431591 auto *T = reinterpret_cast <tl_tree *>(q->extra );
1444- INC_REF (T);
1592+ zval fetcher;
1593+ ZVAL_COPY (&fetcher, &q->fetcher );
1594+ if (T) {
1595+ INC_REF (T);
1596+ }
14451597 if (do_rpc_get_and_parse (qid, timeout - precise_now) < 0 ) {
14461598 zval *r = make_query_result_or_error (NULL , {TL_ERROR_RESPONSE_NOT_FOUND , " Response not found, probably timed out" });
14471599 RETVAL_ZVAL (r, false , true );
14481600 efree (r);
14491601 return ;
14501602 }
1451- zval *r = make_query_result_or_error (vk_rpc_tl_query_result_one_impl (T), {TL_ERROR_RESPONSE_NOT_FOUND , " Response not found, probably timed out" });
1603+ zval *r = make_query_result_or_error (vk_rpc_tl_query_result_one_impl (T, &fetcher), {TL_ERROR_RESPONSE_NOT_FOUND , " Response not found, probably timed out" });
1604+ zval_ptr_dtor (&fetcher);
14521605 RETVAL_ZVAL (r, false , true );
14531606 efree (r);
14541607}
0 commit comments