Skip to content

Commit 905fddc

Browse files
committed
is_literal
1 parent 7bc0dd2 commit 905fddc

17 files changed

+944
-542
lines changed

Zend/zend_compile.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3819,7 +3819,21 @@ static zend_result zend_compile_func_is_scalar(znode *result, zend_ast_list *arg
38193819
opline = zend_emit_op_tmp(result, ZEND_TYPE_CHECK, &arg_node, NULL);
38203820
opline->extended_value = (1 << IS_FALSE | 1 << IS_TRUE | 1 << IS_DOUBLE | 1 << IS_LONG | 1 << IS_STRING);
38213821
return SUCCESS;
3822-
}
3822+
} /* }}} */
3823+
3824+
static zend_result zend_compile_func_is_literal(znode *result, zend_ast_list *args) /* {{{ */
3825+
{
3826+
znode arg_node;
3827+
3828+
if (args->children != 1) {
3829+
return FAILURE;
3830+
}
3831+
3832+
zend_compile_expr(&arg_node, args->child[0]);
3833+
zend_emit_op(result, ZEND_LITERAL_CHECK, &arg_node, NULL);
3834+
3835+
return SUCCESS;
3836+
} /* }}} */
38233837

38243838
zend_result zend_compile_func_cast(znode *result, zend_ast_list *args, uint32_t type) /* {{{ */
38253839
{
@@ -4350,6 +4364,8 @@ zend_result zend_try_compile_special_func(znode *result, zend_string *lcname, ze
43504364
return zend_compile_func_typecheck(result, args, IS_RESOURCE);
43514365
} else if (zend_string_equals_literal(lcname, "is_scalar")) {
43524366
return zend_compile_func_is_scalar(result, args);
4367+
} else if (zend_string_equals_literal(lcname, "is_literal")) {
4368+
return zend_compile_func_is_literal(result, args);
43534369
} else if (zend_string_equals_literal(lcname, "boolval")) {
43544370
return zend_compile_func_cast(result, args, _IS_BOOL);
43554371
} else if (zend_string_equals_literal(lcname, "intval")) {
@@ -6996,6 +7012,7 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
69967012

69977013
unqualified_name = decl->name;
69987014
op_array->function_name = name = zend_prefix_with_ns(unqualified_name);
7015+
69997016
lcname = zend_string_tolower(name);
70007017

70017018
if (FC(imports_function)) {
@@ -8367,6 +8384,7 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
83678384
key_ast = elem_ast->child[1];
83688385
if (key_ast) {
83698386
zval *key = zend_ast_get_zval(key_ast);
8387+
83708388
switch (Z_TYPE_P(key)) {
83718389
case IS_LONG:
83728390
zend_hash_index_update(Z_ARRVAL_P(result), Z_LVAL_P(key), value);

Zend/zend_language_scanner.l

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,15 @@ ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename)
760760
RESET_DOC_COMMENT();
761761
}
762762

763+
zend_ast* zend_get_scanned_string_ast(zval *zendlval, uint32_t start_line) {
764+
if (Z_TYPE_P(zendlval) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR_P(zendlval))) {
765+
zend_string *string =
766+
Z_STR_P(zendlval);
767+
768+
GC_TYPE_INFO(string) |= IS_STR_LITERAL;
769+
}
770+
return zend_ast_create_zval_with_lineno(zendlval, start_line);
771+
}
763772

764773
ZEND_API size_t zend_get_scanned_file_offset(void)
765774
{
@@ -3081,7 +3090,7 @@ emit_token_with_str:
30813090
emit_token_with_val:
30823091
if (PARSER_MODE()) {
30833092
ZEND_ASSERT(Z_TYPE_P(zendlval) != IS_UNDEF);
3084-
elem->ast = zend_ast_create_zval_with_lineno(zendlval, start_line);
3093+
elem->ast = zend_get_scanned_string_ast(zendlval, start_line);
30853094
}
30863095
30873096
emit_token:

Zend/zend_operators.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,10 +1870,15 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
18701870
{
18711871
zval *orig_op1 = op1;
18721872
zval op1_copy, op2_copy;
1873+
bool literal = false;
18731874

18741875
ZVAL_UNDEF(&op1_copy);
18751876
ZVAL_UNDEF(&op2_copy);
18761877

1878+
if (UNEXPECTED(Z_IS_LITERAL_P(op1) && Z_IS_LITERAL_P(op2))) {
1879+
literal = true;
1880+
}
1881+
18771882
do {
18781883
if (UNEXPECTED(Z_TYPE_P(op1) != IS_STRING)) {
18791884
if (Z_ISREF_P(op1)) {
@@ -1924,13 +1929,23 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
19241929
}
19251930
ZVAL_COPY(result, op2);
19261931
}
1932+
if (literal) {
1933+
ZSTR_SET_LITERAL(&Z_STR_P(result));
1934+
} else {
1935+
ZSTR_UNSET_LITERAL(&Z_STR_P(result));
1936+
}
19271937
} else if (UNEXPECTED(Z_STRLEN_P(op2) == 0)) {
19281938
if (EXPECTED(result != op1)) {
19291939
if (result == orig_op1) {
19301940
i_zval_ptr_dtor(result);
19311941
}
19321942
ZVAL_COPY(result, op1);
19331943
}
1944+
if (literal) {
1945+
ZSTR_SET_LITERAL(&Z_STR_P(result));
1946+
} else {
1947+
ZSTR_UNSET_LITERAL(&Z_STR_P(result));
1948+
}
19341949
} else {
19351950
size_t op1_len = Z_STRLEN_P(op1);
19361951
size_t op2_len = Z_STRLEN_P(op2);
@@ -1950,12 +1965,18 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
19501965
if (result == op1 && Z_REFCOUNTED_P(result)) {
19511966
/* special case, perform operations on result */
19521967
result_str = zend_string_extend(Z_STR_P(result), result_len, 0);
1968+
if (UNEXPECTED(!literal && ZSTR_IS_LITERAL(result_str))) {
1969+
ZSTR_UNSET_LITERAL(&result_str);
1970+
}
19531971
} else {
19541972
result_str = zend_string_alloc(result_len, 0);
19551973
memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
19561974
if (result == orig_op1) {
19571975
i_zval_ptr_dtor(result);
19581976
}
1977+
if (UNEXPECTED(literal && !ZSTR_IS_LITERAL(result_str))) {
1978+
ZSTR_SET_LITERAL_FAST(result_str);
1979+
}
19591980
}
19601981

19611982
/* This has to happen first to account for the cases where result == op1 == op2 and

Zend/zend_string.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *
231231
return ret;
232232
}
233233

234+
bool literal = GC_TYPE_INFO(str) & IS_STR_LITERAL;
235+
234236
/* Create a short living interned, freed after the request. */
235237
#if ZEND_RC_DEBUG
236238
if (zend_rc_debug) {
@@ -247,6 +249,10 @@ static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *
247249
}
248250

249251
ret = zend_add_interned_string(str, &CG(interned_strings), 0);
252+
253+
if (UNEXPECTED(literal)) {
254+
GC_TYPE_INFO(ret) |= IS_STR_LITERAL;
255+
}
250256

251257
return ret;
252258
}

Zend/zend_string.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,54 @@ static zend_always_inline void zend_string_release_ex(zend_string *s, bool persi
330330
}
331331
}
332332

333+
#define ZSTR_IS_LITERAL(s) (GC_TYPE_INFO(s) & IS_STR_LITERAL)
334+
335+
static zend_always_inline zend_string* zend_string_set_literal(zend_string *s) {
336+
if (UNEXPECTED(ZSTR_IS_LITERAL(s))) {
337+
return s;
338+
}
339+
340+
if (EXPECTED(GC_REFCOUNT(s) == 1 && !ZSTR_IS_INTERNED(s))) {
341+
GC_TYPE_INFO(s) |= IS_STR_LITERAL;
342+
return s;
343+
}
344+
345+
zend_string *literal = zend_string_dup(s, 0);
346+
347+
GC_TYPE_INFO(literal) |= IS_STR_LITERAL;
348+
349+
zend_string_release(s);
350+
351+
return literal;
352+
}
353+
354+
static zend_always_inline zend_string* zend_string_unset_literal(zend_string *s) {
355+
if (UNEXPECTED(!ZSTR_IS_LITERAL(s))) {
356+
return s;
357+
}
358+
359+
if (EXPECTED(GC_REFCOUNT(s) == 1 && !ZSTR_IS_INTERNED(s))) {
360+
GC_TYPE_INFO(s) &= ~IS_STR_LITERAL;
361+
return s;
362+
}
363+
364+
zend_string *literal = zend_string_dup(s, 0);
365+
366+
zend_string_release(s);
367+
368+
return literal;
369+
}
370+
371+
static zend_always_inline void zend_string_set_literal_fast(zend_string *s) {
372+
ZEND_ASSERT(GC_REFCOUNT(s) == 1 && !ZSTR_IS_INTERNED(s));
373+
374+
GC_TYPE_INFO(s) |= IS_STR_LITERAL;
375+
}
376+
377+
#define ZSTR_SET_LITERAL(s) *(s) = zend_string_set_literal(*(s))
378+
#define ZSTR_SET_LITERAL_FAST zend_string_set_literal_fast
379+
#define ZSTR_UNSET_LITERAL(s) *(s) = zend_string_unset_literal(*(s))
380+
333381
#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__)))
334382
BEGIN_EXTERN_C()
335383
ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2);

Zend/zend_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
698698
#define IS_STR_PERSISTENT GC_PERSISTENT /* allocated using malloc */
699699
#define IS_STR_PERMANENT (1<<8) /* relives request boundary */
700700
#define IS_STR_VALID_UTF8 (1<<9) /* valid UTF-8 according to PCRE */
701+
#define IS_STR_LITERAL (1<<10)
701702

702703
/* array flags */
703704
#define IS_ARRAY_IMMUTABLE GC_IMMUTABLE
@@ -745,6 +746,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
745746
#define Z_PROTECT_RECURSION_P(zv) Z_PROTECT_RECURSION(*(zv))
746747
#define Z_UNPROTECT_RECURSION_P(zv) Z_UNPROTECT_RECURSION(*(zv))
747748

749+
#define Z_IS_LITERAL(zval) (Z_TYPE(zval) == IS_STRING && ZSTR_IS_LITERAL(Z_STR(zval)))
750+
#define Z_IS_LITERAL_P(zv) Z_IS_LITERAL(*(zv))
751+
748752
/* All data types < IS_STRING have their constructor/destructors skipped */
749753
#define Z_CONSTANT(zval) (Z_TYPE(zval) == IS_CONSTANT_AST)
750754
#define Z_CONSTANT_P(zval_p) Z_CONSTANT(*(zval_p))

Zend/zend_vm_def.h

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,19 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV, SPEC(NO_CONST_
408408
size_t len = ZSTR_LEN(op1_str);
409409

410410
str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
411+
if (ZSTR_IS_LITERAL(str) && !ZSTR_IS_LITERAL(op2_str)) {
412+
ZSTR_UNSET_LITERAL(&str);
413+
}
411414
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
412415
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
413416
if (OP2_TYPE & (IS_TMP_VAR|IS_VAR)) {
414417
zend_string_release_ex(op2_str, 0);
415418
}
416419
} else {
417420
str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0);
421+
if (ZSTR_IS_LITERAL(op1_str) && ZSTR_IS_LITERAL(op2_str)) {
422+
ZSTR_SET_LITERAL_FAST(str);
423+
}
418424
memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str));
419425
memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
420426
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
@@ -3099,7 +3105,6 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
30993105
zval *op1, *op2;
31003106
zend_string *op1_str, *op2_str, *str;
31013107

3102-
31033108
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
31043109
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
31053110
if ((OP1_TYPE == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) &&
@@ -3131,13 +3136,19 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31313136
size_t len = ZSTR_LEN(op1_str);
31323137

31333138
str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
3139+
if (ZSTR_IS_LITERAL(str) && !ZSTR_IS_LITERAL(op2_str)) {
3140+
ZSTR_UNSET_LITERAL(&str);
3141+
}
31343142
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
31353143
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
31363144
if (OP2_TYPE & (IS_TMP_VAR|IS_VAR)) {
31373145
zend_string_release_ex(op2_str, 0);
31383146
}
31393147
} else {
31403148
str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0);
3149+
if (ZSTR_IS_LITERAL(op1_str) && ZSTR_IS_LITERAL(op2_str)) {
3150+
ZSTR_SET_LITERAL_FAST(str);
3151+
}
31413152
memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str));
31423153
memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
31433154
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
@@ -3152,6 +3163,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31523163
}
31533164

31543165
SAVE_OPLINE();
3166+
31553167
if (OP1_TYPE == IS_CONST) {
31563168
op1_str = Z_STR_P(op1);
31573169
} else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
@@ -3166,12 +3178,14 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31663178
op2_str = Z_STR_P(op2);
31673179
} else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
31683180
op2_str = zend_string_copy(Z_STR_P(op2));
3181+
31693182
} else {
31703183
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) {
31713184
ZVAL_UNDEFINED_OP2();
31723185
}
31733186
op2_str = zval_get_string_func(op2);
31743187
}
3188+
31753189
do {
31763190
if (OP1_TYPE != IS_CONST) {
31773191
if (UNEXPECTED(ZSTR_LEN(op1_str) == 0)) {
@@ -3198,6 +3212,9 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31983212
}
31993213
}
32003214
str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0);
3215+
if (UNEXPECTED(ZSTR_IS_LITERAL(op1_str) && ZSTR_IS_LITERAL(op2_str))) {
3216+
ZSTR_SET_LITERAL_FAST(str);
3217+
}
32013218
memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str));
32023219
memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
32033220
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
@@ -3208,6 +3225,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
32083225
zend_string_release_ex(op2_str, 0);
32093226
}
32103227
} while (0);
3228+
32113229
FREE_OP1();
32123230
FREE_OP2();
32133231
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3286,11 +3304,12 @@ ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV, NUM)
32863304
ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM)
32873305
{
32883306
USE_OPLINE
3289-
zend_string **rope;
3290-
zval *var, *ret;
3307+
zend_string **rope, *result;
3308+
zval *var;
32913309
uint32_t i;
32923310
size_t len = 0;
32933311
char *target;
3312+
bool literal = true;
32943313

32953314
rope = (zend_string**)EX_VAR(opline->op1.var);
32963315
if (OP2_TYPE == IS_CONST) {
@@ -3326,16 +3345,22 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM)
33263345
for (i = 0; i <= opline->extended_value; i++) {
33273346
len += ZSTR_LEN(rope[i]);
33283347
}
3329-
ret = EX_VAR(opline->result.var);
3330-
ZVAL_STR(ret, zend_string_alloc(len, 0));
3331-
target = Z_STRVAL_P(ret);
3348+
3349+
result = zend_string_alloc(len, 0);
3350+
target = ZSTR_VAL(result);
33323351
for (i = 0; i <= opline->extended_value; i++) {
3352+
if (literal && !ZSTR_IS_LITERAL(rope[i])) {
3353+
literal = false;
3354+
}
33333355
memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i]));
33343356
target += ZSTR_LEN(rope[i]);
33353357
zend_string_release_ex(rope[i], 0);
33363358
}
33373359
*target = '\0';
3338-
3360+
if (literal) {
3361+
ZSTR_SET_LITERAL_FAST(result);
3362+
}
3363+
ZVAL_STR(EX_VAR(opline->result.var), result);
33393364
ZEND_VM_NEXT_OPCODE();
33403365
}
33413366

@@ -4240,6 +4265,18 @@ ZEND_VM_COLD_HANDLER(201, ZEND_VERIFY_NEVER_TYPE, UNUSED, UNUSED)
42404265
HANDLE_EXCEPTION();
42414266
}
42424267

4268+
ZEND_VM_HOT_HANDLER(202, ZEND_LITERAL_CHECK, ANY, UNUSED)
4269+
{
4270+
USE_OPLINE
4271+
4272+
zval *zv = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
4273+
4274+
ZVAL_BOOL(EX_VAR(opline->result.var), Z_IS_LITERAL_P(zv));
4275+
4276+
FREE_OP1();
4277+
ZEND_VM_NEXT_OPCODE();
4278+
}
4279+
42434280
ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY, SPEC(OBSERVER))
42444281
{
42454282
USE_OPLINE

0 commit comments

Comments
 (0)