Skip to content

Commit 7be62b7

Browse files
committed
Implement frameless static calls
1 parent 17187c4 commit 7be62b7

File tree

7 files changed

+102
-8
lines changed

7 files changed

+102
-8
lines changed

Zend/Optimizer/zend_dump.c

Lines changed: 4 additions & 1 deletion
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

Lines changed: 24 additions & 0 deletions
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;
@@ -5329,6 +5336,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53295336
}
53305337
}
53315338

5339+
uint32_t init_opnum = get_next_op_number();
53325340
opline = get_next_op();
53335341
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
53345342

@@ -5371,6 +5379,22 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53715379
}
53725380
}
53735381

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

build/gen_stub.php

Lines changed: 14 additions & 6 deletions
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) {
@@ -5175,7 +5183,7 @@ function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo):
51755183
function generateCodeWithConditions(
51765184
iterable $infos, string $separator, Closure $codeGenerator, ?string $parentCond = null): string {
51775185
$code = "";
5178-
5186+
51795187
// For combining the conditional blocks of the infos with the same condition
51805188
$openCondition = null;
51815189
foreach ($infos as $info) {

ext/zend_test/test.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,30 @@ static ZEND_METHOD(_ZendTestClass, takesUnionType)
11621162
RETURN_NULL();
11631163
}
11641164

1165+
static ZEND_METHOD(_ZendTestClass, framelessStaticMethod)
1166+
{
1167+
zend_long lhs, rhs;
1168+
1169+
ZEND_PARSE_PARAMETERS_START(2, 2)
1170+
Z_PARAM_LONG(lhs)
1171+
Z_PARAM_LONG(rhs)
1172+
ZEND_PARSE_PARAMETERS_END();
1173+
1174+
RETURN_LONG(lhs + rhs);
1175+
}
1176+
1177+
ZEND_FRAMELESS_FUNCTION(_ZendTestClass_framelessStaticMethod, 2)
1178+
{
1179+
zend_long lhs, rhs;
1180+
1181+
Z_FLF_PARAM_LONG(1, lhs);
1182+
Z_FLF_PARAM_LONG(2, rhs);
1183+
1184+
ZVAL_LONG(return_value, lhs + rhs);
1185+
1186+
flf_clean:;
1187+
}
1188+
11651189
// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`.
11661190
//
11671191
// We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM.

ext/zend_test/test.stub.php

Lines changed: 3 additions & 0 deletions
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

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 0 deletions
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)