Skip to content

Commit 242ba55

Browse files
authored
You can now write serializers for TL in PHP (support in vkext) (#1416)
1 parent 1ff4cdb commit 242ba55

7 files changed

Lines changed: 208 additions & 24 deletions

File tree

vkext/vk_zend.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,25 @@ static zend_always_inline void vk_zend_update_public_property_long(zval *object,
226226
#endif
227227
}
228228

229+
static zend_always_inline void vk_zend_call_known_instance_method(zval *object,
230+
const char * name, size_t name_len, zval *retval_ptr,
231+
uint32_t param_count, zval *params) {
232+
zend_object* zobj = Z_OBJ_P(object);
233+
zend_string* method_name = zend_string_init(name, name_len, 0);
234+
zend_function* fun = Z_OBJ_HANDLER_P(object, get_method)(&zobj, method_name, 0);
235+
zend_string_release(method_name);
236+
if (!fun) {
237+
return; // retval stays UNDEF
238+
}
239+
#if PHP_MAJOR_VERSION >= 8
240+
zend_call_known_instance_method(fun, zobj, retval_ptr, param_count, params);
241+
#else
242+
zval method_name_zval;
243+
ZVAL_STR(&method_name_zval, method_name);
244+
call_user_function(NULL, object, &method_name_zval, retval_ptr, param_count, params);
245+
#endif
246+
}
247+
229248
#define ZAPI_TO_PP(az) (&(az))
230249
#define ZP_TO_API_P(az) (az)
231250
#define SMART_STRDATA(ss) ((ss).s)

vkext/vkext-rpc-req-error.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ void tl::RpcReqError::tl_fetch() {
6868
}
6969

7070
void RpcError::try_fetch() {
71-
int op = tl_parse_int();
71+
int op = tl_lookup_int();
7272
if (op == TL_REQ_RESULT_HEADER) {
73+
(void)tl_parse_int(); // skip op
7374
flags = tl_parse_int();
7475
header.emplace().tl_fetch(flags);
75-
op = tl_parse_int();
76+
op = tl_lookup_int();
7677
}
7778
if (op == TL_RPC_REQ_ERROR) {
79+
(void)tl_parse_int(); // skip op
7880
error.emplace().tl_fetch();
7981
}
8082
}

vkext/vkext-rpc-tl-serialization.cpp

Lines changed: 171 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
927960
struct 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

10911124
void _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

11001137
struct 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
}

vkext/vkext-rpc.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -854,9 +854,7 @@ static struct rpc_query *rpc_query_alloc(double timeout) {
854854
q->qid = qid;
855855
q->start_time = precise_now;
856856
q->timeout = timeout;
857-
if (tl_current_function_name) {
858-
q->fun_name = tl_current_function_name;
859-
}
857+
q->fun_name = tl_current_function_name;
860858
/* ADD_CNT(tree_insert);
861859
START_TICKS(tree_insert);
862860
query_tree = tree_insert_query (query_tree, q, lrand48 ());

vkext/vkext-rpc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#define RPC_BUF_SIZE (1 << 12)
1313
#define RPC_SERVER_MAGIC 0x8940303d
1414
#define RPC_BUFFER_MAGIC 0x8fa0da0c
15-
#define RPC_MAX_QUERY_LEN (1 << 24)
15+
#define RPC_MAX_QUERY_LEN ((1 << 24) - 1)
1616

1717
#define RPC_SKIP 0
1818

@@ -113,6 +113,7 @@ struct rpc_query {
113113
int answer_len;
114114
enum query_status status;
115115
void *extra;
116+
zval fetcher; // set if extra == NULL
116117
void (*extra_free)(struct rpc_query *);
117118
const char *fun_name;
118119
};
@@ -303,7 +304,9 @@ struct stats {
303304
DECLARE_STAT(store);
304305
DECLARE_STAT(fetch);
305306
DECLARE_STAT(store_function);
307+
DECLARE_STAT(store_function2);
306308
DECLARE_STAT(fetch_function);
309+
DECLARE_STAT(fetch_function2);
307310
DECLARE_STAT(crc32);
308311
DECLARE_STAT(tree_insert);
309312
DECLARE_STAT(total);

vkext/vkext-tl-parse.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ int tl_parse_int() {
2727
return do_rpc_fetch_int(&tl.error);
2828
}
2929

30+
int tl_lookup_int() {
31+
if (tl.error) {
32+
return -1;
33+
}
34+
return do_rpc_lookup_int(&tl.error);
35+
}
36+
3037
long long tl_parse_long() {
3138
if (tl.error) {
3239
return -1;
@@ -90,6 +97,7 @@ std::string tl_parse_string() {
9097
return res;
9198
}
9299
res.assign(s);
100+
free(s);
93101
return res;
94102
}
95103

0 commit comments

Comments
 (0)