Coverage Report

Created: 2026-07-25 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/zend_partial.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend Engine                                                          |
4
   +----------------------------------------------------------------------+
5
   | Copyright © Zend Technologies Ltd., a subsidiary company of          |
6
   |     Perforce Software, Inc., and Contributors.                       |
7
   +----------------------------------------------------------------------+
8
   | This source file is subject to the Modified BSD License that is      |
9
   | bundled with this package in the file LICENSE, and is available      |
10
   | through the World Wide Web at <https://www.php.net/license/>.        |
11
   |                                                                      |
12
   | SPDX-License-Identifier: BSD-3-Clause                                |
13
   +----------------------------------------------------------------------+
14
   | Authors: Arnaud Le Blanc <arnaud.lb@gmail.com>                       |
15
   +----------------------------------------------------------------------+
16
*/
17
18
/**
19
 * Partial Function Application:
20
 *
21
 * A partial application is compiled to the usual sequence of function call
22
 * opcodes (INIT_FCALL, SEND_VAR, etc), but the sequence ends with a
23
 * CALLABLE_CONVERT_PARTIAL opcode instead of DO_FCALL, similarly to
24
 * first class callables. Placeholders are compiled to SEND_PLACEHOLDER opcodes:
25
 *
26
 * $f = f($a, ?)
27
 *
28
 * 0001 INIT_FCALL f
29
 * 0002 SEND_VAR CV($a)
30
 * 0003 SEND_PLACEHOLDER
31
 * 0004 CV($f) = CALLABLE_CONVERT_PARTIAL
32
 *
33
 * SEND_PLACEHOLDER sets the argument slot type to _IS_PLACEHOLDER.
34
 *
35
 * CALLABLE_CONVERT_PARTIAL uses the information available on the stack to
36
 * create a Closure and return it, consuming the stack frame in the process
37
 * like an internal function call.
38
 *
39
 * We create the Closure by generating the relevant AST and compiling it to an
40
 * op_array. The op_array is cached in the Opcache SHM and inline caches.
41
 *
42
 * This file implements the Closure generation logic
43
 * (see zend_partial_create(), zp_compile()).
44
 */
45
46
#include "zend.h"
47
#include "zend_API.h"
48
#include "zend_arena.h"
49
#include "zend_ast.h"
50
#include "zend_compile.h"
51
#include "zend_closures.h"
52
#include "zend_attributes.h"
53
#include "zend_exceptions.h"
54
#include "ext/opcache/ZendAccelerator.h"
55
56
6.20k
static zend_always_inline bool Z_IS_PLACEHOLDER_P(const zval *p) {
57
6.20k
  return Z_TYPE_P(p) == _IS_PLACEHOLDER;
58
6.20k
}
59
60
179
static zend_always_inline bool zp_is_static_closure(const zend_function *function) {
61
179
  return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE));
62
179
}
63
64
536
static zend_always_inline bool zp_is_non_static_closure(const zend_function *function) {
65
536
  return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == ZEND_ACC_CLOSURE);
66
536
}
67
68
static zend_never_inline ZEND_COLD void zp_args_underflow(
69
    const zend_function *function, uint32_t args, uint32_t expected)
70
20
{
71
20
  zend_string *symbol = get_function_or_method_name(function);
72
20
  const char *limit = function->common.num_args <= function->common.required_num_args ?
73
15
      "exactly" : "at least";
74
75
20
  zend_argument_count_error(
76
20
    "Partial application of %s() expects %s %d arguments, %d given",
77
20
    ZSTR_VAL(symbol), limit, expected, args);
78
79
20
  zend_string_release(symbol);
80
20
}
81
82
static zend_never_inline ZEND_COLD void zp_args_overflow(
83
    const zend_function *function, uint32_t args, uint32_t expected)
84
33
{
85
33
  zend_string *symbol = get_function_or_method_name(function);
86
87
33
  zend_argument_count_error(
88
33
    "Partial application of %s() expects at most %d arguments, %d given",
89
33
    ZSTR_VAL(symbol), expected, args);
90
91
33
  zend_string_release(symbol);
92
33
}
93
94
static zend_result zp_args_check(const zend_function *function,
95
    uint32_t argc, const zval *argv,
96
    const zend_array *extra_named_args,
97
756
    bool uses_variadic_placeholder) {
98
99
756
  if (extra_named_args) {
100
200
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(extra_named_args, zend_string *key, zval *arg) {
101
200
      if (UNEXPECTED(Z_IS_PLACEHOLDER_P(arg))) {
102
5
        zend_throw_error(NULL,
103
5
            "Cannot use named placeholder for unknown or variadic parameter $%s",
104
5
            ZSTR_VAL(key));
105
5
        return FAILURE;
106
5
      }
107
200
    } ZEND_HASH_FOREACH_END();
108
50
  }
109
110
751
  if (argc < function->common.required_num_args) {
111
60
    if (uses_variadic_placeholder) {
112
      /* Missing args will be turned into placeholders */
113
40
      return SUCCESS;
114
40
    }
115
116
20
    zp_args_underflow(function, argc, function->common.required_num_args);
117
20
    return FAILURE;
118
691
  } else if (argc > function->common.num_args
119
132
      && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) {
120
33
    zp_args_overflow(function, argc, function->common.num_args);
121
33
    return FAILURE;
122
33
  }
123
124
658
  return SUCCESS;
125
751
}
126
127
typedef struct zp_names {
128
  zend_string *variadic_param;
129
  zend_string *extra_named_params;
130
  zend_string *inner_closure;
131
  zend_string *params[1]; /* argc elements */
132
} zp_names;
133
134
static bool zp_name_exists(const zp_names *names, uint32_t argc, const zend_string *name)
135
618
{
136
1.82k
  for (uint32_t i = 0; i < argc; i++) {
137
1.22k
    if (names->params[i] && zend_string_equals(names->params[i], name)) {
138
12
      return true;
139
12
    }
140
1.22k
  }
141
606
  if (names->variadic_param && zend_string_equals(names->variadic_param, name)) {
142
30
    return true;
143
30
  }
144
576
  if (names->extra_named_params && zend_string_equals(names->extra_named_params, name)) {
145
0
    return true;
146
0
  }
147
576
  if (names->inner_closure && zend_string_equals(names->inner_closure, name)) {
148
0
    return true;
149
0
  }
150
151
576
  return false;
152
576
}
153
154
static void zp_names_dtor(zp_names *names, uint32_t argc)
155
698
{
156
1.93k
  for (uint32_t i = 0; i < argc; i++) {
157
1.23k
    if (names->params[i]) {
158
1.17k
      zend_string_release(names->params[i]);
159
1.17k
    }
160
1.23k
  }
161
698
  if (names->variadic_param) {
162
109
    zend_string_release(names->variadic_param);
163
109
  }
164
698
  if (names->extra_named_params) {
165
45
    zend_string_release(names->extra_named_params);
166
45
  }
167
698
  if (names->inner_closure) {
168
113
    zend_string_release(names->inner_closure);
169
113
  }
170
698
}
171
172
static zend_string *zp_get_func_param_name(const zend_function *function, uint32_t arg_offset)
173
1.28k
{
174
1.28k
  return zend_string_copy(function->common.arg_info[arg_offset].name);
175
1.28k
}
176
177
/* Assign a name for every variable that will be used in the generated closure,
178
 * including params and used vars. */
179
static zp_names *zp_assign_names(uint32_t argc, zval *argv,
180
    zend_function *function, bool variadic_partial,
181
    zend_array *extra_named_params)
182
698
{
183
698
  zp_names *names = zend_arena_calloc(&CG(ast_arena),
184
698
      1, zend_safe_address_guarded(argc, sizeof(*names->params), offsetof(zp_names, params)));
185
186
  /* Assign names for params. We never rename those. */
187
698
  for (uint32_t offset = 0, num_args = MIN(argc, function->common.num_args);
188
1.82k
      offset < num_args; offset++) {
189
1.12k
    if (Z_IS_PLACEHOLDER_P(&argv[offset])) {
190
760
      names->params[offset] = zp_get_func_param_name(function, offset);
191
760
    }
192
1.12k
  }
193
194
  /* Assign name for the variadic param. Never renamed. */
195
698
  if (variadic_partial && (function->common.fn_flags & ZEND_ACC_VARIADIC)) {
196
109
    names->variadic_param = zp_get_func_param_name(function, function->common.num_args);
197
109
  }
198
199
  /* Assign names for placeholders that bind to the variadic param:
200
   *
201
   * function f($a, ...$args) {}
202
   * f(?, ?, ...); // The second placeholder binds into the variadic param.
203
   *
204
   * By default these are named $origNameN with N the offset from the
205
   * variadic param. In case of clash we increment N until a free name is
206
   * found. */
207
810
  for (uint32_t offset = function->common.num_args; offset < argc; offset++) {
208
112
    ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC);
209
112
    if (!Z_IS_PLACEHOLDER_P(&argv[offset])) {
210
36
      continue;
211
36
    }
212
76
    zend_string *orig_name = zp_get_func_param_name(function, function->common.num_args);
213
76
    zend_string *new_name;
214
80
    for (uint32_t n = 0;; n++) {
215
80
      new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n);
216
80
      if (!zp_name_exists(names, argc, new_name)) {
217
76
        break;
218
76
      }
219
4
      zend_string_release(new_name);
220
4
    }
221
76
    names->params[offset] = new_name;
222
76
    zend_string_release(orig_name);
223
76
  }
224
225
  /* Assign names for pre-bound params (lexical vars).
226
   * There may be clashes, we ensure to generate unique names. */
227
1.93k
  for (uint32_t offset = 0; offset < argc; offset++) {
228
1.23k
    if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) {
229
892
      continue;
230
892
    }
231
342
    uint32_t n = 2;
232
342
    zend_string *orig_name = zp_get_func_param_name(function, MIN(offset, function->common.num_args));
233
342
    zend_string *new_name = zend_string_copy(orig_name);
234
375
    while (zp_name_exists(names, argc, new_name)) {
235
33
      zend_string_release(new_name);
236
33
      new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n);
237
33
      n++;
238
33
    }
239
342
    names->params[offset] = new_name;
240
342
    zend_string_release(orig_name);
241
342
  }
242
243
  /* Assign name for $extra_named_params */
244
698
  if (extra_named_params) {
245
45
    uint32_t n = 2;
246
45
    zend_string *new_name = ZSTR_INIT_LITERAL("extra_named_params", 0);
247
45
    while (zp_name_exists(names, argc, new_name)) {
248
0
      zend_string_release(new_name);
249
0
      new_name = zend_strpprintf(0, "%s%" PRIu32, "extra_named_params", n);
250
0
      n++;
251
0
    }
252
45
    names->extra_named_params = new_name;
253
45
  }
254
255
  /* Assign name for $fn */
256
698
  if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
257
113
    uint32_t n = 2;
258
113
    zend_string *new_name = ZSTR_INIT_LITERAL("fn", 0);
259
118
    while (zp_name_exists(names, argc, new_name)) {
260
5
      zend_string_release(new_name);
261
5
      new_name = zend_strpprintf(0, "%s%" PRIu32, "fn", n);
262
5
      n++;
263
5
    }
264
113
    names->inner_closure = new_name;
265
113
  }
266
267
698
  return names;
268
698
}
269
270
ZEND_ATTRIBUTE_CONST static inline bool zp_is_power_of_two(uint32_t x)
271
869
{
272
869
  return (x > 0) && !(x & (x - 1));
273
869
}
274
275
ZEND_ATTRIBUTE_CONST static bool zp_is_simple_type(uint32_t type_mask)
276
869
{
277
869
  ZEND_ASSERT(!(type_mask & ~_ZEND_TYPE_MAY_BE_MASK));
278
279
869
  return zp_is_power_of_two(type_mask)
280
199
    || type_mask == MAY_BE_BOOL
281
175
    || type_mask == MAY_BE_ANY;
282
869
}
283
284
static zend_ast *zp_simple_type_to_ast(uint32_t type)
285
451
{
286
451
  zend_string *name;
287
288
451
  switch (type) {
289
3
    case MAY_BE_NULL:
290
3
      name = ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE);
291
3
      break;
292
0
    case MAY_BE_TRUE:
293
0
      name = ZSTR_KNOWN(ZEND_STR_TRUE);
294
0
      break;
295
0
    case MAY_BE_FALSE:
296
0
      name = ZSTR_KNOWN(ZEND_STR_FALSE);
297
0
      break;
298
107
    case MAY_BE_LONG:
299
107
      name = ZSTR_KNOWN(ZEND_STR_INT);
300
107
      break;
301
12
    case MAY_BE_DOUBLE:
302
12
      name = ZSTR_KNOWN(ZEND_STR_FLOAT);
303
12
      break;
304
97
    case MAY_BE_STRING:
305
97
      name = ZSTR_KNOWN(ZEND_STR_STRING);
306
97
      break;
307
12
    case MAY_BE_BOOL:
308
12
      name = ZSTR_KNOWN(ZEND_STR_BOOL);
309
12
      break;
310
23
    case MAY_BE_VOID:
311
23
      name = ZSTR_KNOWN(ZEND_STR_VOID);
312
23
      break;
313
0
    case MAY_BE_NEVER:
314
0
      name = ZSTR_KNOWN(ZEND_STR_NEVER);
315
0
      break;
316
42
    case MAY_BE_OBJECT:
317
42
      name = ZSTR_KNOWN(ZEND_STR_OBJECT);
318
42
      break;
319
82
    case MAY_BE_ANY:
320
82
      name = ZSTR_KNOWN(ZEND_STR_MIXED);
321
82
      break;
322
3
    case MAY_BE_CALLABLE:
323
3
      return zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE);
324
62
    case MAY_BE_ARRAY:
325
62
      return zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY);
326
8
    case MAY_BE_STATIC:
327
8
      return zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC);
328
0
    default:
329
0
      ZEND_UNREACHABLE();
330
451
  }
331
332
378
  zend_ast *ast = zend_ast_create_zval_from_str(name);
333
378
  ast->attr = ZEND_NAME_NOT_FQ;
334
335
378
  return ast;
336
451
}
337
338
static zend_ast *zp_type_name_to_ast(zend_string *name)
339
19
{
340
19
  zend_ast *ast = zend_ast_create_zval_from_str(name);
341
342
19
  if (zend_get_class_fetch_type(name) != ZEND_FETCH_CLASS_DEFAULT) {
343
8
    ast->attr = ZEND_NAME_NOT_FQ;
344
11
  } else {
345
11
    ast->attr = ZEND_NAME_FQ;
346
11
  }
347
348
19
  return ast;
349
19
}
350
351
static zend_ast *zp_type_to_ast(const zend_type type)
352
1.06k
{
353
1.06k
  if (!ZEND_TYPE_IS_SET(type)) {
354
610
    return NULL;
355
610
  }
356
357
459
  if (ZEND_TYPE_IS_UNION(type)
358
459
      || (ZEND_TYPE_IS_COMPLEX(type) && ZEND_TYPE_PURE_MASK(type))
359
459
      || (ZEND_TYPE_PURE_MASK(type) && !zp_is_simple_type(ZEND_TYPE_PURE_MASK(type)))) {
360
    /* This is a union type */
361
362
11
    zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_UNION);
363
364
    /* Add complex types if any */
365
11
    if (ZEND_TYPE_HAS_LIST(type)) {
366
0
      ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), const zend_type *type_ptr) {
367
0
        type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr));
368
0
      } ZEND_TYPE_LIST_FOREACH_END();
369
11
    } else if (ZEND_TYPE_HAS_NAME(type)) {
370
0
      zend_ast *name_ast = zp_type_name_to_ast(
371
0
          zend_string_copy(ZEND_TYPE_NAME(type)));
372
0
      type_ast = zend_ast_list_add(type_ast, name_ast);
373
11
    } else {
374
11
      ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type));
375
11
    }
376
377
    /* Add simple types if any */
378
11
    uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
379
380
    /* IS_TRUE|IS_FALSE is represented as a single bool node */
381
11
    if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
382
0
      type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(MAY_BE_BOOL));
383
0
      type_mask &= ~MAY_BE_BOOL;
384
0
    }
385
209
    for (uint32_t may_be_type = 1; may_be_type < _ZEND_TYPE_MAY_BE_MASK; may_be_type <<= 1) {
386
198
      if (type_mask & may_be_type) {
387
22
        type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(may_be_type));
388
22
      }
389
198
    }
390
391
11
    return type_ast;
392
11
  }
393
394
448
  if (ZEND_TYPE_IS_INTERSECTION(type)) {
395
0
    ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type));
396
0
    zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_INTERSECTION);
397
0
    ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), const zend_type *type_ptr) {
398
0
      type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr));
399
0
    } ZEND_TYPE_LIST_FOREACH_END();
400
0
    return type_ast;
401
0
  }
402
403
448
  if (ZEND_TYPE_HAS_NAME(type)) {
404
19
    ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type));
405
19
    zend_ast *type_ast = zp_type_name_to_ast(
406
19
        zend_string_copy(ZEND_TYPE_NAME(type)));
407
19
    return type_ast;
408
19
  }
409
410
429
  ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type));
411
412
429
  uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
413
429
  ZEND_ASSERT(zp_is_simple_type(type_mask));
414
415
429
  return zp_simple_type_to_ast(type_mask);
416
429
}
417
418
static zend_result zp_get_param_default_value(zval *result, zend_function *function, uint32_t arg_offset)
419
138
{
420
138
  ZEND_ASSERT(arg_offset < function->common.num_args);
421
422
138
  if (function->type == ZEND_USER_FUNCTION) {
423
123
    zend_op *opline = &function->op_array.opcodes[arg_offset];
424
123
    if (EXPECTED(opline->opcode == ZEND_RECV_INIT)) {
425
123
      ZVAL_COPY(result, RT_CONSTANT(opline, opline->op2));
426
123
      return SUCCESS;
427
123
    }
428
0
    ZEND_ASSERT(opline->opcode == ZEND_RECV);
429
15
  } else {
430
15
    if (function->common.fn_flags & ZEND_ACC_USER_ARG_INFO) {
431
5
      goto error;
432
5
    }
433
434
10
    const zend_arg_info *arg_info = &function->internal_function.arg_info[arg_offset];
435
436
10
    if (zend_get_default_from_internal_arg_info(result, arg_info) == SUCCESS) {
437
0
      return SUCCESS;
438
0
    }
439
10
  }
440
441
15
error:
442
15
  zend_argument_error_ex(zend_ce_argument_count_error, function, arg_offset + 1,
443
15
      "must be passed explicitly, because the default value is not known");
444
445
15
  return FAILURE;
446
138
}
447
448
static bool zp_arg_must_be_sent_by_ref(const zend_function *function, uint32_t arg_num)
449
1.65k
{
450
1.65k
  if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
451
1.65k
    if (QUICK_ARG_MUST_BE_SENT_BY_REF(function, arg_num)) {
452
32
      return true;
453
32
    }
454
1.65k
  } else if (ARG_MUST_BE_SENT_BY_REF(function, arg_num)) {
455
0
    return true;
456
0
  }
457
1.61k
  return false;
458
1.65k
}
459
460
static zend_ast *zp_attribute_to_ast(zend_attribute *attribute)
461
29
{
462
29
  zend_ast *args_ast;
463
29
  if (attribute->argc) {
464
0
    args_ast = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST);
465
0
    for (uint32_t i = 0; i < attribute->argc; i++) {
466
0
      zend_ast *arg_ast = zend_ast_create_zval(&attribute->args[i].value);
467
0
      if (attribute->args[i].name) {
468
0
        arg_ast = zend_ast_create(ZEND_AST_NAMED_ARG,
469
0
            zend_ast_create_zval_from_str(
470
0
              zend_string_copy(attribute->args[i].name)),
471
0
            arg_ast);
472
0
      }
473
0
      args_ast = zend_ast_list_add(args_ast, arg_ast);
474
0
    }
475
29
  } else {
476
29
    args_ast = NULL;
477
29
  }
478
29
  return zend_ast_create(ZEND_AST_ATTRIBUTE,
479
29
      zend_ast_create_zval_from_str(zend_string_copy(attribute->name)),
480
29
      args_ast);
481
29
}
482
483
static zend_ast *zp_param_attributes_to_ast(zend_function *function,
484
    uint32_t offset)
485
935
{
486
935
  zend_ast *attributes_ast = NULL;
487
935
  if (!function->common.attributes) {
488
891
    return NULL;
489
891
  }
490
491
  /* Inherit the SensitiveParameter attribute */
492
44
  zend_attribute *attr = zend_get_parameter_attribute_str(
493
44
      function->common.attributes,
494
44
      "sensitiveparameter", strlen("sensitiveparameter"), offset);
495
44
  if (attr) {
496
22
    attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP,
497
22
        zp_attribute_to_ast(attr));
498
22
    attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST,
499
22
        attributes_ast);
500
22
  }
501
502
44
  return attributes_ast;
503
935
}
504
505
static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array,
506
    const zend_op *declaring_opline)
507
666
{
508
  /* We attempt to generate a name that hints at where the PFA was created,
509
   * similarly to Closures (GH-13550).
510
   * We do not attempt to make the name unique. */
511
512
666
  zend_string *filename = declaring_op_array->filename;
513
666
  uint32_t start_lineno = declaring_opline->lineno;
514
515
666
  zend_string *class = zend_empty_string;
516
666
  zend_string *separator = zend_empty_string;
517
666
  zend_string *function = filename;
518
666
  const char *parens = "";
519
520
666
  if (declaring_op_array->function_name) {
521
45
    function = declaring_op_array->function_name;
522
45
    if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) {
523
      /* If the parent function is a closure, don't redundantly
524
       * add the classname and parentheses. */
525
45
    } else {
526
45
      parens = "()";
527
528
45
      if (declaring_op_array->scope && declaring_op_array->scope->name) {
529
24
        class = declaring_op_array->scope->name;
530
24
        separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM);
531
24
      }
532
45
    }
533
45
  }
534
535
666
  zend_string *name = zend_strpprintf_unchecked(
536
666
    0,
537
666
    "{closure:pfa:%S%S%S%s:%" PRIu32 "}",
538
666
    class,
539
666
    separator,
540
666
    function,
541
666
    parens,
542
666
    start_lineno
543
666
  );
544
545
666
  return name;
546
666
}
547
548
/* Generate the AST for calling the actual function */
549
static zend_ast *zp_compile_forwarding_call(
550
  zval *this_ptr, zend_function *function,
551
  uint32_t argc, zval *argv, zend_array *extra_named_params,
552
  zp_names *var_names, bool uses_variadic_placeholder, uint32_t num_args,
553
  zend_class_entry *called_scope, zend_type return_type,
554
  bool forward_superfluous_args,
555
  zend_ast *stmts_ast)
556
829
{
557
829
  bool is_assert = zend_string_equals(function->common.function_name,
558
829
      ZSTR_KNOWN(ZEND_STR_ASSERT));
559
560
829
  zend_ast *args_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST);
561
829
  zend_ast *call_ast = NULL;
562
563
829
  if (is_assert) {
564
    /* We are going to call assert() dynamically (via call_user_func),
565
     * otherwise assert() would print the generated AST on failure, which is
566
     * irrelevant. */
567
8
    args_ast = zend_ast_list_add(args_ast,
568
8
        zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ASSERT)));
569
8
  }
570
571
  /* Generate positional arguments */
572
2.28k
  for (uint32_t offset = 0; offset < argc; offset++) {
573
1.47k
    if (Z_ISUNDEF(argv[offset])) {
574
      /* Argument was not passed. Pass its default value. */
575
56
      if (offset < function->common.required_num_args) {
576
        /* Required param was not passed. This can happen due to named
577
         * args. Using the same exception CE and message as
578
         * zend_handle_undef_args(). */
579
17
        zend_argument_error_ex(zend_ce_argument_count_error, function, offset + 1, "not passed");
580
17
        goto error;
581
17
      }
582
39
      zval default_value;
583
39
      if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) {
584
5
        ZEND_ASSERT(EG(exception));
585
5
        goto error;
586
5
      }
587
34
      zend_ast *default_value_ast;
588
34
      if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
589
        /* Must dup AST because we are going to destroy it */
590
24
        default_value_ast = zend_ast_dup(Z_ASTVAL(default_value));
591
24
        zval_ptr_dtor_nogc(&default_value);
592
24
      } else {
593
10
        default_value_ast = zend_ast_create_zval(&default_value);
594
10
      }
595
34
      args_ast = zend_ast_list_add(args_ast, default_value_ast);
596
1.42k
    } else {
597
1.42k
      args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR,
598
1.42k
            zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset]))));
599
1.42k
    }
600
1.47k
  }
601
  /* Use unpacking to pass extra named params */
602
807
  if (extra_named_params) {
603
45
    args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK,
604
45
          zend_ast_create(ZEND_AST_VAR,
605
45
            zend_ast_create_zval_from_str(zend_string_copy(var_names->extra_named_params)))));
606
45
  }
607
807
  if (uses_variadic_placeholder) {
608
391
    if (function->common.fn_flags & ZEND_ACC_VARIADIC) {
609
      /* Pass variadic params */
610
109
      args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK,
611
109
            zend_ast_create(ZEND_AST_VAR,
612
109
              zend_ast_create_zval_from_str(zend_string_copy(var_names->variadic_param)))));
613
282
    } else if (forward_superfluous_args) {
614
      /* When a '...' placeholder is used, and the underlying function is
615
       * not variadic, superfluous arguments are forwarded.
616
       * Add a ...array_slice(func_get_args(), n) argument, which should
617
       * be compiled as ZEND_AST_UNPACK + ZEND_FUNC_GET_ARGS. */
618
619
141
      zend_ast *func_get_args_name_ast = zend_ast_create_zval_from_str(
620
141
          zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_GET_ARGS)));
621
141
      func_get_args_name_ast->attr = ZEND_NAME_FQ;
622
623
141
      zend_ast *array_slice_name_ast = zend_ast_create_zval_from_str(
624
141
          zend_string_copy(ZSTR_KNOWN(ZEND_STR_ARRAY_SLICE)));
625
141
      array_slice_name_ast->attr = ZEND_NAME_FQ;
626
627
141
      args_ast = zend_ast_list_add(args_ast,
628
141
        zend_ast_create(ZEND_AST_UNPACK,
629
141
          zend_ast_create(ZEND_AST_CALL,
630
141
              array_slice_name_ast,
631
141
              zend_ast_create_list(2, ZEND_AST_ARG_LIST,
632
141
                zend_ast_create(ZEND_AST_CALL,
633
141
                  func_get_args_name_ast,
634
141
                  zend_ast_create_list(0, ZEND_AST_ARG_LIST)),
635
141
                zend_ast_create_zval_from_long(num_args)))));
636
141
    }
637
391
  }
638
639
807
  if (is_assert) {
640
8
    zend_ast *func_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CALL_USER_FUNC));
641
8
    func_name_ast->attr = ZEND_NAME_FQ;
642
8
    call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast);
643
799
  } else if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
644
136
    zend_ast *fn_ast = zend_ast_create(ZEND_AST_VAR,
645
136
          zend_ast_create_zval_from_str(zend_string_copy(var_names->inner_closure)));
646
136
    call_ast = zend_ast_create(ZEND_AST_CALL, fn_ast, args_ast);
647
663
  } else if (Z_TYPE_P(this_ptr) == IS_OBJECT) {
648
131
    zend_ast *this_ast = zend_ast_create(ZEND_AST_VAR,
649
131
          zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_THIS)));
650
131
    zend_ast *method_name_ast = zend_ast_create_zval_from_str(
651
131
        zend_string_copy(function->common.function_name));
652
131
    call_ast = zend_ast_create(ZEND_AST_METHOD_CALL, this_ast,
653
131
        method_name_ast, args_ast);
654
532
  } else if (called_scope) {
655
55
    zend_ast *class_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_STATIC));
656
55
    class_name_ast->attr = ZEND_NAME_NOT_FQ;
657
55
    zend_ast *method_name_ast = zend_ast_create_zval_from_str(
658
55
        zend_string_copy(function->common.function_name));
659
55
    call_ast = zend_ast_create(ZEND_AST_STATIC_CALL, class_name_ast,
660
55
        method_name_ast, args_ast);
661
477
  } else {
662
477
    zend_ast *func_name_ast = zend_ast_create_zval_from_str(zend_string_copy(function->common.function_name));
663
477
    func_name_ast->attr = ZEND_NAME_FQ;
664
477
    call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast);
665
477
  }
666
667
  /* Void functions can not 'return $expr' */
668
807
  if (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_VOID) {
669
23
    stmts_ast = zend_ast_list_add(stmts_ast, call_ast);
670
784
  } else {
671
784
    zend_ast *return_ast = zend_ast_create(ZEND_AST_RETURN, call_ast);
672
784
    stmts_ast = zend_ast_list_add(stmts_ast, return_ast);
673
784
  }
674
675
807
  return stmts_ast;
676
677
22
error:
678
22
  zend_ast_destroy(args_ast);
679
22
  zend_ast_destroy(call_ast);
680
22
  return NULL;
681
829
}
682
683
static uint32_t zp_compute_num_required(const zend_function *function,
684
162
    uint32_t orig_offset, uint32_t new_offset, uint32_t num_required) {
685
162
  if (orig_offset < function->common.num_args) {
686
162
    if (orig_offset < function->common.required_num_args) {
687
63
      num_required = MAX(num_required, new_offset + 1);
688
63
    }
689
162
  } else {
690
0
    ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC);
691
    /* Placeholders that run into the variadic portion become
692
     * required and make all params before them required */
693
0
    ZEND_ASSERT(orig_offset >= num_required);
694
0
    num_required = new_offset + 1;
695
0
  }
696
697
162
  return num_required;
698
162
}
699
700
/* Compile PFA to an op_array */
701
static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
702
    uint32_t argc, zval *argv, zend_array *extra_named_params,
703
    const zend_array *named_positions,
704
    const zend_op_array *declaring_op_array,
705
    const zend_op *declaring_opline, void **cache_slot,
706
790
    bool uses_variadic_placeholder) {
707
708
790
  zend_op_array *op_array = NULL;
709
710
790
  if (UNEXPECTED(function->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)) {
711
34
    const char *format = "Cannot call %S() dynamically";
712
34
    zend_throw_error(NULL, format, function->common.function_name);
713
34
    return NULL;
714
34
  }
715
716
756
  if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) {
717
58
    ZEND_ASSERT(EG(exception));
718
58
    return NULL;
719
58
  }
720
721
698
  zend_class_entry *called_scope;
722
698
  if (Z_TYPE_P(this_ptr) == IS_OBJECT) {
723
135
    called_scope = Z_OBJCE_P(this_ptr);
724
563
  } else {
725
563
    called_scope = Z_CE_P(this_ptr);
726
563
  }
727
728
  /* CG(ast_arena) is usually NULL, so we can't just make a snapshot */
729
698
  zend_arena *orig_ast_arena = CG(ast_arena);
730
698
  CG(ast_arena) = zend_arena_create(1024 * 4);
731
732
698
  uint32_t orig_lineno = CG(zend_lineno);
733
698
  CG(zend_lineno) = zend_get_executed_lineno();
734
735
698
  uint32_t new_argc = argc;
736
737
698
  if (uses_variadic_placeholder) {
738
    /* A variadic placeholder generates an implicit positional placeholder
739
     * for any unspecified arg, so make room for those. */
740
260
    new_argc = MAX(new_argc, function->common.num_args);
741
260
  }
742
743
698
  zval *tmp = zend_arena_alloc(&CG(ast_arena), zend_safe_address_guarded(new_argc, sizeof(*tmp), 0));
744
698
  memcpy(tmp, argv, zend_safe_address_guarded(argc, sizeof(*tmp), 0));
745
698
  argv = tmp;
746
747
  /* Compute param positions and number of required args, add implicit
748
   * placeholders.
749
   *
750
   * Parameters are placed in the following order:
751
   * - Positional placeholders
752
   * - Then named placeholders in their syntax order
753
   * - Then implicit placeholders added by '...'
754
   */
755
698
  uint32_t num_params = 0;
756
698
  uint32_t num_required = 0;
757
698
  uint32_t *arg_to_param_offset_map = zend_arena_alloc(&CG(ast_arena),
758
698
      zend_safe_address_guarded(new_argc, sizeof(*arg_to_param_offset_map), 0));
759
698
  {
760
698
    uint32_t num_positional = 0;
761
762
    /* First, we handle explicit placeholders */
763
1.83k
    for (uint32_t arg_offset = 0; arg_offset < argc; arg_offset++) {
764
1.14k
      if (!Z_IS_PLACEHOLDER_P(&argv[arg_offset])) {
765
466
        continue;
766
466
      }
767
768
674
      num_params++;
769
770
674
      zend_arg_info *arg_info = &function->common.arg_info[MIN(arg_offset, function->common.num_args)];
771
674
      zval *named_pos = named_positions ? zend_hash_find(named_positions, arg_info->name) : NULL;
772
674
      uint32_t param_offset;
773
674
      if (named_pos) {
774
        /* Placeholder is sent as named arg. 'num_positional' is fixed
775
         * at this point. */
776
126
        param_offset = num_positional + Z_LVAL_P(named_pos);
777
548
      } else {
778
        /* Placeholder is sent as positional */
779
548
        param_offset = num_positional++;
780
548
      }
781
782
674
      arg_to_param_offset_map[arg_offset] = param_offset;
783
674
    }
784
785
698
    num_required = num_params;
786
787
    /* Handle implicit placeholders added by '...' */
788
698
    if (uses_variadic_placeholder) {
789
734
      for (uint32_t arg_offset = 0; arg_offset < new_argc; arg_offset++) {
790
474
        if (arg_offset < argc && !Z_ISUNDEF(argv[arg_offset])) {
791
312
          continue;
792
312
        }
793
794
        /* Unspecified parameters become placeholders */
795
162
        Z_TYPE_INFO(argv[arg_offset]) = _IS_PLACEHOLDER;
796
797
162
        num_params++;
798
799
162
        uint32_t param_offset = num_params - 1;
800
801
162
        arg_to_param_offset_map[arg_offset] = param_offset;
802
803
162
        num_required = zp_compute_num_required(function,
804
162
            arg_offset, param_offset, num_required);
805
162
      }
806
260
    }
807
698
  }
808
809
698
  argc = new_argc;
810
811
  /* Assign variable names */
812
813
698
  zp_names *var_names = zp_assign_names(argc, argv, function,
814
698
      uses_variadic_placeholder, extra_named_params);
815
816
  /* Generate AST */
817
818
698
  zend_ast *lexical_vars_ast = zend_ast_create_list(0, ZEND_AST_CLOSURE_USES);
819
698
  zend_ast *params_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST);
820
698
  zend_ast *return_type_ast = NULL;
821
698
  zend_ast *stmts_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST);
822
698
  zend_ast *attributes_ast = NULL;
823
824
  /* Generate AST for params and lexical vars */
825
698
  {
826
    /* The inner Closure, if any, is assumed to be the first lexical var by
827
     * do_closure_bind(). */
828
698
    if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
829
113
      zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
830
113
          zend_string_copy(var_names->inner_closure));
831
113
      lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast);
832
113
    }
833
834
698
    zend_ast **params = zend_arena_calloc(&CG(ast_arena), num_params, sizeof(*params));
835
1.91k
    for (uint32_t offset = 0; offset < argc; offset++) {
836
1.22k
      if (Z_IS_PLACEHOLDER_P(&argv[offset])) {
837
836
        zend_arg_info *arg_info = &function->common.arg_info[MIN(offset, function->common.num_args)];
838
839
836
        int param_flags = 0;
840
836
        if (zp_arg_must_be_sent_by_ref(function, offset+1)) {
841
12
          param_flags |= ZEND_PARAM_REF;
842
12
        }
843
844
836
        uint32_t param_offset = arg_to_param_offset_map[offset];
845
836
        zend_ast *param_type_ast = zp_type_to_ast(arg_info->type);
846
836
        zend_ast *default_value_ast = NULL;
847
836
        if (param_offset >= num_required) {
848
99
          zval default_value;
849
99
          if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) {
850
25
            for (uint32_t i = 0; i < num_params; i++) {
851
15
              zend_ast_destroy(params[i]);
852
15
            }
853
10
            goto error;
854
10
          }
855
89
          default_value_ast = zend_ast_create_zval(&default_value);
856
89
        }
857
858
826
        ZEND_ASSERT(offset < function->common.num_args || (function->common.fn_flags & ZEND_ACC_VARIADIC));
859
860
826
        zend_ast *attributes_ast = zp_param_attributes_to_ast(function, MIN(offset, function->common.num_args));
861
826
        params[param_offset] = zend_ast_create_ex(ZEND_AST_PARAM,
862
826
            param_flags, param_type_ast,
863
826
            zend_ast_create_zval_from_str(
864
826
              zend_string_copy(var_names->params[offset])),
865
826
            default_value_ast, attributes_ast, NULL, NULL);
866
867
826
      } else if (!Z_ISUNDEF(argv[offset])) {
868
        // TODO: If the pre-bound parameter is a literal, it can be a
869
        // literal in the function body instead of a lexical var.
870
337
        zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
871
337
            zend_string_copy(var_names->params[offset]));
872
337
        if (zp_arg_must_be_sent_by_ref(function, offset+1)) {
873
6
          lexical_var_ast->attr = ZEND_BIND_REF;
874
6
        }
875
337
        lexical_vars_ast = zend_ast_list_add(
876
337
            lexical_vars_ast, lexical_var_ast);
877
337
      }
878
1.22k
    }
879
880
1.50k
    for (uint32_t i = 0; i < num_params; i++) {
881
821
      params_ast = zend_ast_list_add(params_ast, params[i]);
882
821
    }
883
688
  }
884
885
688
  if (extra_named_params) {
886
45
    zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
887
45
        zend_string_copy(var_names->extra_named_params));
888
45
    lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast);
889
45
  }
890
891
  /* If we have a variadic placeholder and the underlying function is
892
   * variadic, add a variadic param. */
893
688
  if (uses_variadic_placeholder
894
250
      && (function->common.fn_flags & ZEND_ACC_VARIADIC)) {
895
109
    zend_arg_info *arg_info = &function->common.arg_info[function->common.num_args];
896
109
    int param_flags = ZEND_PARAM_VARIADIC;
897
109
    if (zp_arg_must_be_sent_by_ref(function, function->common.num_args+1)) {
898
6
      param_flags |= ZEND_PARAM_REF;
899
6
    }
900
109
    zend_ast *param_type_ast = zp_type_to_ast(arg_info->type);
901
109
    zend_ast *attributes_ast = zp_param_attributes_to_ast(function, function->common.num_args);
902
109
    params_ast = zend_ast_list_add(params_ast, zend_ast_create_ex(ZEND_AST_PARAM,
903
109
        param_flags, param_type_ast,
904
109
        zend_ast_create_zval_from_str(
905
109
          zend_string_copy(var_names->variadic_param)),
906
109
        NULL, attributes_ast, NULL, NULL));
907
109
  }
908
909
688
  zend_type return_type = {0};
910
688
  if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
911
124
    return_type = (function->common.arg_info-1)->type;
912
124
    return_type_ast = zp_type_to_ast(return_type);
913
124
  }
914
915
  /**
916
   * Generate function body.
917
   *
918
   * If we may need to forward superflous arguments, do that conditionally, as
919
   * it's faster:
920
   *
921
   * if (func_num_args() <= n) {
922
   *    // normal call
923
   * } else {
924
   *    // call with superflous arg forwarding
925
   * }
926
   *
927
   * The func_num_args() call should be compiled to a single FUNC_NUM_ARGS op.
928
   */
929
930
688
  if (uses_variadic_placeholder && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) {
931
141
    zend_ast *no_forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST);
932
141
    zend_ast *forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST);
933
934
141
    no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
935
141
        argc, argv, extra_named_params,
936
141
        var_names, uses_variadic_placeholder, num_params,
937
141
        called_scope, return_type, false, no_forwarding_ast);
938
939
141
    if (!no_forwarding_ast) {
940
0
      ZEND_ASSERT(EG(exception));
941
0
      goto error;
942
0
    }
943
944
141
    forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
945
141
        argc, argv, extra_named_params,
946
141
        var_names, uses_variadic_placeholder, num_params,
947
141
        called_scope, return_type, true, forwarding_ast);
948
949
141
    if (!forwarding_ast) {
950
0
      ZEND_ASSERT(EG(exception));
951
0
      zend_ast_destroy(no_forwarding_ast);
952
0
      goto error;
953
0
    }
954
955
141
    zend_ast *func_num_args_name_ast = zend_ast_create_zval_from_str(
956
141
        zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_NUM_ARGS)));
957
141
    func_num_args_name_ast->attr = ZEND_NAME_FQ;
958
959
141
    stmts_ast = zend_ast_list_add(stmts_ast,
960
141
      zend_ast_create_list(2, ZEND_AST_IF,
961
141
        zend_ast_create(ZEND_AST_IF_ELEM,
962
141
          zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL,
963
141
            zend_ast_create(ZEND_AST_CALL, func_num_args_name_ast,
964
141
              zend_ast_create_list(0, ZEND_AST_ARG_LIST)),
965
141
            zend_ast_create_zval_from_long(num_params)),
966
141
          no_forwarding_ast),
967
141
        zend_ast_create(ZEND_AST_IF_ELEM,
968
141
          NULL,
969
141
          forwarding_ast)));
970
547
  } else {
971
547
    stmts_ast = zp_compile_forwarding_call(this_ptr, function,
972
547
        argc, argv, extra_named_params,
973
547
        var_names, uses_variadic_placeholder, num_params,
974
547
        called_scope, return_type, false, stmts_ast);
975
976
547
    if (!stmts_ast) {
977
22
      ZEND_ASSERT(EG(exception));
978
22
      goto error;
979
22
    }
980
547
  }
981
982
  /* Inherit the NoDiscard attribute */
983
666
  if (function->common.attributes) {
984
18
    zend_attribute *attr = zend_get_attribute_str(
985
18
        function->common.attributes, "nodiscard", strlen("nodiscard"));
986
18
    if (attr) {
987
7
      attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP,
988
7
          zp_attribute_to_ast(attr));
989
7
      attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST,
990
7
          attributes_ast);
991
7
    }
992
18
  }
993
994
666
  int closure_flags = function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE;
995
666
  zend_ast *closure_ast = zend_ast_create_decl(ZEND_AST_CLOSURE,
996
666
      closure_flags, CG(zend_lineno), NULL,
997
666
      NULL, params_ast, lexical_vars_ast, stmts_ast,
998
666
      return_type_ast, attributes_ast);
999
1000
666
  if (Z_TYPE_P(this_ptr) != IS_OBJECT && !zp_is_non_static_closure(function)) {
1001
488
    ((zend_ast_decl*)closure_ast)->flags |= ZEND_ACC_STATIC;
1002
488
  }
1003
1004
666
#if ZEND_DEBUG
1005
666
  {
1006
666
    const char *tmp = getenv("DUMP_PFA_AST");
1007
666
    if (tmp && ZEND_ATOL(tmp)) {
1008
0
      zend_string *str = zend_ast_export("", closure_ast, "");
1009
0
      fprintf(stderr, "PFA AST: %s\n", ZSTR_VAL(str));
1010
0
      zend_string_release(str);
1011
0
    }
1012
666
  }
1013
666
#endif
1014
1015
666
  zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline);
1016
1017
666
  op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array,
1018
666
      declaring_opline, function, pfa_name);
1019
1020
666
  zend_ast_destroy(closure_ast);
1021
1022
698
clean:
1023
698
  zp_names_dtor(var_names, argc);
1024
698
  zend_arena_destroy(CG(ast_arena));
1025
698
  CG(ast_arena) = orig_ast_arena;
1026
698
  CG(zend_lineno) = orig_lineno;
1027
1028
698
  return op_array;
1029
1030
32
error:
1031
32
  zend_ast_destroy(lexical_vars_ast);
1032
32
  zend_ast_destroy(params_ast);
1033
32
  zend_ast_destroy(return_type_ast);
1034
32
  zend_ast_destroy(stmts_ast);
1035
32
  zend_ast_destroy(attributes_ast);
1036
32
  goto clean;
1037
666
}
1038
1039
/* Get the op_array of a PFA from caches or compile it */
1040
static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function,
1041
    uint32_t argc, zval *argv, zend_array *extra_named_params,
1042
    const zend_array *named_positions,
1043
    const zend_op_array *declaring_op_array,
1044
    const zend_op *declaring_opline, void **cache_slot,
1045
988
    bool uses_variadic_placeholder) {
1046
1047
988
  if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION
1048
988
          ? cache_slot[0] == function
1049
988
          : cache_slot[0] == function->op_array.opcodes)) {
1050
0
    ZEND_ASSERT(!(function->common.fn_flags & ZEND_ACC_NEVER_CACHE));
1051
0
    return cache_slot[1];
1052
0
  }
1053
1054
988
  const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array,
1055
988
      declaring_opline, function);
1056
1057
988
  if (UNEXPECTED(!op_array)) {
1058
790
    op_array = zp_compile(this_ptr, function, argc, argv,
1059
790
      extra_named_params, named_positions, declaring_op_array, declaring_opline,
1060
790
      cache_slot, uses_variadic_placeholder);
1061
790
  }
1062
1063
988
  if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) {
1064
838
    cache_slot[0] = function->type == ZEND_INTERNAL_FUNCTION
1065
838
      ? (void*)function
1066
838
      : (void*)function->op_array.opcodes;
1067
838
    cache_slot[1] = (zend_op_array*)op_array;
1068
838
  }
1069
1070
988
  return op_array;
1071
988
}
1072
1073
static void zp_free_unbound_args(uint32_t start, uint32_t argc, zval *argv)
1074
146
{
1075
444
  for (uint32_t offset = start; offset < argc; offset++) {
1076
298
    zval_ptr_dtor_nogc(&argv[offset]);
1077
298
  }
1078
146
}
1079
1080
/* Bind pre-bound arguments as lexical vars */
1081
static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv,
1082
864
    zend_array *extra_named_params) {
1083
1084
864
  zend_arg_info *arg_infos = function->common.arg_info;
1085
864
  uint32_t bind_offset = 0;
1086
1087
864
  if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
1088
132
    zval var;
1089
132
    ZVAL_OBJ(&var, ZEND_CLOSURE_OBJECT(function));
1090
132
    Z_ADDREF(var);
1091
132
    zend_closure_bind_var_ex(result, bind_offset, &var);
1092
132
    bind_offset += sizeof(Bucket);
1093
132
  }
1094
1095
2.16k
  for (uint32_t offset = 0; offset < argc; offset++) {
1096
1.32k
    zval *var = &argv[offset];
1097
1.32k
    if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) {
1098
931
      continue;
1099
931
    }
1100
390
    zend_arg_info *arg_info;
1101
390
    if (offset < function->common.num_args) {
1102
343
      arg_info = &arg_infos[offset];
1103
343
    } else if (function->common.fn_flags & ZEND_ACC_VARIADIC) {
1104
47
      arg_info = &arg_infos[function->common.num_args];
1105
47
    } else {
1106
0
      arg_info = NULL;
1107
0
    }
1108
390
    if (arg_info && ZEND_TYPE_IS_SET(arg_info->type)
1109
139
        && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var,
1110
390
          /* current_frame */ true, /* is_internal */ false))) {
1111
22
      zend_string *need_msg = zend_type_to_string_resolved(arg_info->type,
1112
22
          function->common.scope);
1113
22
      zend_argument_type_error_ex(function, offset + 1,
1114
22
          "must be of type %s, %s given",
1115
22
          ZSTR_VAL(need_msg), zend_zval_value_name(var));
1116
22
      zend_string_release(need_msg);
1117
22
      zval_ptr_dtor(result);
1118
22
      ZVAL_NULL(result);
1119
22
      zp_free_unbound_args(offset, argc, argv);
1120
22
      return;
1121
22
    }
1122
368
    ZEND_ASSERT(zp_arg_must_be_sent_by_ref(function, offset+1) ? Z_ISREF_P(var) : !Z_ISREF_P(var));
1123
368
    zend_closure_bind_var_ex(result, bind_offset, var);
1124
368
    bind_offset += sizeof(Bucket);
1125
368
  }
1126
1127
842
  if (extra_named_params) {
1128
59
    zval var;
1129
59
    ZVAL_ARR(&var, extra_named_params);
1130
59
    Z_ADDREF(var);
1131
59
    zend_closure_bind_var_ex(result, bind_offset, &var);
1132
59
  }
1133
842
}
1134
1135
void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
1136
    uint32_t argc, zval *argv, zend_array *extra_named_params,
1137
    const zend_array *named_positions,
1138
    const zend_op_array *declaring_op_array,
1139
    const zend_op *declaring_opline, void **cache_slot,
1140
988
    bool uses_variadic_placeholder) {
1141
1142
988
  const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv,
1143
988
      extra_named_params, named_positions,
1144
988
      declaring_op_array, declaring_opline,
1145
988
      cache_slot, uses_variadic_placeholder);
1146
1147
988
  if (UNEXPECTED(!op_array)) {
1148
124
    ZEND_ASSERT(EG(exception));
1149
124
    zp_free_unbound_args(0, argc, argv);
1150
124
    ZVAL_NULL(result);
1151
124
    return;
1152
124
  }
1153
1154
864
  zend_class_entry *called_scope;
1155
864
  zval object;
1156
1157
864
  if (Z_TYPE_P(this_ptr) == IS_OBJECT) {
1158
179
    called_scope = Z_OBJCE_P(this_ptr);
1159
685
  } else {
1160
685
    called_scope = Z_CE_P(this_ptr);
1161
685
  }
1162
1163
864
  if (Z_TYPE_P(this_ptr) == IS_OBJECT && !zp_is_static_closure(function)) {
1164
179
    ZVAL_COPY_VALUE(&object, this_ptr);
1165
685
  } else {
1166
685
    ZVAL_UNDEF(&object);
1167
685
  }
1168
1169
864
  zend_create_partial_closure(result, (zend_function*)op_array,
1170
864
      function->common.scope, called_scope, &object,
1171
864
      (function->common.fn_flags & ZEND_ACC_CLOSURE) != 0);
1172
1173
864
  zp_bind(result, function, argc, argv, extra_named_params);
1174
864
}
1175
1176
void zend_partial_op_array_dtor(zval *pDest)
1177
48
{
1178
48
  destroy_op_array(Z_PTR_P(pDest));
1179
48
}