Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Implement frameless static calls #16471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
iluuu1994 wants to merge 2 commits into php:master
base: master
Choose a base branch
Loading
from iluuu1994:frameless-static-calls
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Zend/Optimizer/zend_dump.c
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,10 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block

if (ZEND_OP_IS_FRAMELESS_ICALL(opline->opcode)) {
zend_function *func = ZEND_FLF_FUNC(opline);
fprintf(stderr, "(%s)", ZSTR_VAL(func->common.function_name));
fprintf(stderr, "(%s%s%s)",
func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
func->common.scope ? "::" : "",
ZSTR_VAL(func->common.function_name));
}

if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
Expand Down
62 changes: 39 additions & 23 deletions Zend/zend_compile.c
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -5336,28 +5336,11 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
}
}

opline = get_next_op();
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;

zend_set_class_name_op1(opline, &class_node);

if (method_node.op_type == IS_CONST) {
opline->op2_type = IS_CONST;
opline->op2.constant = zend_add_func_name_literal(
Z_STR(method_node.u.constant));
opline->result.num = zend_alloc_cache_slots(2);
} else {
if (opline->op1_type == IS_CONST) {
opline->result.num = zend_alloc_cache_slot();
}
SET_NODE(opline->op2, &method_node);
}

/* Check if we already know which method we're calling */
if (opline->op2_type == IS_CONST) {
if (method_node.op_type == IS_CONST) {
zend_class_entry *ce = NULL;
if (opline->op1_type == IS_CONST) {
zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1);
if (class_node.op_type == IS_CONST) {
zend_string *lcname = zend_string_tolower(Z_STR(class_node.u.constant));
ce = zend_hash_find_ptr(CG(class_table), lcname);
if (ce) {
if (zend_compile_ignore_class(ce, CG(active_op_array)->filename)) {
Expand All @@ -5367,15 +5350,48 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
&& zend_string_equals_ci(CG(active_class_entry)->name, lcname)) {
ce = CG(active_class_entry);
}
} else if (opline->op1_type == IS_UNUSED
&& (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
zend_string_release(lcname);
} else if (class_node.op_type == IS_UNUSED
&& (class_node.u.op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
&& zend_is_scope_known()) {
ce = CG(active_class_entry);
}
if (ce) {
zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
zend_string *lcname = zend_string_tolower(Z_STR(method_node.u.constant));
fbc = zend_get_compatible_func_or_null(ce, lcname);
zend_string_release(lcname);

if (fbc
&& !(CG(compiler_options) & ZEND_COMPILE_NO_BUILTINS)
&& (fbc->type == ZEND_INTERNAL_FUNCTION)
&& zend_ast_is_list(args_ast)
&& !zend_args_contain_unpack_or_named(zend_ast_get_list(args_ast))) {
if (zend_compile_frameless_icall(result, zend_ast_get_list(args_ast), fbc, type) != (uint32_t)-1) {
zval_ptr_dtor(&method_node.u.constant);
if (class_node.op_type == IS_CONST) {
zval_ptr_dtor(&class_node.u.constant);
}
return;
}
}
}
}

opline = get_next_op();
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;

zend_set_class_name_op1(opline, &class_node);

if (method_node.op_type == IS_CONST) {
opline->op2_type = IS_CONST;
opline->op2.constant = zend_add_func_name_literal(
Z_STR(method_node.u.constant));
opline->result.num = zend_alloc_cache_slots(2);
} else {
if (opline->op1_type == IS_CONST) {
opline->result.num = zend_alloc_cache_slot();
}
SET_NODE(opline->op2, &method_node);
}

zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast));
Expand Down
18 changes: 13 additions & 5 deletions build/gen_stub.php
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,10 @@ public function getDeclarationClassName(): string {
return implode('_', $this->className->getParts());
}

public function getDeclarationName(): string {
return "{$this->getDeclarationClassName()}_{$this->methodName}";
}

public function getDeclaration(): string {
return "ZEND_METHOD({$this->getDeclarationClassName()}, $this->methodName);\n";
}
Expand All @@ -1068,6 +1072,10 @@ public function getArgInfoName(): string {
return "arginfo_class_{$this->getDeclarationClassName()}_{$this->methodName}";
}

public function getFramelessFunctionInfosName(): string {
return "frameless_function_infos_{$this->className}_{$this->methodName}";
}

public function getMethodSynopsisFilename(): string
{
$parts = [...$this->className->getParts(), ltrim($this->methodName, '_')];
Expand Down Expand Up @@ -1331,12 +1339,12 @@ public function getFramelessDeclaration(): ?string {
}

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

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

if (!empty($this->framelessFunctionInfos)) {
if ($this->isMethod()) {
throw new Exception('Frameless methods are not supported yet');
if ($this->isMethod() && !($this->flags & Modifiers::STATIC)) {
throw new Exception('Frameless methods must be static');
}
if ($this->name->getNamespace()) {
if (!$this->isMethod() && $this->name->getNamespace()) {
throw new Exception('Namespaced direct calls to frameless functions are not supported yet');
}
if ($this->alias) {
Expand Down
24 changes: 24 additions & 0 deletions ext/zend_test/test.c
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,30 @@ static ZEND_METHOD(_ZendTestClass, takesUnionType)
RETURN_NULL();
}

static ZEND_METHOD(_ZendTestClass, framelessStaticMethod)
{
zend_long lhs, rhs;

ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_LONG(lhs)
Z_PARAM_LONG(rhs)
ZEND_PARSE_PARAMETERS_END();

RETURN_LONG(lhs + rhs);
}

ZEND_FRAMELESS_FUNCTION(_ZendTestClass_framelessStaticMethod, 2)
{
zend_long lhs, rhs;

Z_FLF_PARAM_LONG(1, lhs);
Z_FLF_PARAM_LONG(2, rhs);

ZVAL_LONG(return_value, lhs + rhs);

flf_clean:;
}

// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`.
//
// We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM.
Expand Down
3 changes: 3 additions & 0 deletions ext/zend_test/test.stub.php
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ public function returnsThrowable(): Throwable {}
static public function variadicTest(string|Iterator ...$elements) : static {}

public function takesUnionType(stdclass|Iterator $arg): void {}

/** @frameless-function {"arity": 2} */
public static function framelessStaticMethod(int $lhs, int $rhs): int {}
}

class _ZendTestMagicCall
Expand Down
22 changes: 21 additions & 1 deletion ext/zend_test/test_arginfo.h
View file Open in desktop

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions ext/zend_test/tests/frameless_static_methods.phpt
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Frameless static methods
--EXTENSIONS--
zend_test
--FILE--
<?php

var_dump(_ZendTestClass::framelessStaticMethod(42, 69));

?>
--EXPECT--
int(111)
Loading

AltStyle によって変換されたページ (->オリジナル) /