Skip to content

Commit 090ed5d

Browse files
committed
is_literal
1 parent 0c89eda commit 090ed5d

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
@@ -3821,7 +3821,21 @@ static zend_result zend_compile_func_is_scalar(znode *result, zend_ast_list *arg
38213821
opline = zend_emit_op_tmp(result, ZEND_TYPE_CHECK, &arg_node, NULL);
38223822
opline->extended_value = (1 << IS_FALSE | 1 << IS_TRUE | 1 << IS_DOUBLE | 1 << IS_LONG | 1 << IS_STRING);
38233823
return SUCCESS;
3824-
}
3824+
} /* }}} */
3825+
3826+
static zend_result zend_compile_func_is_literal(znode *result, zend_ast_list *args) /* {{{ */
3827+
{
3828+
znode arg_node;
3829+
3830+
if (args->children != 1) {
3831+
return FAILURE;
3832+
}
3833+
3834+
zend_compile_expr(&arg_node, args->child[0]);
3835+
zend_emit_op(result, ZEND_LITERAL_CHECK, &arg_node, NULL);
3836+
3837+
return SUCCESS;
3838+
} /* }}} */
38253839

38263840
zend_result zend_compile_func_cast(znode *result, zend_ast_list *args, uint32_t type) /* {{{ */
38273841
{
@@ -4352,6 +4366,8 @@ zend_result zend_try_compile_special_func(znode *result, zend_string *lcname, ze
43524366
return zend_compile_func_typecheck(result, args, IS_RESOURCE);
43534367
} else if (zend_string_equals_literal(lcname, "is_scalar")) {
43544368
return zend_compile_func_is_scalar(result, args);
4369+
} else if (zend_string_equals_literal(lcname, "is_literal")) {
4370+
return zend_compile_func_is_literal(result, args);
43554371
} else if (zend_string_equals_literal(lcname, "boolval")) {
43564372
return zend_compile_func_cast(result, args, _IS_BOOL);
43574373
} else if (zend_string_equals_literal(lcname, "intval")) {
@@ -6998,6 +7014,7 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
69987014

69997015
unqualified_name = decl->name;
70007016
op_array->function_name = name = zend_prefix_with_ns(unqualified_name);
7017+
70017018
lcname = zend_string_tolower(name);
70027019

70037020
if (FC(imports_function)) {
@@ -8369,6 +8386,7 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
83698386
key_ast = elem_ast->child[1];
83708387
if (key_ast) {
83718388
zval *key = zend_ast_get_zval(key_ast);
8389+
83728390
switch (Z_TYPE_P(key)) {
83738391
case IS_LONG:
83748392
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
@@ -1859,10 +1859,15 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
18591859
{
18601860
zval *orig_op1 = op1;
18611861
zval op1_copy, op2_copy;
1862+
bool literal = false;
18621863

18631864
ZVAL_UNDEF(&op1_copy);
18641865
ZVAL_UNDEF(&op2_copy);
18651866

1867+
if (UNEXPECTED(Z_IS_LITERAL_P(op1) && Z_IS_LITERAL_P(op2))) {
1868+
literal = true;
1869+
}
1870+
18661871
do {
18671872
if (UNEXPECTED(Z_TYPE_P(op1) != IS_STRING)) {
18681873
if (Z_ISREF_P(op1)) {
@@ -1913,13 +1918,23 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
19131918
}
19141919
ZVAL_COPY(result, op2);
19151920
}
1921+
if (literal) {
1922+
ZSTR_SET_LITERAL(&Z_STR_P(result));
1923+
} else {
1924+
ZSTR_UNSET_LITERAL(&Z_STR_P(result));
1925+
}
19161926
} else if (UNEXPECTED(Z_STRLEN_P(op2) == 0)) {
19171927
if (EXPECTED(result != op1)) {
19181928
if (result == orig_op1) {
19191929
i_zval_ptr_dtor(result);
19201930
}
19211931
ZVAL_COPY(result, op1);
19221932
}
1933+
if (literal) {
1934+
ZSTR_SET_LITERAL(&Z_STR_P(result));
1935+
} else {
1936+
ZSTR_UNSET_LITERAL(&Z_STR_P(result));
1937+
}
19231938
} else {
19241939
size_t op1_len = Z_STRLEN_P(op1);
19251940
size_t op2_len = Z_STRLEN_P(op2);
@@ -1939,12 +1954,18 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval
19391954
if (result == op1 && Z_REFCOUNTED_P(result)) {
19401955
/* special case, perform operations on result */
19411956
result_str = zend_string_extend(Z_STR_P(result), result_len, 0);
1957+
if (UNEXPECTED(!literal && ZSTR_IS_LITERAL(result_str))) {
1958+
ZSTR_UNSET_LITERAL(&result_str);
1959+
}
19421960
} else {
19431961
result_str = zend_string_alloc(result_len, 0);
19441962
memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
19451963
if (result == orig_op1) {
19461964
i_zval_ptr_dtor(result);
19471965
}
1966+
if (UNEXPECTED(literal && !ZSTR_IS_LITERAL(result_str))) {
1967+
ZSTR_SET_LITERAL_FAST(result_str);
1968+
}
19481969
}
19491970

19501971
/* 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);
@@ -3100,7 +3106,6 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31003106
zval *op1, *op2;
31013107
zend_string *op1_str, *op2_str, *str;
31023108

3103-
31043109
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
31053110
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
31063111
if ((OP1_TYPE == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) &&
@@ -3132,13 +3137,19 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31323137
size_t len = ZSTR_LEN(op1_str);
31333138

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

31553166
SAVE_OPLINE();
3167+
31563168
if (OP1_TYPE == IS_CONST) {
31573169
op1_str = Z_STR_P(op1);
31583170
} else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
@@ -3167,12 +3179,14 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31673179
op2_str = Z_STR_P(op2);
31683180
} else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
31693181
op2_str = zend_string_copy(Z_STR_P(op2));
3182+
31703183
} else {
31713184
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) {
31723185
ZVAL_UNDEFINED_OP2();
31733186
}
31743187
op2_str = zval_get_string_func(op2);
31753188
}
3189+
31763190
do {
31773191
if (OP1_TYPE != IS_CONST) {
31783192
if (UNEXPECTED(ZSTR_LEN(op1_str) == 0)) {
@@ -3199,6 +3213,9 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
31993213
}
32003214
}
32013215
str = zend_string_alloc(ZSTR_LEN(op1_str) + ZSTR_LEN(op2_str), 0);
3216+
if (UNEXPECTED(ZSTR_IS_LITERAL(op1_str) && ZSTR_IS_LITERAL(op2_str))) {
3217+
ZSTR_SET_LITERAL_FAST(str);
3218+
}
32023219
memcpy(ZSTR_VAL(str), ZSTR_VAL(op1_str), ZSTR_LEN(op1_str));
32033220
memcpy(ZSTR_VAL(str) + ZSTR_LEN(op1_str), ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
32043221
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
@@ -3209,6 +3226,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
32093226
zend_string_release_ex(op2_str, 0);
32103227
}
32113228
} while (0);
3229+
32123230
FREE_OP1();
32133231
FREE_OP2();
32143232
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3287,11 +3305,12 @@ ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV, NUM)
32873305
ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM)
32883306
{
32893307
USE_OPLINE
3290-
zend_string **rope;
3291-
zval *var, *ret;
3308+
zend_string **rope, *result;
3309+
zval *var;
32923310
uint32_t i;
32933311
size_t len = 0;
32943312
char *target;
3313+
bool literal = true;
32953314

32963315
rope = (zend_string**)EX_VAR(opline->op1.var);
32973316
if (OP2_TYPE == IS_CONST) {
@@ -3327,16 +3346,22 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM)
33273346
for (i = 0; i <= opline->extended_value; i++) {
33283347
len += ZSTR_LEN(rope[i]);
33293348
}
3330-
ret = EX_VAR(opline->result.var);
3331-
ZVAL_STR(ret, zend_string_alloc(len, 0));
3332-
target = Z_STRVAL_P(ret);
3349+
3350+
result = zend_string_alloc(len, 0);
3351+
target = ZSTR_VAL(result);
33333352
for (i = 0; i <= opline->extended_value; i++) {
3353+
if (literal && !ZSTR_IS_LITERAL(rope[i])) {
3354+
literal = false;
3355+
}
33343356
memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i]));
33353357
target += ZSTR_LEN(rope[i]);
33363358
zend_string_release_ex(rope[i], 0);
33373359
}
33383360
*target = '\0';
3339-
3361+
if (literal) {
3362+
ZSTR_SET_LITERAL_FAST(result);
3363+
}
3364+
ZVAL_STR(EX_VAR(opline->result.var), result);
33403365
ZEND_VM_NEXT_OPCODE();
33413366
}
33423367

@@ -4241,6 +4266,18 @@ ZEND_VM_COLD_HANDLER(201, ZEND_VERIFY_NEVER_TYPE, UNUSED, UNUSED)
42414266
HANDLE_EXCEPTION();
42424267
}
42434268

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

0 commit comments

Comments
 (0)