Skip to content

Commit a1cbd3c

Browse files
committed
Zend/JIT: fix undefined function suggestion under opcache JIT
The JIT compiler had its own hand-written stub for raising "Call to undefined function" errors that bypassed the new Levenshtein suggestion logic, since it duplicated the error message instead of reusing the interpreter's helper. Extract the shared logic into zend_undefined_function_error() and call it from both the VM helper and the JIT stub.
1 parent 11e41df commit a1cbd3c

5 files changed

Lines changed: 20 additions & 35 deletions

File tree

Zend/zend_execute.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2607,6 +2607,19 @@ ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(co
26072607
}
26082608
}
26092609

2610+
/* function_name and function_name[1] (the lowercased key) are adjacent RT_CONSTANT literals;
2611+
* for INIT_NS_FCALL_BY_NAME with a namespace prefix, function_name[2] is the global fallback */
2612+
ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_function_error(zval *function_name)
2613+
{
2614+
zend_string *lc_key = Z_STR_P(function_name + 1);
2615+
zend_string *suggestion = zend_find_similar_function(ZSTR_VAL(lc_key), ZSTR_LEN(lc_key));
2616+
if (suggestion) {
2617+
zend_throw_error(NULL, "Call to undefined function %s() (did you mean %s()?)", Z_STRVAL_P(function_name), ZSTR_VAL(suggestion));
2618+
} else {
2619+
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
2620+
}
2621+
}
2622+
26102623
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_invalid_method_call(const zval *object, const zval *function_name)
26112624
{
26122625
zend_throw_error(NULL, "Call to a member function %s() on %s",

Zend/zend_execute.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_
513513
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
514514
ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type);
515515
ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method);
516+
ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_function_error(zval *function_name);
516517
ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc);
517518

518519
ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data);

Zend/zend_vm_def.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,14 +1000,7 @@ ZEND_VM_COLD_HELPER(zend_undefined_function_helper, ANY, ANY)
10001000

10011001
SAVE_OPLINE();
10021002
function_name = RT_CONSTANT(opline, opline->op2);
1003-
/* For INIT_NS_FCALL_BY_NAME with a namespace prefix, op2+2 is the global fallback */
1004-
zend_string *lc_key = Z_STR_P(function_name + 1);
1005-
zend_string *suggestion = zend_find_similar_function(ZSTR_VAL(lc_key), ZSTR_LEN(lc_key));
1006-
if (suggestion) {
1007-
zend_throw_error(NULL, "Call to undefined function %s() (did you mean %s()?)", Z_STRVAL_P(function_name), ZSTR_VAL(suggestion));
1008-
} else {
1009-
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
1010-
}
1003+
zend_undefined_function_error(function_name);
10111004
HANDLE_EXCEPTION();
10121005
}
10131006

Zend/zend_vm_execute.h

Lines changed: 2 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/opcache/jit/zend_jit_ir.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,21 +2168,13 @@ static int zend_jit_undefined_function_stub(zend_jit_ctx *jit)
21682168
{
21692169
// JIT: load EX(opline)
21702170
ir_ref ref = ir_LOAD_A(jit_FP(jit));
2171-
ir_ref arg3 = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_op, op2.constant)));
2171+
ir_ref arg1 = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_op, op2.constant)));
21722172

21732173
if (sizeof(void*) == 8) {
2174-
arg3 = ir_LOAD_A(ir_ADD_A(ref, ir_SEXT_A(arg3)));
2175-
} else {
2176-
arg3 = ir_LOAD_A(arg3);
2174+
arg1 = ir_ADD_A(ref, ir_SEXT_A(arg1));
21772175
}
2178-
arg3 = ir_ADD_OFFSET(arg3, offsetof(zend_string, val));
21792176

2180-
ir_CALL_3(IR_VOID,
2181-
ir_CONST_FUNC_PROTO(zend_throw_error,
2182-
ir_proto_2(&jit->ctx, IR_VARARG_FUNC, IR_VOID, IR_ADDR, IR_ADDR)),
2183-
IR_NULL,
2184-
ir_CONST_ADDR("Call to undefined function %s()"),
2185-
arg3);
2177+
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_undefined_function_error), arg1);
21862178

21872179
ir_IJMP(jit_STUB_ADDR(jit, jit_stub_exception_handler));
21882180

0 commit comments

Comments
 (0)