Skip to content

Commit afa4e02

Browse files
committed
Implement frameless static calls
1 parent 1a2b370 commit afa4e02

File tree

7 files changed

+101
-8
lines changed

7 files changed

+101
-8
lines changed

Zend/Optimizer/zend_dump.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,10 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
465465

466466
if (ZEND_OP_IS_FRAMELESS_ICALL(opline->opcode)) {
467467
zend_function *func = ZEND_FLF_FUNC(opline);
468-
fprintf(stderr, "(%s)", ZSTR_VAL(func->common.function_name));
468+
fprintf(stderr, "(%s%s%s)",
469+
func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
470+
func->common.scope ? "::" : "",
471+
ZSTR_VAL(func->common.function_name));
469472
}
470473

471474
if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {

Zend/zend_compile.c

+23
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@
5959

6060
#define FC(member) (CG(file_context).member)
6161

62+
#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
63+
#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
64+
#define literal_dtor(zv) do { \
65+
zval_ptr_dtor_nogc(zv); \
66+
ZVAL_NULL(zv); \
67+
} while (0)
68+
6269
typedef struct _zend_loop_var {
6370
uint8_t opcode;
6471
uint8_t var_type;
@@ -5333,6 +5340,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53335340
}
53345341
}
53355342

5343+
uint32_t init_opnum = get_next_op_number();
53365344
opline = get_next_op();
53375345
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
53385346

@@ -5375,6 +5383,21 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53755383
}
53765384
}
53775385

5386+
if (!(CG(compiler_options) & ZEND_COMPILE_NO_BUILTINS)
5387+
&& (fbc->type == ZEND_INTERNAL_FUNCTION)
5388+
&& zend_ast_is_list(args_ast)
5389+
&& !zend_args_contain_unpack_or_named(zend_ast_get_list(args_ast))) {
5390+
if (zend_compile_frameless_icall(result, zend_ast_get_list(args_ast), fbc, type) != (uint32_t)-1) {
5391+
/* Update opline in case it got invalidated. */
5392+
zend_op_array *op_array = CG(active_op_array);
5393+
opline = &op_array->opcodes[init_opnum];
5394+
literal_dtor(&ZEND_OP1_LITERAL(opline));
5395+
literal_dtor(&ZEND_OP2_LITERAL(opline));
5396+
MAKE_NOP(opline);
5397+
return;
5398+
}
5399+
}
5400+
53785401
zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast));
53795402
}
53805403
/* }}} */

build/gen_stub.php

+14-6
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,10 @@ public function getDeclarationClassName(): string {
11181118
return implode('_', $this->className->getParts());
11191119
}
11201120

1121+
public function getDeclarationName(): string {
1122+
return "{$this->getDeclarationClassName()}_{$this->methodName}";
1123+
}
1124+
11211125
public function getDeclaration(): string {
11221126
return "ZEND_METHOD({$this->getDeclarationClassName()}, $this->methodName);\n";
11231127
}
@@ -1126,6 +1130,10 @@ public function getArgInfoName(): string {
11261130
return "arginfo_class_{$this->getDeclarationClassName()}_{$this->methodName}";
11271131
}
11281132

1133+
public function getFramelessFunctionInfosName(): string {
1134+
return "frameless_function_infos_{$this->className}_{$this->methodName}";
1135+
}
1136+
11291137
public function getMethodSynopsisFilename(): string
11301138
{
11311139
$parts = [...$this->className->getParts(), ltrim($this->methodName, '_')];
@@ -1396,12 +1404,12 @@ public function getFramelessDeclaration(FuncInfo $funcInfo): ?string {
13961404
}
13971405

13981406
foreach ($this->framelessFunctionInfos as $framelessFunctionInfo) {
1399-
$code .= "ZEND_FRAMELESS_FUNCTION({$this->name->getFunctionName()}, {$framelessFunctionInfo->arity});\n";
1407+
$code .= "ZEND_FRAMELESS_FUNCTION({$this->name->getDeclarationName()}, {$framelessFunctionInfo->arity});\n";
14001408
}
14011409

14021410
$code .= 'static const zend_frameless_function_info ' . $this->getFramelessFunctionInfosName() . "[] = {\n";
14031411
foreach ($this->framelessFunctionInfos as $framelessFunctionInfo) {
1404-
$code .= "\t{ ZEND_FRAMELESS_FUNCTION_NAME({$this->name->getFunctionName()}, {$framelessFunctionInfo->arity}), {$framelessFunctionInfo->arity} },\n";
1412+
$code .= "\t{ ZEND_FRAMELESS_FUNCTION_NAME({$this->name->getDeclarationName()}, {$framelessFunctionInfo->arity}), {$framelessFunctionInfo->arity} },\n";
14051413
}
14061414
$code .= "\t{ 0 },\n";
14071415
$code .= "};\n";
@@ -1427,10 +1435,10 @@ public function getFunctionEntry(): string {
14271435
$functionEntryCode = null;
14281436

14291437
if (!empty($this->framelessFunctionInfos)) {
1430-
if ($this->isMethod()) {
1431-
throw new Exception('Frameless methods are not supported yet');
1438+
if ($this->isMethod() && !($this->flags & Modifiers::STATIC)) {
1439+
throw new Exception('Frameless methods must be static');
14321440
}
1433-
if ($this->name->getNamespace()) {
1441+
if (!$this->isMethod() && $this->name->getNamespace()) {
14341442
throw new Exception('Namespaced direct calls to frameless functions are not supported yet');
14351443
}
14361444
if ($this->alias) {
@@ -5161,7 +5169,7 @@ function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo):
51615169
function generateCodeWithConditions(
51625170
iterable $infos, string $separator, Closure $codeGenerator, ?string $parentCond = null): string {
51635171
$code = "";
5164-
5172+
51655173
// For combining the conditional blocks of the infos with the same condition
51665174
$openCondition = null;
51675175
foreach ($infos as $info) {

ext/zend_test/test.c

+24
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,30 @@ static ZEND_METHOD(_ZendTestClass, takesUnionType)
11541154
RETURN_NULL();
11551155
}
11561156

1157+
static ZEND_METHOD(_ZendTestClass, framelessStaticMethod)
1158+
{
1159+
zend_long lhs, rhs;
1160+
1161+
ZEND_PARSE_PARAMETERS_START(2, 2)
1162+
Z_PARAM_LONG(lhs)
1163+
Z_PARAM_LONG(rhs)
1164+
ZEND_PARSE_PARAMETERS_END();
1165+
1166+
RETURN_LONG(lhs + rhs);
1167+
}
1168+
1169+
ZEND_FRAMELESS_FUNCTION(_ZendTestClass_framelessStaticMethod, 2)
1170+
{
1171+
zend_long lhs, rhs;
1172+
1173+
Z_FLF_PARAM_LONG(1, lhs);
1174+
Z_FLF_PARAM_LONG(2, rhs);
1175+
1176+
ZVAL_LONG(return_value, lhs + rhs);
1177+
1178+
flf_clean:;
1179+
}
1180+
11571181
// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`.
11581182
//
11591183
// We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM.

ext/zend_test/test.stub.php

+3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public function returnsThrowable(): Throwable {}
6868
static public function variadicTest(string|Iterator ...$elements) : static {}
6969

7070
public function takesUnionType(stdclass|Iterator $arg): void {}
71+
72+
/** @frameless-function {"arity": 2} */
73+
public static function framelessStaticMethod(int $lhs, int $rhs): int {}
7174
}
7275

7376
class _ZendTestMagicCall

ext/zend_test/test_arginfo.h

+21-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Frameless static methods
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
var_dump(_ZendTestClass::framelessStaticMethod(42, 69));
9+
10+
?>
11+
--EXPECT--
12+
int(111)

0 commit comments

Comments
 (0)