Skip to content

Commit a612954

Browse files
committed
Delay variable bindings
1 parent 84fc2df commit a612954

File tree

5 files changed

+168
-10
lines changed

5 files changed

+168
-10
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Delayed binding
3+
--FILE--
4+
<?php
5+
6+
class Pair {
7+
public function __construct(public $a, public $b) {}
8+
}
9+
10+
var_dump(new Pair(1, 2) is Pair { a: $a, b: $b });
11+
var_dump($a, $b);
12+
unset($a, $b);
13+
14+
var_dump(new Pair(1, 2) is Pair { a: $a, b: 3 });
15+
var_dump($a, $b);
16+
unset($a, $b);
17+
18+
var_dump(new Pair(new \stdClass(), 2) is Pair { a: $a, b: 2 });
19+
var_dump($a, $b);
20+
unset($a, $b);
21+
22+
var_dump(new Pair(new \stdClass(), 2) is Pair { a: $a, b: 3 });
23+
var_dump($a, $b);
24+
unset($a, $b);
25+
26+
?>
27+
--EXPECTF--
28+
bool(true)
29+
int(1)
30+
int(2)
31+
bool(false)
32+
33+
Warning: Undefined variable $a in %s on line %d
34+
35+
Warning: Undefined variable $b in %s on line %d
36+
NULL
37+
NULL
38+
bool(true)
39+
40+
Warning: Undefined variable $b in %s on line %d
41+
object(stdClass)#2 (0) {
42+
}
43+
NULL
44+
bool(false)
45+
46+
Warning: Undefined variable $a in %s on line %d
47+
48+
Warning: Undefined variable $b in %s on line %d
49+
NULL
50+
NULL

Zend/zend_execute_API.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "zend_inheritance.h"
3939
#include "zend_observer.h"
4040
#include "zend_call_stack.h"
41+
#include "zend_pattern_matching.h"
4142
#ifdef HAVE_SYS_TIME_H
4243
#include <sys/time.h>
4344
#endif
@@ -203,6 +204,8 @@ void init_executor(void) /* {{{ */
203204
zend_fiber_init();
204205
zend_weakrefs_init();
205206

207+
zend_pm_contexts_free();
208+
206209
EG(active) = 1;
207210
}
208211
/* }}} */

Zend/zend_globals.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "zend_arena.h"
4242
#include "zend_call_stack.h"
4343
#include "zend_max_execution_timer.h"
44+
#include "zend_pattern_matching.h"
4445

4546
/* Define ZTS if you want a thread-safe Zend */
4647
/*#undef ZTS*/
@@ -304,6 +305,9 @@ struct _zend_executor_globals {
304305
struct sigaction oldact;
305306
#endif
306307

308+
zend_pm_context *pm_context;
309+
zend_pm_context pm_context_spare;
310+
307311
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
308312
};
309313

Zend/zend_pattern_matching.c

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,22 @@ static pm_result match_binding(zval *zv, zend_ast *pattern)
170170
return PM_MISMATCH;
171171
}
172172

173-
// FIXME: Delay to the end of pattern matching
174-
zend_execute_data *execute_data = EG(current_execute_data);
175-
uint32_t var = (uint32_t) pattern->attr;
176-
zval *cv = EX_VAR(var);
177-
zend_assign_to_variable(cv, zv, IS_CV, EX_USES_STRICT_TYPES());
178-
/* Destructor might throw */
179-
if (EG(exception)) {
180-
return PM_ERROR;
173+
zend_pm_context *context = EG(pm_context);
174+
zend_pm_bindings *bindings = context->bindings;
175+
if (!bindings) {
176+
bindings = &context->bindings_spare;
177+
context->bindings = bindings;
178+
} else if (bindings->num_used == ZEND_PM_BINDINGS_SLOTS) {
179+
zend_pm_bindings *new_bindings = emalloc(sizeof(zend_pm_bindings));
180+
new_bindings->next = bindings;
181+
bindings = new_bindings;
182+
context->bindings = bindings;
181183
}
184+
185+
zend_pm_binding *binding = &bindings->list[bindings->num_used++];
186+
binding->var = (uint32_t) pattern->attr;
187+
ZVAL_COPY(&binding->value, zv);
188+
182189
return PM_MATCH;
183190
}
184191

@@ -246,7 +253,6 @@ pm_result match_class_const(zval *zv, zend_ast *pattern)
246253
pm_result zend_pattern_match_ex(zval *zv, zend_ast *pattern)
247254
{
248255
ZVAL_DEREF(zv);
249-
// FIXME: Do we need DEINDIRECT too?
250256

251257
switch (pattern->kind) {
252258
case ZEND_AST_TYPE_PATTERN:
@@ -283,7 +289,80 @@ pm_result zend_pattern_match_ex(zval *zv, zend_ast *pattern)
283289
}
284290
}
285291

292+
static void create_context(void)
293+
{
294+
if (!EG(pm_context)) {
295+
EG(pm_context) = &EG(pm_context_spare);
296+
} else {
297+
zend_pm_context *prev = EG(pm_context);
298+
EG(pm_context) = emalloc(sizeof(zend_pm_context));
299+
EG(pm_context)->prev = prev;
300+
}
301+
}
302+
303+
static void pm_context_free(bool free_values)
304+
{
305+
zend_pm_context *context = EG(pm_context);
306+
zend_pm_bindings *bindings = context->bindings;
307+
308+
EG(pm_context) = context->prev;
309+
310+
while (bindings) {
311+
if (free_values) {
312+
for (uint8_t i = 0; i < bindings->num_used; i++) {
313+
zval_ptr_dtor(&bindings->list[i].value);
314+
}
315+
}
316+
zend_pm_bindings *next = bindings->next;
317+
if (bindings != &context->bindings_spare) {
318+
efree(bindings);
319+
}
320+
bindings = bindings->next;
321+
bindings = next;
322+
}
323+
324+
context->bindings = NULL;
325+
memset(&context->bindings_spare, 0, sizeof(context->bindings_spare));
326+
327+
if (context != &EG(pm_context_spare)) {
328+
efree(context);
329+
} else {
330+
memset(context, 0, sizeof(zend_pm_context));
331+
}
332+
}
333+
334+
static void bind_variables(void)
335+
{
336+
zend_pm_context *context = EG(pm_context);
337+
zend_pm_bindings *bindings = context->bindings;
338+
zend_execute_data *execute_data = EG(current_execute_data);
339+
while (bindings) {
340+
for (uint32_t i = 0; i < bindings->num_used; i++) {
341+
zend_pm_binding *binding = &bindings->list[i];
342+
zend_assign_to_variable(EX_VAR(binding->var), &binding->value, IS_CV, EX_USES_STRICT_TYPES());
343+
}
344+
bindings = bindings->next;
345+
}
346+
}
347+
348+
void zend_pm_contexts_free(void)
349+
{
350+
while (EG(pm_context)) {
351+
pm_context_free(false);
352+
}
353+
}
354+
286355
bool zend_pattern_match(zval *zv, zend_ast *pattern)
287356
{
288-
return zend_pattern_match_ex(zv, pattern) == PM_MATCH;
357+
// FIXME: Only create context when necessary
358+
create_context();
359+
pm_result result = zend_pattern_match_ex(zv, pattern);
360+
if (result == PM_MATCH) {
361+
bind_variables();
362+
pm_context_free(true);
363+
return true;
364+
} else {
365+
pm_context_free(true);
366+
return false;
367+
}
289368
}

Zend/zend_pattern_matching.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@
1919

2020
#include "zend.h"
2121

22+
#define ZEND_PM_BINDINGS_SLOTS 8
23+
24+
typedef struct {
25+
uint32_t var;
26+
zval value;
27+
} zend_pm_binding;
28+
29+
typedef struct _zend_pm_bindings zend_pm_bindings;
30+
struct _zend_pm_bindings {
31+
zend_pm_binding list[ZEND_PM_BINDINGS_SLOTS];
32+
zend_pm_bindings *next;
33+
uint8_t num_used;
34+
};
35+
36+
typedef struct _zend_pm_context zend_pm_context;
37+
typedef struct _zend_pm_context {
38+
zend_pm_bindings *bindings;
39+
zend_pm_bindings bindings_spare;
40+
zend_pm_context *prev;
41+
} zend_pm_context;
42+
2243
bool zend_pattern_match(zval *zv, zend_ast *pattern);
44+
void zend_pm_contexts_free(void);
2345

2446
#endif

0 commit comments

Comments
 (0)