Skip to content

Commit 0ee49c6

Browse files
authored
checker: cache node.args[0] on fixed_array_builtin_method_call and array_builtin_method_call (#23411)
1 parent b0b08bc commit 0ee49c6

1 file changed

Lines changed: 80 additions & 83 deletions

File tree

‎vlib/v/checker/fn.v‎

Lines changed: 80 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3142,21 +3142,22 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
31423142
c.table.sym(unaliased_left_type).info as ast.Array
31433143
}
31443144
elem_typ = array_info.elem_type
3145+
mut arg0 := if node.args.len > 0 { node.args[0] } else { ast.CallArg{} }
31453146
if method_name in ['filter', 'map', 'any', 'all', 'count'] {
3146-
if node.args.len > 0 && mut node.args[0].expr is ast.LambdaExpr {
3147-
if node.args[0].expr.params.len != 1 {
3147+
if node.args.len > 0 && mut arg0.expr is ast.LambdaExpr {
3148+
if arg0.expr.params.len != 1 {
31483149
c.error('lambda expressions used in the builtin array methods require exactly 1 parameter',
3149-
node.args[0].expr.pos)
3150+
arg0.expr.pos)
31503151
return ast.void_type
31513152
}
31523153
if method_name == 'map' {
3153-
c.lambda_expr_fix_type_of_param(mut node.args[0].expr, mut node.args[0].expr.params[0],
3154+
c.lambda_expr_fix_type_of_param(mut arg0.expr, mut arg0.expr.params[0],
31543155
elem_typ)
3155-
le_type := c.expr(mut node.args[0].expr.expr)
3156+
le_type := c.expr(mut arg0.expr.expr)
31563157
// eprintln('>>>>> node.args[0].expr: ${ast.Expr(node.args[0].expr)} | elem_typ: ${elem_typ} | etype: ${le_type}')
3157-
c.support_lambda_expr_one_param(elem_typ, le_type, mut node.args[0].expr)
3158+
c.support_lambda_expr_one_param(elem_typ, le_type, mut arg0.expr)
31583159
} else {
3159-
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut node.args[0].expr)
3160+
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut arg0.expr)
31603161
}
31613162
} else {
31623163
// position of `it` doesn't matter
@@ -3169,10 +3170,10 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
31693170
node.pos)
31703171
return ast.void_type
31713172
} else {
3172-
arg_type := c.expr(mut node.args[0].expr)
3173+
arg_type := c.expr(mut arg0.expr)
31733174
if arg_type !in [ast.int_type, ast.int_literal_type] {
31743175
c.error('the first argument of `array.insert()` should be integer',
3175-
node.args[0].expr.pos())
3176+
arg0.expr.pos())
31763177
return ast.void_type
31773178
}
31783179
}
@@ -3215,10 +3216,10 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
32153216
c.error('`.${method_name}()` expected 1 argument, but got ${node.args.len}',
32163217
node.pos)
32173218
} else {
3218-
if mut node.args[0].expr is ast.LambdaExpr {
3219-
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr)
3219+
if mut arg0.expr is ast.LambdaExpr {
3220+
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut arg0.expr)
32203221
}
3221-
arg_type := c.expr(mut node.args[0].expr)
3222+
arg_type := c.expr(mut arg0.expr)
32223223
arg_sym := c.table.sym(arg_type)
32233224
if arg_sym.kind == .function {
32243225
func_info := arg_sym.info as ast.FnType
@@ -3237,12 +3238,12 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
32373238
}
32383239
}
32393240
}
3240-
node.args[0].typ = arg_type
3241+
arg0.typ = arg_type
32413242
if method := c.table.find_method(left_sym, method_name) {
32423243
c.check_expected_call_arg(arg_type, method.params[1].typ, node.language,
3243-
node.args[0]) or {
3244+
arg0) or {
32443245
c.error('${err.msg()} in argument 1 to `${left_sym.name}.${method_name}`',
3245-
node.args[0].pos)
3246+
arg0.pos)
32463247
}
32473248
}
32483249
if method_name == 'sort_with_compare' {
@@ -3268,24 +3269,24 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
32683269
if node.args.len > 1 {
32693270
c.error('expected 0 or 1 argument, but got ${node.args.len}', node.pos)
32703271
} else if node.args.len == 1 {
3271-
if mut node.args[0].expr is ast.LambdaExpr {
3272-
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.bool_type, mut node.args[0].expr)
3273-
} else if node.args[0].expr is ast.InfixExpr {
3274-
c.check_sort_external_variable_access(node.args[0].expr)
3275-
if node.args[0].expr.op !in [.gt, .lt] {
3272+
if mut arg0.expr is ast.LambdaExpr {
3273+
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.bool_type, mut arg0.expr)
3274+
} else if mut arg0.expr is ast.InfixExpr {
3275+
c.check_sort_external_variable_access(arg0.expr)
3276+
if arg0.expr.op !in [.gt, .lt] {
32763277
c.error('`.${method_name}()` can only use `<` or `>` comparison',
32773278
node.pos)
32783279
}
3279-
left_name := '${node.args[0].expr.left}'[0]
3280-
right_name := '${node.args[0].expr.right}'[0]
3280+
left_name := '${arg0.expr.left}'[0]
3281+
right_name := '${arg0.expr.right}'[0]
32813282
if left_name !in [`a`, `b`] || right_name !in [`a`, `b`] {
32823283
c.error('`.${method_name}()` can only use `a` or `b` as argument, e.g. `arr.${method_name}(a < b)`',
32833284
node.pos)
32843285
} else if left_name == right_name {
32853286
c.error('`.${method_name}()` cannot use same argument', node.pos)
32863287
}
3287-
if node.args[0].expr.left !in [ast.CallExpr, ast.Ident, ast.SelectorExpr, ast.IndexExpr]
3288-
|| node.args[0].expr.right !in [ast.CallExpr, ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
3288+
if arg0.expr.left !in [ast.CallExpr, ast.Ident, ast.SelectorExpr, ast.IndexExpr]
3289+
|| arg0.expr.right !in [ast.CallExpr, ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
32893290
c.error('`.${method_name}()` can only use ident, index, selector or call as argument, \ne.g. `arr.${method_name}(a < b)`, `arr.${method_name}(a.id < b.id)`, `arr.${method_name}(a[0] < b[0])`',
32903291
node.pos)
32913292
}
@@ -3303,7 +3304,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
33033304
elem_sym := c.table.sym(elem_typ)
33043305
if elem_sym.kind == .thread {
33053306
if node.args.len != 0 {
3306-
c.error('`.wait()` does not have any arguments', node.args[0].pos)
3307+
c.error('`.wait()` does not have any arguments', arg0.pos)
33073308
}
33083309
thread_ret_type := c.unwrap_generic(elem_sym.thread_info().return_type)
33093310
if thread_ret_type.has_flag(.option) {
@@ -3331,7 +3332,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
33313332
arg_sym := c.table.sym(arg_type)
33323333
ret_type := match arg_sym.info {
33333334
ast.FnType {
3334-
if node.args[0].expr is ast.SelectorExpr {
3335+
if arg0.expr is ast.SelectorExpr {
33353336
arg_type
33363337
} else {
33373338
arg_sym.info.func.return_type
@@ -3365,7 +3366,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
33653366
node.return_type = ast.int_type
33663367
} else if method_name == 'clone' {
33673368
if node.args.len != 0 {
3368-
c.error('`.clone()` does not have any arguments', node.args[0].pos)
3369+
c.error('`.clone()` does not have any arguments', arg0.pos)
33693370
}
33703371
c.ensure_same_array_return_type(mut node, left_type)
33713372
} else if method_name == 'sorted' {
@@ -3379,10 +3380,10 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
33793380
// 'int (string *, string *)' (aka 'int (struct string *, struct string *)')
33803381
// to parameter of type 'int (*)(voidptr, voidptr)' (aka 'int (*)(void *, void *)')
33813382
node.args[0].expr = ast.CastExpr{
3382-
expr: node.args[0].expr
3383+
expr: arg0.expr
33833384
typ: ast.voidptr_type
33843385
typname: 'voidptr'
3385-
expr_type: c.expr(mut node.args[0].expr)
3386+
expr_type: c.expr(mut arg0.expr)
33863387
pos: node.pos
33873388
}
33883389
} else if method_name == 'sort' {
@@ -3392,11 +3393,9 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
33923393
if node.args.len != 1 {
33933394
c.error('`.contains()` expected 1 argument, but got ${node.args.len}', node.pos)
33943395
} else if !left_sym.has_method('contains') {
3395-
arg_typ := c.unwrap_generic(c.expr(mut node.args[0].expr))
3396+
arg_typ := c.unwrap_generic(c.expr(mut arg0.expr))
33963397
c.check_expected_call_arg(arg_typ, c.unwrap_generic(elem_typ), node.language,
3397-
node.args[0]) or {
3398-
c.error('${err.msg()} in argument 1 to `.contains()`', node.args[0].pos)
3399-
}
3398+
arg0) or { c.error('${err.msg()} in argument 1 to `.contains()`', arg0.pos) }
34003399
}
34013400
for i, mut arg in node.args {
34023401
node.args[i].typ = c.expr(mut arg.expr)
@@ -3406,11 +3405,9 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
34063405
if node.args.len != 1 {
34073406
c.error('`.index()` expected 1 argument, but got ${node.args.len}', node.pos)
34083407
} else if !left_sym.has_method('index') {
3409-
arg_typ := c.unwrap_generic(c.expr(mut node.args[0].expr))
3408+
arg_typ := c.unwrap_generic(c.expr(mut arg0.expr))
34103409
c.check_expected_call_arg(arg_typ, c.unwrap_generic(elem_typ), node.language,
3411-
node.args[0]) or {
3412-
c.error('${err.msg()} in argument 1 to `.index()`', node.args[0].pos)
3413-
}
3410+
arg0) or { c.error('${err.msg()} in argument 1 to `.index()`', arg0.pos) }
34143411
}
34153412
for i, mut arg in node.args {
34163413
node.args[i].typ = c.expr(mut arg.expr)
@@ -3429,7 +3426,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
34293426
}
34303427
}
34313428
if node.args.len != 0 {
3432-
c.error('`.${method_name}()` does not have any arguments', node.args[0].pos)
3429+
c.error('`.${method_name}()` does not have any arguments', arg0.pos)
34333430
}
34343431
node.return_type = array_info.elem_type
34353432
if method_name == 'pop' {
@@ -3450,9 +3447,9 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
34503447
if node.args.len != 1 {
34513448
c.error('`.delete()` expected 1 argument, but got ${node.args.len}', node.pos)
34523449
} else {
3453-
arg_typ := c.unwrap_generic(c.expr(mut node.args[0].expr))
3454-
c.check_expected_call_arg(arg_typ, ast.int_type, node.language, node.args[0]) or {
3455-
c.error('${err.msg()} in argument 1 to `.delete()`', node.args[0].pos)
3450+
arg_typ := c.unwrap_generic(c.expr(mut arg0.expr))
3451+
c.check_expected_call_arg(arg_typ, ast.int_type, node.language, arg0) or {
3452+
c.error('${err.msg()} in argument 1 to `.delete()`', arg0.pos)
34563453
}
34573454
}
34583455
node.return_type = ast.void_type
@@ -3485,15 +3482,16 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
34853482
} else {
34863483
c.table.sym(unaliased_left_type).info as ast.ArrayFixed
34873484
}
3485+
mut arg0 := if node.args.len > 0 { node.args[0] } else { ast.CallArg{} }
34883486
elem_typ := array_info.elem_type
34893487
if method_name == 'index' {
34903488
if node.args.len != 1 {
34913489
c.error('`.index()` expected 1 argument, but got ${node.args.len}', node.pos)
34923490
return ast.int_type
34933491
} else if !left_sym.has_method('index') {
3494-
arg_typ := c.expr(mut node.args[0].expr)
3495-
c.check_expected_call_arg(arg_typ, elem_typ, node.language, node.args[0]) or {
3496-
c.error('${err.msg()} in argument 1 to `.index()`', node.args[0].pos)
3492+
arg_typ := c.expr(mut arg0.expr)
3493+
c.check_expected_call_arg(arg_typ, elem_typ, node.language, arg0) or {
3494+
c.error('${err.msg()} in argument 1 to `.index()`', arg0.pos)
34973495
return ast.int_type
34983496
}
34993497
}
@@ -3506,9 +3504,9 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
35063504
c.error('`.contains()` expected 1 argument, but got ${node.args.len}', node.pos)
35073505
return ast.bool_type
35083506
} else if !left_sym.has_method('contains') {
3509-
arg_typ := c.expr(mut node.args[0].expr)
3510-
c.check_expected_call_arg(arg_typ, elem_typ, node.language, node.args[0]) or {
3511-
c.error('${err.msg()} in argument 1 to `.contains()`', node.args[0].pos)
3507+
arg_typ := c.expr(mut arg0.expr)
3508+
c.check_expected_call_arg(arg_typ, elem_typ, node.language, arg0) or {
3509+
c.error('${err.msg()} in argument 1 to `.contains()`', arg0.pos)
35123510
return ast.bool_type
35133511
}
35143512
}
@@ -3522,18 +3520,18 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
35223520
node.pos)
35233521
return ast.bool_type
35243522
}
3525-
if node.args.len > 0 && mut node.args[0].expr is ast.LambdaExpr {
3526-
if node.args[0].expr.params.len != 1 {
3523+
if node.args.len > 0 && mut arg0.expr is ast.LambdaExpr {
3524+
if arg0.expr.params.len != 1 {
35273525
c.error('lambda expressions used in the builtin array methods require exactly 1 parameter',
3528-
node.args[0].expr.pos)
3526+
arg0.expr.pos)
35293527
return ast.bool_type
35303528
}
3531-
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut node.args[0].expr)
3529+
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut arg0.expr)
35323530
} else {
35333531
// position of `it` doesn't matter
35343532
scope_register_it(mut node.scope, node.pos, elem_typ)
35353533
}
3536-
c.expr(mut node.args[0].expr)
3534+
c.expr(mut arg0.expr)
35373535
c.check_predicate_param(false, elem_typ, node)
35383536
node.return_type = ast.bool_type
35393537
} else if method_name == 'count' {
@@ -3542,25 +3540,25 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
35423540
node.pos)
35433541
return ast.bool_type
35443542
}
3545-
if node.args.len > 0 && mut node.args[0].expr is ast.LambdaExpr {
3546-
if node.args[0].expr.params.len != 1 {
3543+
if node.args.len > 0 && mut arg0.expr is ast.LambdaExpr {
3544+
if arg0.expr.params.len != 1 {
35473545
c.error('lambda expressions used in the builtin array methods require exactly 1 parameter',
3548-
node.args[0].expr.pos)
3546+
arg0.expr.pos)
35493547
return ast.bool_type
35503548
}
3551-
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut node.args[0].expr)
3549+
c.support_lambda_expr_one_param(elem_typ, ast.bool_type, mut arg0.expr)
35523550
} else {
35533551
// position of `it` doesn't matter
35543552
scope_register_it(mut node.scope, node.pos, elem_typ)
35553553
}
3556-
c.expr(mut node.args[0].expr)
3554+
c.expr(mut arg0.expr)
35573555
c.check_predicate_param(false, elem_typ, node)
35583556
node.return_type = ast.int_type
35593557
} else if method_name == 'wait' {
35603558
elem_sym := c.table.sym(elem_typ)
35613559
if elem_sym.kind == .thread {
35623560
if node.args.len != 0 {
3563-
c.error('`.wait()` does not have any arguments', node.args[0].pos)
3561+
c.error('`.wait()` does not have any arguments', arg0.pos)
35643562
}
35653563
thread_ret_type := c.unwrap_generic(elem_sym.thread_info().return_type)
35663564
if thread_ret_type.has_flag(.option) {
@@ -3581,27 +3579,26 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
35813579
node.pos)
35823580
return ast.void_type
35833581
}
3584-
if mut node.args[0].expr is ast.LambdaExpr {
3585-
if node.args[0].expr.params.len != 1 {
3582+
if mut arg0.expr is ast.LambdaExpr {
3583+
if arg0.expr.params.len != 1 {
35863584
c.error('lambda expressions used in the builtin array methods require exactly 1 parameter',
3587-
node.args[0].expr.pos)
3585+
arg0.expr.pos)
35883586
return ast.void_type
35893587
}
3590-
c.lambda_expr_fix_type_of_param(mut node.args[0].expr, mut node.args[0].expr.params[0],
3591-
elem_typ)
3592-
le_type := c.expr(mut node.args[0].expr.expr)
3593-
c.support_lambda_expr_one_param(elem_typ, le_type, mut node.args[0].expr)
3588+
c.lambda_expr_fix_type_of_param(mut arg0.expr, mut arg0.expr.params[0], elem_typ)
3589+
le_type := c.expr(mut arg0.expr.expr)
3590+
c.support_lambda_expr_one_param(elem_typ, le_type, mut arg0.expr)
35943591
} else {
35953592
// position of `it` doesn't matter
35963593
scope_register_it(mut node.scope, node.pos, elem_typ)
35973594
}
35983595

35993596
c.check_predicate_param(true, elem_typ, node)
3600-
arg_type := c.check_expr_option_or_result_call(node.args[0].expr, c.expr(mut node.args[0].expr))
3597+
arg_type := c.check_expr_option_or_result_call(arg0.expr, c.expr(mut arg0.expr))
36013598
arg_sym := c.table.sym(arg_type)
36023599
ret_type := match arg_sym.info {
36033600
ast.FnType {
3604-
if node.args[0].expr is ast.SelectorExpr {
3601+
if arg0.expr is ast.SelectorExpr {
36053602
arg_type
36063603
} else {
36073604
arg_sym.info.func.return_type
@@ -3634,24 +3631,24 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
36343631
if node.args.len > 1 {
36353632
c.error('expected 0 or 1 argument, but got ${node.args.len}', node.pos)
36363633
} else if node.args.len == 1 {
3637-
if mut node.args[0].expr is ast.LambdaExpr {
3638-
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.bool_type, mut node.args[0].expr)
3639-
} else if node.args[0].expr is ast.InfixExpr {
3640-
c.check_sort_external_variable_access(node.args[0].expr)
3641-
if node.args[0].expr.op !in [.gt, .lt] {
3634+
if mut arg0.expr is ast.LambdaExpr {
3635+
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.bool_type, mut arg0.expr)
3636+
} else if mut arg0.expr is ast.InfixExpr {
3637+
c.check_sort_external_variable_access(arg0.expr)
3638+
if arg0.expr.op !in [.gt, .lt] {
36423639
c.error('`.${method_name}()` can only use `<` or `>` comparison',
36433640
node.pos)
36443641
}
3645-
left_name := '${node.args[0].expr.left}'[0]
3646-
right_name := '${node.args[0].expr.right}'[0]
3642+
left_name := '${arg0.expr.left}'[0]
3643+
right_name := '${arg0.expr.right}'[0]
36473644
if left_name !in [`a`, `b`] || right_name !in [`a`, `b`] {
36483645
c.error('`.${method_name}()` can only use `a` or `b` as argument, e.g. `arr.${method_name}(a < b)`',
36493646
node.pos)
36503647
} else if left_name == right_name {
36513648
c.error('`.${method_name}()` cannot use same argument', node.pos)
36523649
}
3653-
if node.args[0].expr.left !in [ast.Ident, ast.SelectorExpr, ast.IndexExpr]
3654-
|| node.args[0].expr.right !in [ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
3650+
if arg0.expr.left !in [ast.Ident, ast.SelectorExpr, ast.IndexExpr]
3651+
|| arg0.expr.right !in [ast.Ident, ast.SelectorExpr, ast.IndexExpr] {
36553652
c.error('`.${method_name}()` can only use ident, index or selector as argument, \ne.g. `arr.${method_name}(a < b)`, `arr.${method_name}(a.id < b.id)`, `arr.${method_name}(a[0] < b[0])`',
36563653
node.pos)
36573654
}
@@ -3678,10 +3675,10 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
36783675
c.error('`.${method_name}()` expected 1 argument, but got ${node.args.len}',
36793676
node.pos)
36803677
} else {
3681-
if mut node.args[0].expr is ast.LambdaExpr {
3682-
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr)
3678+
if mut arg0.expr is ast.LambdaExpr {
3679+
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut arg0.expr)
36833680
}
3684-
arg_type := c.expr(mut node.args[0].expr)
3681+
arg_type := c.expr(mut arg0.expr)
36853682
arg_sym := c.table.sym(arg_type)
36863683
if arg_sym.kind == .function {
36873684
func_info := arg_sym.info as ast.FnType
@@ -3700,12 +3697,12 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
37003697
}
37013698
}
37023699
}
3703-
node.args[0].typ = arg_type
3700+
arg0.typ = arg_type
37043701
if method := c.table.find_method(left_sym, method_name) {
37053702
c.check_expected_call_arg(arg_type, method.params[1].typ, node.language,
3706-
node.args[0]) or {
3703+
arg0) or {
37073704
c.error('${err.msg()} in argument 1 to `${left_sym.name}.${method_name}`',
3708-
node.args[0].pos)
3705+
arg0.pos)
37093706
}
37103707
}
37113708
for mut arg in node.args {
@@ -3722,7 +3719,7 @@ fn (mut c Checker) fixed_array_builtin_method_call(mut node ast.CallExpr, left_t
37223719
}
37233720
} else if method_name in ['reverse', 'reverse_in_place'] {
37243721
if node.args.len != 0 {
3725-
c.error('`.${method_name}` does not have any arguments', node.args[0].pos)
3722+
c.error('`.${method_name}` does not have any arguments', arg0.pos)
37263723
} else {
37273724
if method_name == 'reverse' {
37283725
node.return_type = node.left_type

0 commit comments

Comments
 (0)