Skip to content

Commit ac8497c

Browse files
committed
test trampoline functions to ensure stability
1 parent 04ccbd1 commit ac8497c

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
/*
3+
* Copyright 2025 New Relic Corporation. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*DESCRIPTION
8+
Test that trampoline functions not blow up.
9+
*/
10+
11+
/*INI
12+
newrelic.code_level_metrics.enabled = true
13+
*/
14+
15+
/*EXPECT_TRACED_ERRORS null*/
16+
17+
/*EXPECT
18+
bool(false)
19+
*/
20+
21+
class B {}
22+
class A extends B
23+
{
24+
public function bar($func)
25+
{
26+
var_dump(is_callable(array('B', 'foo')));
27+
}
28+
29+
public function __call($func, $args) {}
30+
}
31+
32+
class X
33+
{
34+
public static function __callStatic($func, $args) {}
35+
}
36+
37+
$a = new A();
38+
// Extra X::foo() wrapper to force use of allocated trampoline.
39+
X::foo($a->bar('foo'));
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/*
3+
* Copyright 2025 New Relic Corporation. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*DESCRIPTION
8+
Test that trampoline functions not blow up.
9+
*/
10+
11+
/*INI
12+
newrelic.code_level_metrics.enabled = true
13+
*/
14+
15+
/*EXPECT_METRICS_EXIST
16+
Custom/trampoline_factorial, 42
17+
*/
18+
19+
/*EXPECT_TRACED_ERRORS null*/
20+
21+
/*EXPECT
22+
ok - trampoline('trampoline_factorial', 42)
23+
*/
24+
25+
require_once __DIR__ . '/../../../include/tap.php';
26+
27+
newrelic_add_custom_tracer('trampoline_factorial');
28+
29+
function trampoline_factorial($n, $acc = 1)
30+
{
31+
if ($n == 1) {
32+
return $acc;
33+
}
34+
return function () use ($n, $acc) {
35+
return trampoline_factorial($n - 1, $n * $acc);
36+
};
37+
}
38+
39+
function trampoline(callable $c, ...$args)
40+
{
41+
while (is_callable($c)) {
42+
$c = $c(...$args);
43+
}
44+
return $c;
45+
}
46+
47+
tap_equal(1.4050061177528801E+51, trampoline('trampoline_factorial', 42), "trampoline('trampoline_factorial', 42)");
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
/*
3+
* Copyright 2025 New Relic Corporation. All rights reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*DESCRIPTION
8+
Test that trampoline functions not blow up.
9+
*/
10+
11+
/*INI
12+
newrelic.code_level_metrics.enabled = true
13+
*/
14+
15+
/*EXPECT_METRICS_EXIST
16+
Custom/class@anonymous::execute, 2
17+
*/
18+
19+
/*EXPECT_SPAN_EVENTS_LIKE
20+
[
21+
[
22+
{
23+
"category": "generic",
24+
"type": "Span",
25+
"guid": "??",
26+
"traceId": "??",
27+
"transactionId": "??",
28+
"name": "Custom\/class@anonymous::execute",
29+
"timestamp": "??",
30+
"duration": "??",
31+
"priority": "??",
32+
"sampled": true,
33+
"parentId": "??"
34+
},
35+
{},
36+
{
37+
"code.lineno": "??",
38+
"code.namespace": "class@anonymous",
39+
"code.filepath": "__FILE__",
40+
"code.function": "execute"
41+
}
42+
],
43+
[
44+
{
45+
"category": "generic",
46+
"type": "Span",
47+
"guid": "??",
48+
"traceId": "??",
49+
"transactionId": "??",
50+
"name": "Custom\/class@anonymous::execute",
51+
"timestamp": "??",
52+
"duration": "??",
53+
"priority": "??",
54+
"sampled": true,
55+
"parentId": "??"
56+
},
57+
{},
58+
{
59+
"code.lineno": "??",
60+
"code.namespace": "class@anonymous",
61+
"code.filepath": "__FILE__",
62+
"code.function": "execute"
63+
}
64+
]
65+
]
66+
*/
67+
68+
/*EXPECT_TRACED_ERRORS null*/
69+
70+
$anon = new class() {
71+
function execute()
72+
{
73+
new ReflectionClass('Wrapper');
74+
}
75+
};
76+
77+
$anon2 = new class() {
78+
function execute()
79+
{
80+
new ReflectionClass('Wrapper');
81+
}
82+
};
83+
84+
$klass = new ReflectionClass($anon);
85+
$method = $klass->getMethod('execute');
86+
$klass_name = $klass->getName();
87+
$method_name = $method->getName();
88+
89+
newrelic_add_custom_tracer("$klass_name::$method_name");
90+
91+
(new Wrapper($anon))->execute(
92+
(new Wrapper($anon))->execute()
93+
);
94+
95+
class Wrapper
96+
{
97+
protected $wrapped;
98+
99+
function __construct($wrapped)
100+
{
101+
$this->wrapped = $wrapped;
102+
}
103+
104+
public function __call($method, $arguments)
105+
{
106+
return call_user_func([$this->wrapped, $method]);
107+
}
108+
}

0 commit comments

Comments
 (0)