@@ -531,7 +531,7 @@ static zval *create_php_instance(const char *class_name) {
531531 return ci;
532532}
533533
534- 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 );
534+ 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 );
535535
536536/* *
537537 * This function extracts ORIGINAL tl name from given php class name.
@@ -923,6 +923,36 @@ inline void tl_debug(const char *s __attribute__((unused)), int n __attribute__(
923923
924924/* {{{ Interface functions */
925925
926+ // returns 0 if no typedStore was found, otherwise returns allocated ZVAL with fetcher instance
927+ bool store_function2 (VK_ZVAL_API_P arr, zval *fetcher) {
928+ ADD_CNT (store_function2)
929+ START_TIMER (store_function2)
930+ assert (arr);
931+ if (Z_TYPE_P (arr) != IS_OBJECT) {
932+ END_TIMER (store_function2)
933+ return false ;
934+ }
935+ vk_zend_call_known_instance_method (arr, " typedStore" , strlen (" typedStore" ), fetcher, 0 , NULL );
936+ if (EG (exception)) {
937+ // This behavior is consistent with old code, in this case, query_one will return qid 0
938+ fprintf (stderr, " typedStore exception\n " );
939+ _zend_object* old_exception = EG (exception);
940+ EG (exception) = NULL ;
941+ OBJ_RELEASE (old_exception);
942+ END_TIMER (store_function2)
943+ return false ;
944+ }
945+ if (Z_TYPE_P (fetcher) != IS_OBJECT) { // returned null or function not found
946+ fprintf (stderr, " typedStore fetcher type is %d\n " , Z_TYPE_P (fetcher));
947+ END_TIMER (store_function2)
948+ return false ;
949+ }
950+ // when using fetcher, tl_current_function_name will not be accessed. But we set it anyway in case we forgot something.
951+ tl_current_function_name = " typedStore" ;
952+ END_TIMER (store_function2)
953+ return true ;
954+ }
955+
926956struct tl_tree *store_function (VK_ZVAL_API_P arr) {
927957 ADD_CNT (store_function)
928958 START_TIMER (store_function)
@@ -1033,7 +1063,7 @@ struct tl_tree *store_function(VK_ZVAL_API_P arr) {
10331063 return reinterpret_cast <tl_tree *>(res);
10341064}
10351065
1036- zval ** fetch_function (struct tl_tree *T) {
1066+ zval *fetch_function (struct tl_tree *T) {
10371067 ADD_CNT (fetch_function)
10381068 START_TIMER (fetch_function)
10391069#ifdef VLOG
@@ -1056,10 +1086,10 @@ zval **fetch_function(struct tl_tree *T) {
10561086 vkext_rpc::RpcError rpc_error;
10571087 rpc_error.try_fetch ();
10581088 if (rpc_error.error .has_value ()) {
1059- *_arr = make_query_result_or_error (NULL , rpc_error.error .value (), rpc_error.header .has_value () ? &rpc_error.header .value () : nullptr , rpc_error.flags );
1089+ 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 );
10601090 DEC_REF (T);
10611091 END_TIMER (fetch_function)
1062- return _arr ;
1092+ return ret ;
10631093 }
10641094 tl_parse_restore_pos (pos);
10651095
@@ -1077,28 +1107,55 @@ zval **fetch_function(struct tl_tree *T) {
10771107 VK_ALLOC_INIT_ZVAL (*_arr);
10781108 ZVAL_BOOL (*_arr, 1 );
10791109 }
1080- return _arr;
1110+ return * _arr;
10811111 } else {
10821112 if (*_arr) {
10831113 zval_dtor (*_arr);
10841114 }
10851115 *_arr = make_query_result_or_error (NULL , {TL_ERROR_RESPONSE_SYNTAX, " Can't parse response" });
1086- return _arr;
1116+ return * _arr;
10871117 }
10881118}
10891119
10901120void _extra_dec_ref (struct rpc_query *q) {
1091- if (q->extra ) {
1092- total_tl_working-- ;
1121+ if (! q->extra_free ) {
1122+ return ;
10931123 }
1094- DEC_REF (q->extra );
1095- q->extra = 0 ;
10961124 q->extra_free = 0 ;
1125+ total_tl_working--;
1126+ if (q->extra ) {
1127+ DEC_REF (q->extra );
1128+ q->extra = 0 ;
1129+ }
1130+ zval_ptr_dtor (&q->fetcher );
10971131}
10981132
10991133struct rpc_query *vk_rpc_tl_query_one_impl (struct rpc_connection *c, double timeout, VK_ZVAL_API_P arr, int ignore_answer) {
11001134 do_rpc_clean ();
11011135 START_TIMER (tmp);
1136+ zval fetcher;
1137+ ZVAL_NULL (&fetcher);
1138+ bool fetcher_found = store_function2 (arr, &fetcher);
1139+ END_TIMER (tmp);
1140+ if (fetcher_found) {
1141+ struct rpc_query *q;
1142+ if (!(q = do_rpc_send_noflush (c, timeout, ignore_answer))) {
1143+ zval_ptr_dtor (&fetcher);
1144+ vkext_error (VKEXT_ERROR_NETWORK, " Can't send packet" );
1145+ return 0 ;
1146+ }
1147+ if (q == (struct rpc_query *)1 ) { // answer is ignored
1148+ assert (ignore_answer);
1149+ zval_ptr_dtor (&fetcher);
1150+ return q;
1151+ }
1152+ assert (!ignore_answer);
1153+ ZVAL_COPY_VALUE (&q->fetcher , &fetcher);
1154+ q->extra_free = _extra_dec_ref;
1155+ total_tl_working++;
1156+ return q;
1157+ }
1158+ START_TIMER (tmp);
11021159 void *res = store_function (arr);
11031160 END_TIMER (tmp);
11041161 if (!res) {
@@ -1117,15 +1174,23 @@ struct rpc_query *vk_rpc_tl_query_one_impl(struct rpc_connection *c, double time
11171174 }
11181175 assert (!ignore_answer);
11191176 q->extra = res;
1177+ ZVAL_NULL (&q->fetcher );
11201178 q->extra_free = _extra_dec_ref;
11211179 total_tl_working++;
11221180 return q;
11231181}
11241182
1125- zval **vk_rpc_tl_query_result_one_impl (struct tl_tree *T) {
1183+ zval *fetch_function2 (zval *fetcher);
1184+
1185+ zval *vk_rpc_tl_query_result_one_impl (struct tl_tree *T, zval *fetcher) {
11261186 tl_parse_init ();
11271187 START_TIMER (tmp);
1128- zval **r = fetch_function (T);
1188+ zval *r = NULL ;
1189+ if (T) {
1190+ r = fetch_function (T);
1191+ }else {
1192+ r = fetch_function2 (fetcher);
1193+ }
11291194 // fprintf(stderr, "~~~~ after fetch:\n");
11301195 // php_debug_zval_dump(*r, 1);
11311196 END_TIMER (tmp);
@@ -1341,9 +1406,79 @@ static zval *convert_rpc_extra_header_to_php_repr(const vkext_rpc::tl::RpcReqRes
13411406 return res;
13421407}
13431408
1344- static zval *make_query_result_or_error (zval **r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header, int extra_flags) {
1409+ zval *fetch_function2 (zval *fetcher) {
1410+ ADD_CNT (fetch_function2)
1411+ START_TIMER (fetch_function2)
1412+
1413+ assert (fetcher);
1414+ assert (Z_TYPE_P (fetcher) == IS_OBJECT);
1415+
1416+ vkext_rpc::RpcError rpc_error;
1417+ rpc_error.try_fetch ();
1418+ if (rpc_error.error .has_value ()) {
1419+ 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 );
1420+ END_TIMER (fetch_function2)
1421+ return ret;
1422+ }
1423+
1424+ zval* return_value;
1425+ VK_ALLOC_INIT_ZVAL (return_value);
1426+ ZVAL_UNDEF (return_value);
1427+ vk_zend_call_known_instance_method (fetcher, " typedFetch" , strlen (" typedFetch" ), return_value, 0 , NULL );
1428+ if (EG (exception)) {
1429+ efree (return_value); // it is UNDEF
1430+
1431+ _zend_object * old_exception = EG (exception);
1432+ EG (exception) = NULL ;
1433+
1434+ zval exception_zval;
1435+ ZVAL_OBJ (&exception_zval, old_exception);
1436+
1437+ zval *message;
1438+ VK_ALLOC_INIT_ZVAL (message);
1439+ ZVAL_UNDEF (message);
1440+ vk_zend_call_known_instance_method (&exception_zval, " getMessage" , strlen (" getMessage" ), message, 0 , NULL );
1441+ // fprintf(stderr, "getMessage after call %d\n", Z_TYPE(message));
1442+ assert (Z_TYPE_P (message) == IS_STRING);
1443+
1444+ OBJ_RELEASE (old_exception);
1445+
1446+ zval *_err = create_php_instance (reqResult_error_class_name);
1447+
1448+ vk_zend_update_public_property_nod (_err, " error" , message);
1449+
1450+ // vk_zend_update_public_property_string(_err, "error", "hren");
1451+ vk_zend_update_public_property_long (_err, " error_code" , -1000 );
1452+ END_TIMER (fetch_function2)
1453+ return _err;
1454+ }
1455+ fprintf (stderr, " typedFetch after call %d\n " , Z_TYPE_P (return_value));
1456+ if (Z_TYPE_P (return_value) != IS_OBJECT) { // should be never, but that is user code
1457+ zval *_err = create_php_instance (reqResult_error_class_name);
1458+ vk_zend_update_public_property_string (_err, " error" , " fetcher->typedFetch() did not return object, as expected" );
1459+ vk_zend_update_public_property_long (_err, " error_code" , -1000 );
1460+ END_TIMER (fetch_function2)
1461+ return _err;
1462+ }
1463+ if (rpc_error.header .has_value ()) {
1464+ zval *wrapped_err = create_php_instance (reqResult_header_class_name);
1465+ zval *header_php_repr = convert_rpc_extra_header_to_php_repr (rpc_error.header .value ());
1466+
1467+ set_field_int (&wrapped_err, rpc_error.flags , " flags" , -1 );
1468+ set_field (&wrapped_err, header_php_repr, " extra" , -1 );
1469+ set_field (&wrapped_err, return_value, " result" , -1 );
1470+ END_TIMER (fetch_function2)
1471+ return wrapped_err;
1472+ }
1473+ zval *wrapped_err = create_php_instance (reqResult_underscore_class_name);
1474+ set_field (&wrapped_err, return_value, " result" , -1 );
1475+ END_TIMER (fetch_function2)
1476+ return wrapped_err;
1477+ }
1478+
1479+ static zval *make_query_result_or_error (zval *r, const vkext_rpc::tl::RpcReqError &error, const vkext_rpc::tl::RpcReqResultExtra *header, int extra_flags) {
13451480 if (r) {
1346- return * r;
1481+ return r;
13471482 }
13481483 zval *_err;
13491484 switch (typed_mode) {
@@ -1393,13 +1528,22 @@ void vk_rpc_tl_query_result_impl(struct rpc_queue *Q, double timeout, zval **r)
13931528 }
13941529 struct rpc_query *q = rpc_query_get (qid);
13951530 tl_tree *T = reinterpret_cast <tl_tree *>(q->extra );
1531+ zval fetcher;
1532+ ZVAL_COPY (&fetcher, &q->fetcher );
13961533 tl_current_function_name = q->fun_name ;
1397- INC_REF (T);
1534+ if (T) {
1535+ INC_REF (T);
1536+ }
13981537
13991538 if (do_rpc_get_and_parse (qid, timeout - precise_now) < 0 ) {
1539+ // TODO - most likely. leak here (of both T and fetcher).
1540+ // But it is difficult to simulate this situation, so
1541+ // we decided to keep leak to avoid double delete in case we
1542+ // failed to completely understand this code.
14001543 continue ;
14011544 }
1402- 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" });
1545+ 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" });
1546+ zval_ptr_dtor (&fetcher);
14031547 vk_add_index_zval_nod (*r, qid, res);
14041548 }
14051549}
@@ -1430,14 +1574,19 @@ void vk_rpc_tl_query_result_one(INTERNAL_FUNCTION_PARAMETERS) {
14301574 double timeout = (argc < 2 ) ? q->timeout : precise_now + parse_zend_double (VK_ZVAL_ARRAY_TO_API_P (z[1 ]));
14311575 END_TIMER (parse);
14321576 auto *T = reinterpret_cast <tl_tree *>(q->extra );
1433- INC_REF (T);
1577+ zval fetcher;
1578+ ZVAL_COPY (&fetcher, &q->fetcher );
1579+ if (T) {
1580+ INC_REF (T);
1581+ }
14341582 if (do_rpc_get_and_parse (qid, timeout - precise_now) < 0 ) {
14351583 zval *r = make_query_result_or_error (NULL , {TL_ERROR_RESPONSE_NOT_FOUND, " Response not found, probably timed out" });
14361584 RETVAL_ZVAL (r, false , true );
14371585 efree (r);
14381586 return ;
14391587 }
1440- 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" });
1588+ 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" });
1589+ zval_ptr_dtor (&fetcher);
14411590 RETVAL_ZVAL (r, false , true );
14421591 efree (r);
14431592}
0 commit comments