Skip to content

Commit fe74b99

Browse files
committed
Reference dynamic functions through dynamic_defs
1 parent b3189e7 commit fe74b99

14 files changed

+185
-130
lines changed

Zend/zend_compile.c

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,28 +1072,22 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen
10721072
}
10731073
}
10741074

1075-
ZEND_API zend_result do_bind_function(zval *lcname) /* {{{ */
1075+
ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{ */
10761076
{
1077-
zend_function *function;
1078-
zval *rtd_key, *zv;
1079-
1080-
rtd_key = lcname + 1;
1081-
zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1);
1082-
if (UNEXPECTED(!zv)) {
1083-
do_bind_function_error(Z_STR_P(lcname), NULL, 0);
1077+
zend_function *added_func = zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), func);
1078+
if (UNEXPECTED(!added_func)) {
1079+
do_bind_function_error(Z_STR_P(lcname), &func->op_array, 0);
10841080
return FAILURE;
10851081
}
1086-
function = (zend_function*)Z_PTR_P(zv);
1087-
if (UNEXPECTED(function->common.fn_flags & ZEND_ACC_PRELOADED)
1088-
&& !(CG(compiler_options) & ZEND_COMPILE_PRELOAD)) {
1089-
zv = zend_hash_add(EG(function_table), Z_STR_P(lcname), zv);
1090-
} else {
1091-
zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname));
1082+
1083+
if (func->op_array.refcount) {
1084+
++*func->op_array.refcount;
10921085
}
1093-
if (UNEXPECTED(!zv)) {
1094-
do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0);
1095-
return FAILURE;
1086+
if (func->common.function_name) {
1087+
zend_string_addref(func->common.function_name);
10961088
}
1089+
/* We can't addref static_variables here :(
1090+
* We make sure it's NULLed when destroyed though. */
10971091
return SUCCESS;
10981092
}
10991093
/* }}} */
@@ -6901,9 +6895,18 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name,
69016895
}
69026896
/* }}} */
69036897

6898+
static uint32_t zend_add_dynamic_func_def(zend_op_array *def) {
6899+
zend_op_array *op_array = CG(active_op_array);
6900+
uint32_t def_offset = op_array->num_dynamic_func_defs++;
6901+
op_array->dynamic_func_defs = erealloc(
6902+
op_array->dynamic_func_defs, op_array->num_dynamic_func_defs * sizeof(zend_op_array *));
6903+
op_array->dynamic_func_defs[def_offset] = def;
6904+
return def_offset;
6905+
}
6906+
69046907
static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */
69056908
{
6906-
zend_string *unqualified_name, *name, *lcname, *key;
6909+
zend_string *unqualified_name, *name, *lcname;
69076910
zend_op *opline;
69086911

69096912
unqualified_name = decl->name;
@@ -6939,25 +6942,16 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
69396942
return;
69406943
}
69416944

6942-
/* Generate RTD keys until we find one that isn't in use yet. */
6943-
key = NULL;
6944-
do {
6945-
zend_tmp_string_release(key);
6946-
key = zend_build_runtime_definition_key(lcname, decl->start_lineno);
6947-
} while (!zend_hash_add_ptr(CG(function_table), key, op_array));
6948-
6945+
uint32_t func_ref = zend_add_dynamic_func_def(op_array);
69496946
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
69506947
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
6951-
opline->extended_value = zend_alloc_cache_slot();
6952-
opline->op1_type = IS_CONST;
6953-
LITERAL_STR(opline->op1, key);
6948+
opline->op2.num = func_ref;
69546949
} else {
69556950
opline = get_next_op();
69566951
opline->opcode = ZEND_DECLARE_FUNCTION;
69576952
opline->op1_type = IS_CONST;
69586953
LITERAL_STR(opline->op1, zend_string_copy(lcname));
6959-
/* RTD key is placed after lcname literal in op1 */
6960-
zend_add_literal_string(&key);
6954+
opline->op2.num = func_ref;
69616955
}
69626956
zend_string_release_ex(lcname, 0);
69636957
}

Zend/zend_compile.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,13 @@ struct _zend_op_array {
446446
zend_string *doc_comment;
447447

448448
int last_literal;
449+
uint32_t num_dynamic_func_defs;
449450
zval *literals;
450451

452+
/* Functions and classes that are declared dynamically are stored here and
453+
* referenced by index from opcodes. */
454+
zend_op_array **dynamic_func_defs;
455+
451456
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
452457
};
453458

@@ -768,7 +773,7 @@ bool zend_handle_encoding_declaration(zend_ast *ast);
768773
/* parser-driven code generators */
769774
void zend_do_free(znode *op1);
770775

771-
ZEND_API zend_result do_bind_function(zval *lcname);
776+
ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname);
772777
ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name);
773778
ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_array);
774779
ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline);

Zend/zend_opcode.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
8585
op_array->last_literal = 0;
8686
op_array->literals = NULL;
8787

88+
op_array->num_dynamic_func_defs = 0;
89+
op_array->dynamic_func_defs = NULL;
90+
8891
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
8992
op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
9093

@@ -462,6 +465,7 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
462465
if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
463466
if (GC_DELREF(ht) == 0) {
464467
zend_array_destroy(ht);
468+
ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
465469
}
466470
}
467471
}
@@ -541,6 +545,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
541545
}
542546
efree(arg_info);
543547
}
548+
if (op_array->num_dynamic_func_defs) {
549+
for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
550+
destroy_op_array(op_array->dynamic_func_defs[i]);
551+
}
552+
efree(op_array->dynamic_func_defs);
553+
}
544554
}
545555

546556
static void zend_update_extended_stmts(zend_op_array *op_array)

Zend/zend_vm_def.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7567,12 +7567,14 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
75677567
ZEND_VM_NEXT_OPCODE();
75687568
}
75697569

7570-
ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY)
7570+
ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, NUM)
75717571
{
7572+
zend_function *func;
75727573
USE_OPLINE
75737574

75747575
SAVE_OPLINE();
7575-
do_bind_function(RT_CONSTANT(opline, opline->op1));
7576+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
7577+
do_bind_function(func, RT_CONSTANT(opline, opline->op1));
75767578
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
75777579
}
75787580

@@ -7837,23 +7839,14 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
78377839
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
78387840
}
78397841

7840-
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED, CACHE_SLOT)
7842+
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
78417843
{
78427844
USE_OPLINE
78437845
zend_function *func;
7844-
zval *zfunc;
78457846
zval *object;
78467847
zend_class_entry *called_scope;
78477848

7848-
func = CACHED_PTR(opline->extended_value);
7849-
if (UNEXPECTED(func == NULL)) {
7850-
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
7851-
ZEND_ASSERT(zfunc != NULL);
7852-
func = Z_FUNC_P(zfunc);
7853-
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
7854-
CACHE_PTR(opline->extended_value, func);
7855-
}
7856-
7849+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
78577850
if (Z_TYPE(EX(This)) == IS_OBJECT) {
78587851
called_scope = Z_OBJCE(EX(This));
78597852
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||

Zend/zend_vm_execute.h

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2815,10 +2815,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
28152815

28162816
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
28172817
{
2818+
zend_function *func;
28182819
USE_OPLINE
28192820

28202821
SAVE_OPLINE();
2821-
do_bind_function(RT_CONSTANT(opline, opline->op1));
2822+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
2823+
do_bind_function(func, RT_CONSTANT(opline, opline->op1));
28222824
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
28232825
}
28242826

@@ -5139,6 +5141,32 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_CONST_HANDL
51395141
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
51405142
}
51415143

5144+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
5145+
{
5146+
USE_OPLINE
5147+
zend_function *func;
5148+
zval *object;
5149+
zend_class_entry *called_scope;
5150+
5151+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
5152+
if (Z_TYPE(EX(This)) == IS_OBJECT) {
5153+
called_scope = Z_OBJCE(EX(This));
5154+
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
5155+
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
5156+
object = NULL;
5157+
} else {
5158+
object = &EX(This);
5159+
}
5160+
} else {
5161+
called_scope = Z_CE(EX(This));
5162+
object = NULL;
5163+
}
5164+
zend_create_closure(EX_VAR(opline->result.var), func,
5165+
EX(func)->op_array.scope, called_scope, object);
5166+
5167+
ZEND_VM_NEXT_OPCODE();
5168+
}
5169+
51425170
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
51435171
{
51445172
USE_OPLINE
@@ -10145,41 +10173,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U
1014510173
}
1014610174

1014710175
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
10148-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
10149-
{
10150-
USE_OPLINE
10151-
zend_function *func;
10152-
zval *zfunc;
10153-
zval *object;
10154-
zend_class_entry *called_scope;
10155-
10156-
func = CACHED_PTR(opline->extended_value);
10157-
if (UNEXPECTED(func == NULL)) {
10158-
zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1);
10159-
ZEND_ASSERT(zfunc != NULL);
10160-
func = Z_FUNC_P(zfunc);
10161-
ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
10162-
CACHE_PTR(opline->extended_value, func);
10163-
}
10164-
10165-
if (Z_TYPE(EX(This)) == IS_OBJECT) {
10166-
called_scope = Z_OBJCE(EX(This));
10167-
if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
10168-
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
10169-
object = NULL;
10170-
} else {
10171-
object = &EX(This);
10172-
}
10173-
} else {
10174-
called_scope = Z_CE(EX(This));
10175-
object = NULL;
10176-
}
10177-
zend_create_closure(EX_VAR(opline->result.var), func,
10178-
EX(func)->op_array.scope, called_scope, object);
10179-
10180-
ZEND_VM_NEXT_OPCODE();
10181-
}
10182-
1018310176
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
1018410177
{
1018510178
USE_OPLINE
@@ -53124,7 +53117,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5312453117
(void*)&&ZEND_NULL_LABEL,
5312553118
(void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL,
5312653119
(void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL,
53127-
(void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_LABEL,
53120+
(void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_LABEL,
5312853121
(void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL,
5312953122
(void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL,
5313053123
(void*)&&ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_LABEL,
@@ -54943,6 +54936,10 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5494354936
VM_TRACE(ZEND_DECLARE_CLASS_SPEC_CONST)
5494454937
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5494554938
HYBRID_BREAK();
54939+
HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST):
54940+
VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST)
54941+
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
54942+
HYBRID_BREAK();
5494654943
HYBRID_CASE(ZEND_YIELD_FROM_SPEC_CONST):
5494754944
VM_TRACE(ZEND_YIELD_FROM_SPEC_CONST)
5494854945
ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -55419,10 +55416,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5541955416
VM_TRACE(ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED)
5542055417
ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5542155418
HYBRID_BREAK();
55422-
HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED):
55423-
VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED)
55424-
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
55425-
HYBRID_BREAK();
5542655419
HYBRID_CASE(ZEND_YIELD_SPEC_CONST_UNUSED):
5542755420
VM_TRACE(ZEND_YIELD_SPEC_CONST_UNUSED)
5542855421
ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -61152,7 +61145,7 @@ void zend_vm_init(void)
6115261145
ZEND_NULL_HANDLER,
6115361146
ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER,
6115461147
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
61155-
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER,
61148+
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER,
6115661149
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER,
6115761150
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER,
6115861151
ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_HANDLER,

Zend/zend_vm_handlers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1158,7 +1158,7 @@
11581158
_(2286, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \
11591159
_(2288, ZEND_MAKE_REF_SPEC_CV_UNUSED) \
11601160
_(2289, ZEND_DECLARE_FUNCTION_SPEC) \
1161-
_(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED) \
1161+
_(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) \
11621162
_(2291, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \
11631163
_(2292, ZEND_DECLARE_CLASS_SPEC_CONST) \
11641164
_(2293, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST) \

Zend/zend_vm_opcodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@ static uint32_t zend_vm_opcodes_flags[201] = {
368368
0x00047305,
369369
0x00000000,
370370
0x00000101,
371-
0x00000000,
372-
0x00040103,
371+
0x00001000,
372+
0x00001003,
373373
0x00000303,
374374
0x00000003,
375375
0x00000303,

ext/opcache/Optimizer/compact_literals.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
241241
case ZEND_RECV_INIT:
242242
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
243243
break;
244-
case ZEND_DECLARE_FUNCTION:
245-
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
246-
break;
247244
case ZEND_DECLARE_CLASS:
248245
case ZEND_DECLARE_CLASS_DELAYED:
249246
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
@@ -776,7 +773,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
776773
bind_var_slot[opline->op2.constant] = opline->extended_value;
777774
}
778775
break;
779-
case ZEND_DECLARE_LAMBDA_FUNCTION:
780776
case ZEND_DECLARE_ANON_CLASS:
781777
case ZEND_DECLARE_CLASS_DELAYED:
782778
opline->extended_value = cache_size;

ext/opcache/Optimizer/zend_optimizer.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,16 +1367,24 @@ static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) {
13671367
return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
13681368
}
13691369

1370+
static void zend_foreach_op_array_helper(
1371+
zend_op_array *op_array, zend_op_array_func_t func, void *context) {
1372+
func(op_array, context);
1373+
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
1374+
func(op_array->dynamic_func_defs[i], context);
1375+
}
1376+
}
1377+
13701378
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
13711379
{
13721380
zend_class_entry *ce;
13731381
zend_string *key;
13741382
zend_op_array *op_array;
13751383

1376-
func(&script->main_op_array, context);
1384+
zend_foreach_op_array_helper(&script->main_op_array, func, context);
13771385

13781386
ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1379-
func(op_array, context);
1387+
zend_foreach_op_array_helper(op_array, func, context);
13801388
} ZEND_HASH_FOREACH_END();
13811389

13821390
ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
@@ -1387,7 +1395,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
13871395
if (op_array->scope == ce
13881396
&& op_array->type == ZEND_USER_FUNCTION
13891397
&& !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1390-
func(op_array, context);
1398+
zend_foreach_op_array_helper(op_array, func, context);
13911399
}
13921400
} ZEND_HASH_FOREACH_END();
13931401
} ZEND_HASH_FOREACH_END();

0 commit comments

Comments
 (0)