Skip to content

Commit 9a64646

Browse files
committed
Implement frameless static calls
1 parent 99f72fa commit 9a64646

File tree

7 files changed

+101
-7
lines changed

7 files changed

+101
-7
lines changed

Zend/Optimizer/zend_dump.c

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

480480
if (ZEND_OP_IS_FRAMELESS_ICALL(opline->opcode)) {
481481
zend_function *func = ZEND_FLF_FUNC(opline);
482-
fprintf(stderr, "(%s)", ZSTR_VAL(func->common.function_name));
482+
fprintf(stderr, "(%s%s%s)",
483+
func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
484+
func->common.scope ? "::" : "",
485+
ZSTR_VAL(func->common.function_name));
483486
}
484487

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

Zend/zend_compile.c

+24
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;
@@ -5336,6 +5343,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53365343
}
53375344
}
53385345

5346+
uint32_t init_opnum = get_next_op_number();
53395347
opline = get_next_op();
53405348
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
53415349

@@ -5378,6 +5386,22 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
53785386
}
53795387
}
53805388

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

build/gen_stub.php

+13-5
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,10 @@ public function getDeclarationClassName(): string {
10601060
return implode('_', $this->className->getParts());
10611061
}
10621062

1063+
public function getDeclarationName(): string {
1064+
return "{$this->getDeclarationClassName()}_{$this->methodName}";
1065+
}
1066+
10631067
public function getDeclaration(): string {
10641068
return "ZEND_METHOD({$this->getDeclarationClassName()}, $this->methodName);\n";
10651069
}
@@ -1068,6 +1072,10 @@ public function getArgInfoName(): string {
10681072
return "arginfo_class_{$this->getDeclarationClassName()}_{$this->methodName}";
10691073
}
10701074

1075+
public function getFramelessFunctionInfosName(): string {
1076+
return "frameless_function_infos_{$this->className}_{$this->methodName}";
1077+
}
1078+
10711079
public function getMethodSynopsisFilename(): string
10721080
{
10731081
$parts = [...$this->className->getParts(), ltrim($this->methodName, '_')];
@@ -1331,12 +1339,12 @@ public function getFramelessDeclaration(): ?string {
13311339
}
13321340

13331341
foreach ($this->framelessFunctionInfos as $framelessFunctionInfo) {
1334-
$code .= "ZEND_FRAMELESS_FUNCTION({$this->name->getFunctionName()}, {$framelessFunctionInfo->arity});\n";
1342+
$code .= "ZEND_FRAMELESS_FUNCTION({$this->name->getDeclarationName()}, {$framelessFunctionInfo->arity});\n";
13351343
}
13361344

13371345
$code .= 'static const zend_frameless_function_info ' . $this->getFramelessFunctionInfosName() . "[] = {\n";
13381346
foreach ($this->framelessFunctionInfos as $framelessFunctionInfo) {
1339-
$code .= "\t{ ZEND_FRAMELESS_FUNCTION_NAME({$this->name->getFunctionName()}, {$framelessFunctionInfo->arity}), {$framelessFunctionInfo->arity} },\n";
1347+
$code .= "\t{ ZEND_FRAMELESS_FUNCTION_NAME({$this->name->getDeclarationName()}, {$framelessFunctionInfo->arity}), {$framelessFunctionInfo->arity} },\n";
13401348
}
13411349
$code .= "\t{ 0 },\n";
13421350
$code .= "};\n";
@@ -1362,10 +1370,10 @@ public function getFunctionEntry(): string {
13621370
$functionEntryCode = null;
13631371

13641372
if (!empty($this->framelessFunctionInfos)) {
1365-
if ($this->isMethod()) {
1366-
throw new Exception('Frameless methods are not supported yet');
1373+
if ($this->isMethod() && !($this->flags & Modifiers::STATIC)) {
1374+
throw new Exception('Frameless methods must be static');
13671375
}
1368-
if ($this->name->getNamespace()) {
1376+
if (!$this->isMethod() && $this->name->getNamespace()) {
13691377
throw new Exception('Namespaced direct calls to frameless functions are not supported yet');
13701378
}
13711379
if ($this->alias) {

ext/zend_test/test.c

+24
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,30 @@ static ZEND_METHOD(_ZendTestClass, takesUnionType)
11691169
RETURN_NULL();
11701170
}
11711171

1172+
static ZEND_METHOD(_ZendTestClass, framelessStaticMethod)
1173+
{
1174+
zend_long lhs, rhs;
1175+
1176+
ZEND_PARSE_PARAMETERS_START(2, 2)
1177+
Z_PARAM_LONG(lhs)
1178+
Z_PARAM_LONG(rhs)
1179+
ZEND_PARSE_PARAMETERS_END();
1180+
1181+
RETURN_LONG(lhs + rhs);
1182+
}
1183+
1184+
ZEND_FRAMELESS_FUNCTION(_ZendTestClass_framelessStaticMethod, 2)
1185+
{
1186+
zend_long lhs, rhs;
1187+
1188+
Z_FLF_PARAM_LONG(1, lhs);
1189+
Z_FLF_PARAM_LONG(2, rhs);
1190+
1191+
ZVAL_LONG(return_value, lhs + rhs);
1192+
1193+
flf_clean:;
1194+
}
1195+
11721196
// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`.
11731197
//
11741198
// 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
@@ -69,6 +69,9 @@ public function returnsThrowable(): Throwable {}
6969
static public function variadicTest(string|Iterator ...$elements) : static {}
7070

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

7477
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)