php-src/Zend/zend_ast.c
2017-03-23 22:49:51 +01:00

1728 lines
48 KiB
C

/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2017 Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Bob Weinand <bwoebi@php.net> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#include "zend_ast.h"
#include "zend_API.h"
#include "zend_operators.h"
#include "zend_language_parser.h"
#include "zend_smart_str.h"
#include "zend_exceptions.h"
ZEND_API zend_ast_process_t zend_ast_process = NULL;
static inline void *zend_ast_alloc(size_t size) {
return zend_arena_alloc(&CG(ast_arena), size);
}
static inline void *zend_ast_realloc(void *old, size_t old_size, size_t new_size) {
void *new = zend_ast_alloc(new_size);
memcpy(new, old, old_size);
return new;
}
static inline size_t zend_ast_size(uint32_t children) {
return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
}
static inline size_t zend_ast_list_size(uint32_t children) {
return sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
}
ZEND_API zend_ast *zend_ast_create_znode(znode *node) {
zend_ast_znode *ast;
ast = zend_ast_alloc(sizeof(zend_ast_znode));
ast->kind = ZEND_AST_ZNODE;
ast->attr = 0;
ast->lineno = CG(zend_lineno);
ast->node = *node;
return (zend_ast *) ast;
}
ZEND_API zend_ast *zend_ast_create_zval_with_lineno(zval *zv, zend_ast_attr attr, uint32_t lineno) {
zend_ast_zval *ast;
ast = zend_ast_alloc(sizeof(zend_ast_zval));
ast->kind = ZEND_AST_ZVAL;
ast->attr = attr;
ZVAL_COPY_VALUE(&ast->val, zv);
ast->val.u2.lineno = lineno;
return (zend_ast *) ast;
}
ZEND_API zend_ast *zend_ast_create_zval_ex(zval *zv, zend_ast_attr attr) {
return zend_ast_create_zval_with_lineno(zv, attr, CG(zend_lineno));
}
ZEND_API zend_ast *zend_ast_create_decl(
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3
) {
zend_ast_decl *ast;
ast = zend_ast_alloc(sizeof(zend_ast_decl));
ast->kind = kind;
ast->attr = 0;
ast->start_lineno = start_lineno;
ast->end_lineno = CG(zend_lineno);
ast->flags = flags;
ast->lex_pos = LANG_SCNG(yy_text);
ast->doc_comment = doc_comment;
ast->name = name;
ast->child[0] = child0;
ast->child[1] = child1;
ast->child[2] = child2;
ast->child[3] = child3;
return (zend_ast *) ast;
}
static zend_ast *zend_ast_create_from_va_list(zend_ast_kind kind, zend_ast_attr attr, va_list va) {
uint32_t i, children = kind >> ZEND_AST_NUM_CHILDREN_SHIFT;
zend_ast *ast;
ast = zend_ast_alloc(zend_ast_size(children));
ast->kind = kind;
ast->attr = attr;
ast->lineno = (uint32_t) -1;
for (i = 0; i < children; ++i) {
ast->child[i] = va_arg(va, zend_ast *);
if (ast->child[i] != NULL) {
uint32_t lineno = zend_ast_get_lineno(ast->child[i]);
if (lineno < ast->lineno) {
ast->lineno = lineno;
}
}
}
if (ast->lineno == UINT_MAX) {
ast->lineno = CG(zend_lineno);
}
return ast;
}
ZEND_API zend_ast *zend_ast_create_ex(zend_ast_kind kind, zend_ast_attr attr, ...) {
va_list va;
zend_ast *ast;
va_start(va, attr);
ast = zend_ast_create_from_va_list(kind, attr, va);
va_end(va);
return ast;
}
ZEND_API zend_ast *zend_ast_create(zend_ast_kind kind, ...) {
va_list va;
zend_ast *ast;
va_start(va, kind);
ast = zend_ast_create_from_va_list(kind, 0, va);
va_end(va);
return ast;
}
ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind kind, ...) {
zend_ast *ast;
zend_ast_list *list;
ast = zend_ast_alloc(zend_ast_list_size(4));
list = (zend_ast_list *) ast;
list->kind = kind;
list->attr = 0;
list->lineno = CG(zend_lineno);
list->children = 0;
{
va_list va;
uint32_t i;
va_start(va, kind);
for (i = 0; i < init_children; ++i) {
zend_ast *child = va_arg(va, zend_ast *);
ast = zend_ast_list_add(ast, child);
if (child != NULL) {
uint32_t lineno = zend_ast_get_lineno(child);
if (lineno < ast->lineno) {
ast->lineno = lineno;
}
}
}
va_end(va);
}
return ast;
}
static inline zend_bool is_power_of_two(uint32_t n) {
return ((n != 0) && (n == (n & (~n + 1))));
}
ZEND_API zend_ast *zend_ast_list_add(zend_ast *ast, zend_ast *op) {
zend_ast_list *list = zend_ast_get_list(ast);
if (list->children >= 4 && is_power_of_two(list->children)) {
list = zend_ast_realloc(list,
zend_ast_list_size(list->children), zend_ast_list_size(list->children * 2));
}
list->child[list->children++] = op;
return (zend_ast *) list;
}
static int zend_ast_add_array_element(zval *result, zval *offset, zval *expr)
{
switch (Z_TYPE_P(offset)) {
case IS_UNDEF:
if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), expr)) {
zend_error(E_WARNING,
"Cannot add element to the array as the next element is already occupied");
zval_ptr_dtor(expr);
}
break;
case IS_STRING:
zend_symtable_update(Z_ARRVAL_P(result), Z_STR_P(offset), expr);
zval_dtor(offset);
break;
case IS_NULL:
zend_symtable_update(Z_ARRVAL_P(result), ZSTR_EMPTY_ALLOC(), expr);
break;
case IS_LONG:
zend_hash_index_update(Z_ARRVAL_P(result), Z_LVAL_P(offset), expr);
break;
case IS_FALSE:
zend_hash_index_update(Z_ARRVAL_P(result), 0, expr);
break;
case IS_TRUE:
zend_hash_index_update(Z_ARRVAL_P(result), 1, expr);
break;
case IS_DOUBLE:
zend_hash_index_update(Z_ARRVAL_P(result), zend_dval_to_lval(Z_DVAL_P(offset)), expr);
break;
case IS_RESOURCE:
zend_error(E_NOTICE, "Resource ID#%d used as offset, casting to integer (%d)", Z_RES_HANDLE_P(offset), Z_RES_HANDLE_P(offset));
zend_hash_index_update(Z_ARRVAL_P(result), Z_RES_HANDLE_P(offset), expr);
break;
default:
zend_throw_error(NULL, "Illegal offset type");
return FAILURE;
}
return SUCCESS;
}
ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)
{
zval op1, op2;
int ret = SUCCESS;
switch (ast->kind) {
case ZEND_AST_BINARY_OP:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
} else {
binary_op_type op = get_binary_op(ast->attr);
ret = op(result, &op1, &op2);
zval_dtor(&op1);
zval_dtor(&op2);
}
break;
case ZEND_AST_GREATER:
case ZEND_AST_GREATER_EQUAL:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
} else {
/* op1 > op2 is the same as op2 < op1 */
binary_op_type op = ast->kind == ZEND_AST_GREATER
? is_smaller_function : is_smaller_or_equal_function;
ret = op(result, &op2, &op1);
zval_dtor(&op1);
zval_dtor(&op2);
}
break;
case ZEND_AST_UNARY_OP:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
unary_op_type op = get_unary_op(ast->attr);
ret = op(result, &op1);
zval_dtor(&op1);
}
break;
case ZEND_AST_ZVAL:
{
zval *zv = zend_ast_get_zval(ast);
if (Z_OPT_CONSTANT_P(zv)) {
if (!(Z_TYPE_FLAGS_P(zv) & IS_TYPE_IMMUTABLE)) {
if (UNEXPECTED(zval_update_constant_ex(zv, scope) != SUCCESS)) {
ret = FAILURE;
break;
}
ZVAL_COPY(result, zv);
} else {
ZVAL_COPY_VALUE(result, zv);
if (UNEXPECTED(zval_update_constant_ex(result, scope) != SUCCESS)) {
ret = FAILURE;
break;
}
}
} else {
ZVAL_COPY(result, zv);
}
break;
}
case ZEND_AST_AND:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
zval_dtor(&op2);
} else {
ZVAL_FALSE(result);
}
zval_dtor(&op1);
break;
case ZEND_AST_OR:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
ZVAL_TRUE(result);
} else {
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
zval_dtor(&op2);
}
zval_dtor(&op1);
break;
case ZEND_AST_CONDITIONAL:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
if (!ast->child[1]) {
*result = op1;
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
break;
}
zval_dtor(&op1);
}
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[2], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
break;
}
zval_dtor(&op1);
}
break;
case ZEND_AST_COALESCE:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (Z_TYPE(op1) > IS_NULL) {
*result = op1;
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
break;
}
zval_dtor(&op1);
}
break;
case ZEND_AST_UNARY_PLUS:
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
ZVAL_LONG(&op1, 0);
ret = add_function(result, &op1, &op2);
zval_dtor(&op2);
}
break;
case ZEND_AST_UNARY_MINUS:
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
ZVAL_LONG(&op1, 0);
ret = sub_function(result, &op1, &op2);
zval_dtor(&op2);
}
break;
case ZEND_AST_ARRAY:
array_init(result);
{
uint32_t i;
zend_ast_list *list = zend_ast_get_list(ast);
for (i = 0; i < list->children; i++) {
zend_ast *elem = list->child[i];
if (elem->child[1]) {
if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[1], scope) != SUCCESS)) {
zval_dtor(result);
return FAILURE;
}
} else {
ZVAL_UNDEF(&op1);
}
if (UNEXPECTED(zend_ast_evaluate(&op2, elem->child[0], scope) != SUCCESS)) {
zval_dtor(&op1);
zval_dtor(result);
return FAILURE;
}
if (UNEXPECTED(zend_ast_add_array_element(result, &op1, &op2) != SUCCESS)) {
zval_dtor(&op1);
zval_dtor(&op2);
zval_dtor(result);
return FAILURE;
}
}
}
break;
case ZEND_AST_DIM:
if (ast->child[1] == NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
}
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_dtor(&op1);
ret = FAILURE;
} else {
zval tmp;
if (ast->attr == ZEND_DIM_IS) {
zend_fetch_dimension_by_zval_is(&tmp, &op1, &op2, IS_CONST);
} else {
zend_fetch_dimension_by_zval(&tmp, &op1, &op2);
}
if (UNEXPECTED(Z_ISREF(tmp))) {
ZVAL_DUP(result, Z_REFVAL(tmp));
} else {
ZVAL_DUP(result, &tmp);
}
zval_ptr_dtor(&tmp);
zval_dtor(&op1);
zval_dtor(&op2);
}
break;
default:
zend_throw_error(NULL, "Unsupported constant expression");
ret = FAILURE;
}
return ret;
}
ZEND_API zend_ast *zend_ast_copy(zend_ast *ast)
{
if (ast == NULL) {
return NULL;
} else if (ast->kind == ZEND_AST_ZVAL) {
zend_ast_zval *new = emalloc(sizeof(zend_ast_zval));
new->kind = ZEND_AST_ZVAL;
new->attr = ast->attr;
ZVAL_COPY(&new->val, zend_ast_get_zval(ast));
return (zend_ast *) new;
} else if (zend_ast_is_list(ast)) {
zend_ast_list *list = zend_ast_get_list(ast);
zend_ast_list *new = emalloc(zend_ast_list_size(list->children));
uint32_t i;
new->kind = list->kind;
new->attr = list->attr;
new->children = list->children;
for (i = 0; i < list->children; i++) {
new->child[i] = zend_ast_copy(list->child[i]);
}
return (zend_ast *) new;
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
zend_ast *new = emalloc(zend_ast_size(children));
new->kind = ast->kind;
new->attr = ast->attr;
for (i = 0; i < children; i++) {
new->child[i] = zend_ast_copy(ast->child[i]);
}
return new;
}
}
static void zend_ast_destroy_ex(zend_ast *ast, zend_bool free) {
if (!ast) {
return;
}
switch (ast->kind) {
case ZEND_AST_ZVAL:
/* Destroy value without using GC: When opcache moves arrays into SHM it will
* free the zend_array structure, so references to it from outside the op array
* become invalid. GC would cause such a reference in the root buffer. */
zval_ptr_dtor_nogc(zend_ast_get_zval(ast));
break;
case ZEND_AST_FUNC_DECL:
case ZEND_AST_CLOSURE:
case ZEND_AST_METHOD:
case ZEND_AST_CLASS:
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
if (decl->name) {
zend_string_release(decl->name);
}
if (decl->doc_comment) {
zend_string_release(decl->doc_comment);
}
zend_ast_destroy_ex(decl->child[0], free);
zend_ast_destroy_ex(decl->child[1], free);
zend_ast_destroy_ex(decl->child[2], free);
zend_ast_destroy_ex(decl->child[3], free);
break;
}
default:
if (zend_ast_is_list(ast)) {
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; i++) {
zend_ast_destroy_ex(list->child[i], free);
}
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
for (i = 0; i < children; i++) {
zend_ast_destroy_ex(ast->child[i], free);
}
}
}
if (free) {
efree(ast);
}
}
ZEND_API void zend_ast_destroy(zend_ast *ast) {
zend_ast_destroy_ex(ast, 0);
}
ZEND_API void zend_ast_destroy_and_free(zend_ast *ast) {
zend_ast_destroy_ex(ast, 1);
}
ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn) {
if (zend_ast_is_list(ast)) {
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
fn(&list->child[i]);
}
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
for (i = 0; i < children; ++i) {
fn(&ast->child[i]);
}
}
}
/*
* Operator Precendence
* ====================
* priority associativity operators
* ----------------------------------
* 10 left include, include_once, eval, require, require_once
* 20 left ,
* 30 left or
* 40 left xor
* 50 left and
* 60 right print
* 70 right yield
* 80 right =>
* 85 right yield from
* 90 right = += -= *= /= .= %= &= |= ^= <<= >>= **=
* 100 left ? :
* 110 right ??
* 120 left ||
* 130 left &&
* 140 left |
* 150 left ^
* 160 left &
* 170 non-associative == != === !==
* 180 non-associative < <= > >= <=>
* 190 left << >>
* 200 left + - .
* 210 left * / %
* 220 right !
* 230 non-associative instanceof
* 240 right + - ++ -- ~ (type) @
* 250 right **
* 260 left [
* 270 non-associative clone new
*/
static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent);
static void zend_ast_export_str(smart_str *str, zend_string *s)
{
size_t i;
for (i = 0; i < ZSTR_LEN(s); i++) {
unsigned char c = ZSTR_VAL(s)[i];
if (c == '\'' || c == '\\') {
smart_str_appendc(str, '\\');
smart_str_appendc(str, c);
} else {
smart_str_appendc(str, c);
}
}
}
static void zend_ast_export_qstr(smart_str *str, char quote, zend_string *s)
{
size_t i;
for (i = 0; i < ZSTR_LEN(s); i++) {
unsigned char c = ZSTR_VAL(s)[i];
if (c < ' ') {
switch (c) {
case '\n':
smart_str_appends(str, "\\n");
break;
case '\r':
smart_str_appends(str, "\\r");
break;
case '\t':
smart_str_appends(str, "\\t");
break;
case '\f':
smart_str_appends(str, "\\f");
break;
case '\v':
smart_str_appends(str, "\\v");
break;
#ifdef ZEND_WIN32
case VK_ESCAPE:
#else
case '\e':
#endif
smart_str_appends(str, "\\e");
break;
default:
smart_str_appends(str, "\\0");
smart_str_appendc(str, '0' + (c / 8));
smart_str_appendc(str, '0' + (c % 8));
break;
}
} else {
if (c == quote || c == '$' || c == '\\') {
smart_str_appendc(str, '\\');
}
smart_str_appendc(str, c);
}
}
}
static void zend_ast_export_indent(smart_str *str, int indent)
{
while (indent > 0) {
smart_str_appends(str, " ");
indent--;
}
}
static void zend_ast_export_name(smart_str *str, zend_ast *ast, int priority, int indent)
{
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
if (Z_TYPE_P(zv) == IS_STRING) {
smart_str_append(str, Z_STR_P(zv));
return;
}
}
zend_ast_export_ex(str, ast, priority, indent);
}
static void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int priority, int indent)
{
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
if (Z_TYPE_P(zv) == IS_STRING) {
if (ast->attr == ZEND_NAME_FQ) {
smart_str_appendc(str, '\\');
} else if (ast->attr == ZEND_NAME_RELATIVE) {
smart_str_appends(str, "namespace\\");
}
smart_str_append(str, Z_STR_P(zv));
return;
}
}
zend_ast_export_ex(str, ast, priority, indent);
}
static int zend_ast_valid_var_char(char ch)
{
unsigned char c = (unsigned char)ch;
if (c != '_' && c < 127 &&
(c < '0' || c > '9') &&
(c < 'A' || c > 'Z') &&
(c < 'a' || c > 'z')) {
return 0;
}
return 1;
}
static int zend_ast_valid_var_name(const char *s, size_t len)
{
unsigned char c;
size_t i;
if (len == 0) {
return 0;
}
c = (unsigned char)s[0];
if (c != '_' && c < 127 &&
(c < 'A' || c > 'Z') &&
(c < 'a' || c > 'z')) {
return 0;
}
for (i = 1; i < len; i++) {
c = (unsigned char)s[i];
if (c != '_' && c < 127 &&
(c < '0' || c > '9') &&
(c < 'A' || c > 'Z') &&
(c < 'a' || c > 'z')) {
return 0;
}
}
return 1;
}
static void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent)
{
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
if (Z_TYPE_P(zv) == IS_STRING &&
zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) {
smart_str_append(str, Z_STR_P(zv));
return;
}
} else if (ast->kind == ZEND_AST_VAR) {
zend_ast_export_ex(str, ast, 0, indent);
return;
}
smart_str_appendc(str, '{');
zend_ast_export_name(str, ast, 0, indent);
smart_str_appendc(str, '}');
}
static void zend_ast_export_list(smart_str *str, zend_ast_list *list, int separator, int priority, int indent)
{
uint32_t i = 0;
while (i < list->children) {
if (i != 0 && separator) {
smart_str_appends(str, ", ");
}
zend_ast_export_ex(str, list->child[i], priority, indent);
i++;
}
}
static void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_list *list, int indent)
{
uint32_t i = 0;
zend_ast *ast;
while (i < list->children) {
ast = list->child[i];
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
zend_ast_export_qstr(str, quote, Z_STR_P(zv));
} else if (ast->kind == ZEND_AST_VAR &&
ast->child[0]->kind == ZEND_AST_ZVAL &&
(i + 1 == list->children ||
list->child[i + 1]->kind != ZEND_AST_ZVAL ||
!zend_ast_valid_var_char(
*Z_STRVAL_P(
zend_ast_get_zval(list->child[i + 1]))))) {
zend_ast_export_ex(str, ast, 0, indent);
} else {
smart_str_appendc(str, '{');
zend_ast_export_ex(str, ast, 0, indent);
smart_str_appendc(str, '}');
}
i++;
}
}
static void zend_ast_export_name_list_ex(smart_str *str, zend_ast_list *list, int indent, const char *separator)
{
uint32_t i = 0;
while (i < list->children) {
if (i != 0) {
smart_str_appends(str, separator);
}
zend_ast_export_name(str, list->child[i], 0, indent);
i++;
}
}
#define zend_ast_export_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, ", ")
#define zend_ast_export_catch_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, "|")
static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int indent)
{
uint32_t i = 0;
while (i < list->children) {
if (i != 0) {
smart_str_appends(str, ", ");
}
if (list->child[i]->attr) {
smart_str_appendc(str, '&');
}
smart_str_appendc(str, '$');
zend_ast_export_name(str, list->child[i], 20, indent);
i++;
}
}
static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent)
{
if (!ast) {
return;
}
if (ast->kind == ZEND_AST_STMT_LIST ||
ast->kind == ZEND_AST_TRAIT_ADAPTATIONS) {
zend_ast_list *list = (zend_ast_list*)ast;
uint32_t i = 0;
while (i < list->children) {
ast = list->child[i];
zend_ast_export_stmt(str, ast, indent);
i++;
}
} else {
zend_ast_export_indent(str, indent);
zend_ast_export_ex(str, ast, 0, indent);
switch (ast->kind) {
case ZEND_AST_LABEL:
case ZEND_AST_IF:
case ZEND_AST_SWITCH:
case ZEND_AST_WHILE:
case ZEND_AST_TRY:
case ZEND_AST_FOR:
case ZEND_AST_FOREACH:
case ZEND_AST_FUNC_DECL:
case ZEND_AST_METHOD:
case ZEND_AST_CLASS:
case ZEND_AST_USE_TRAIT:
case ZEND_AST_NAMESPACE:
case ZEND_AST_DECLARE:
break;
default:
smart_str_appendc(str, ';');
break;
}
smart_str_appendc(str, '\n');
}
}
static void zend_ast_export_if_stmt(smart_str *str, zend_ast_list *list, int indent)
{
uint32_t i;
zend_ast *ast;
tail_call:
i = 0;
while (i < list->children) {
ast = list->child[i];
ZEND_ASSERT(ast->kind == ZEND_AST_IF_ELEM);
if (ast->child[0]) {
if (i == 0) {
smart_str_appends(str, "if (");
} else {
zend_ast_export_indent(str, indent);
smart_str_appends(str, "} elseif (");
}
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
} else {
zend_ast_export_indent(str, indent);
smart_str_appends(str, "} else ");
if (ast->child[1]->kind == ZEND_AST_IF) {
list = (zend_ast_list*)ast->child[1];
goto tail_call;
} else {
smart_str_appends(str, "{\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
}
}
i++;
}
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
}
static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int indent)
{
zend_long idx;
zend_string *key;
zval *val;
int first;
ZVAL_DEREF(zv);
switch (Z_TYPE_P(zv)) {
case IS_NULL:
smart_str_appends(str, "null");
break;
case IS_FALSE:
smart_str_appends(str, "false");
break;
case IS_TRUE:
smart_str_appends(str, "true");
break;
case IS_LONG:
smart_str_append_long(str, Z_LVAL_P(zv));
break;
case IS_DOUBLE:
key = zend_strpprintf(0, "%.*G", (int) EG(precision), Z_DVAL_P(zv));
smart_str_appendl(str, ZSTR_VAL(key), ZSTR_LEN(key));
zend_string_release(key);
break;
case IS_STRING:
smart_str_appendc(str, '\'');
zend_ast_export_str(str, Z_STR_P(zv));
smart_str_appendc(str, '\'');
break;
case IS_ARRAY:
smart_str_appendc(str, '[');
first = 1;
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zv), idx, key, val) {
if (first) {
first = 0;
} else {
smart_str_appends(str, ", ");
}
if (key) {
smart_str_appendc(str, '\'');
zend_ast_export_str(str, key);
smart_str_appends(str, "' => ");
} else {
smart_str_append_long(str, idx);
smart_str_appends(str, " => ");
}
zend_ast_export_zval(str, val, 0, indent);
} ZEND_HASH_FOREACH_END();
smart_str_appendc(str, ']');
break;
case IS_CONSTANT:
smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv));
break;
case IS_CONSTANT_AST:
zend_ast_export_ex(str, Z_ASTVAL_P(zv), priority, indent);
break;
EMPTY_SWITCH_DEFAULT_CASE();
}
}
static void zend_ast_export_class_no_header(smart_str *str, zend_ast_decl *decl, int indent) {
if (decl->child[0]) {
smart_str_appends(str, " extends ");
zend_ast_export_ns_name(str, decl->child[0], 0, indent);
}
if (decl->child[1]) {
smart_str_appends(str, " implements ");
zend_ast_export_ex(str, decl->child[1], 0, indent);
}
smart_str_appends(str, " {\n");
zend_ast_export_stmt(str, decl->child[2], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appends(str, "}");
}
#define BINARY_OP(_op, _p, _pl, _pr) do { \
op = _op; \
p = _p; \
pl = _pl; \
pr = _pr; \
goto binary_op; \
} while (0)
#define PREFIX_OP(_op, _p, _pl) do { \
op = _op; \
p = _p; \
pl = _pl; \
goto prefix_op; \
} while (0)
#define FUNC_OP(_op) do { \
op = _op; \
goto func_op; \
} while (0)
#define POSTFIX_OP(_op, _p, _pl) do { \
op = _op; \
p = _p; \
pl = _pl; \
goto postfix_op; \
} while (0)
#define APPEND_NODE_1(_op) do { \
op = _op; \
goto append_node_1; \
} while (0)
#define APPEND_STR(_op) do { \
op = _op; \
goto append_str; \
} while (0)
#define APPEND_DEFAULT_VALUE(n) do { \
p = n; \
goto append_default_value; \
} while (0)
static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent)
{
zend_ast_decl *decl;
int p, pl, pr;
const char *op;
tail_call:
if (!ast) {
return;
}
switch (ast->kind) {
/* special nodes */
case ZEND_AST_ZVAL:
zend_ast_export_zval(str, zend_ast_get_zval(ast), priority, indent);
break;
case ZEND_AST_ZNODE:
/* This AST kind is only used for temporary nodes during compilation */
ZEND_ASSERT(0);
break;
/* declaration nodes */
case ZEND_AST_FUNC_DECL:
case ZEND_AST_CLOSURE:
case ZEND_AST_METHOD:
decl = (zend_ast_decl *) ast;
if (decl->flags & ZEND_ACC_PUBLIC) {
smart_str_appends(str, "public ");
} else if (decl->flags & ZEND_ACC_PROTECTED) {
smart_str_appends(str, "protected ");
} else if (decl->flags & ZEND_ACC_PRIVATE) {
smart_str_appends(str, "private ");
}
if (decl->flags & ZEND_ACC_STATIC) {
smart_str_appends(str, "static ");
}
if (decl->flags & ZEND_ACC_ABSTRACT) {
smart_str_appends(str, "abstract ");
}
if (decl->flags & ZEND_ACC_FINAL) {
smart_str_appends(str, "final ");
}
smart_str_appends(str, "function ");
if (decl->flags & ZEND_ACC_RETURN_REFERENCE) {
smart_str_appendc(str, '&');
}
if (ast->kind != ZEND_AST_CLOSURE) {
smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name));
}
smart_str_appendc(str, '(');
zend_ast_export_ex(str, decl->child[0], 0, indent);
smart_str_appendc(str, ')');
zend_ast_export_ex(str, decl->child[1], 0, indent);
if (decl->child[3]) {
smart_str_appends(str, ": ");
zend_ast_export_ns_name(str, decl->child[3], 0, indent);
}
if (decl->child[2]) {
smart_str_appends(str, " {\n");
zend_ast_export_stmt(str, decl->child[2], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
if (ast->kind != ZEND_AST_CLOSURE) {
smart_str_appendc(str, '\n');
}
} else {
smart_str_appends(str, ";\n");
}
break;
case ZEND_AST_CLASS:
decl = (zend_ast_decl *) ast;
if (decl->flags & ZEND_ACC_INTERFACE) {
smart_str_appends(str, "interface ");
} else if (decl->flags & ZEND_ACC_TRAIT) {
smart_str_appends(str, "trait ");
} else {
if (decl->flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
smart_str_appends(str, "abstract ");
}
if (decl->flags & ZEND_ACC_FINAL) {
smart_str_appends(str, "final ");
}
smart_str_appends(str, "class ");
}
smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name));
zend_ast_export_class_no_header(str, decl, indent);
smart_str_appendc(str, '\n');
break;
/* list nodes */
case ZEND_AST_ARG_LIST:
case ZEND_AST_EXPR_LIST:
case ZEND_AST_PARAM_LIST:
simple_list:
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
break;
case ZEND_AST_ARRAY:
smart_str_appendc(str, '[');
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
smart_str_appendc(str, ']');
break;
case ZEND_AST_ENCAPS_LIST:
smart_str_appendc(str, '"');
zend_ast_export_encaps_list(str, '"', (zend_ast_list*)ast, indent);
smart_str_appendc(str, '"');
break;
case ZEND_AST_STMT_LIST:
case ZEND_AST_TRAIT_ADAPTATIONS:
zend_ast_export_stmt(str, ast, indent);
break;
case ZEND_AST_IF:
zend_ast_export_if_stmt(str, (zend_ast_list*)ast, indent);
break;
case ZEND_AST_SWITCH_LIST:
case ZEND_AST_CATCH_LIST:
zend_ast_export_list(str, (zend_ast_list*)ast, 0, 0, indent);
break;
case ZEND_AST_CLOSURE_USES:
smart_str_appends(str, " use(");
zend_ast_export_var_list(str, (zend_ast_list*)ast, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_PROP_DECL:
if (ast->attr & ZEND_ACC_PUBLIC) {
smart_str_appends(str, "public ");
} else if (ast->attr & ZEND_ACC_PROTECTED) {
smart_str_appends(str, "protected ");
} else if (ast->attr & ZEND_ACC_PRIVATE) {
smart_str_appends(str, "private ");
}
if (ast->attr & ZEND_ACC_STATIC) {
smart_str_appends(str, "static ");
}
goto simple_list;
case ZEND_AST_CONST_DECL:
case ZEND_AST_CLASS_CONST_DECL:
smart_str_appends(str, "const ");
goto simple_list;
case ZEND_AST_NAME_LIST:
zend_ast_export_name_list(str, (zend_ast_list*)ast, indent);
break;
case ZEND_AST_USE:
smart_str_appends(str, "use ");
if (ast->attr == T_FUNCTION) {
smart_str_appends(str, "function ");
} else if (ast->attr == T_CONST) {
smart_str_appends(str, "const ");
}
goto simple_list;
/* 0 child nodes */
case ZEND_AST_MAGIC_CONST:
switch (ast->attr) {
case T_LINE: APPEND_STR("__LINE__");
case T_FILE: APPEND_STR("__FILE__");
case T_DIR: APPEND_STR("__DIR__");
case T_TRAIT_C: APPEND_STR("__TRAIT__");
case T_METHOD_C: APPEND_STR("__METHOD__");
case T_FUNC_C: APPEND_STR("__FUNCTION__");
case T_NS_C: APPEND_STR("__NAMESPACE__");
case T_CLASS_C: APPEND_STR("__CLASS__");
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_TYPE:
switch (ast->attr) {
case IS_ARRAY: APPEND_STR("array");
case IS_CALLABLE: APPEND_STR("callable");
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
/* 1 child node */
case ZEND_AST_VAR:
smart_str_appendc(str, '$');
zend_ast_export_var(str, ast->child[0], 0, indent);
break;
case ZEND_AST_CONST:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
break;
case ZEND_AST_UNPACK:
smart_str_appends(str, "...");
ast = ast->child[0];
goto tail_call;
case ZEND_AST_UNARY_PLUS: PREFIX_OP("+", 240, 241);
case ZEND_AST_UNARY_MINUS: PREFIX_OP("-", 240, 241);
case ZEND_AST_CAST:
switch (ast->attr) {
case IS_NULL: PREFIX_OP("(unset)", 240, 241);
case _IS_BOOL: PREFIX_OP("(bool)", 240, 241);
case IS_LONG: PREFIX_OP("(int)", 240, 241);
case IS_DOUBLE: PREFIX_OP("(double)", 240, 241);
case IS_STRING: PREFIX_OP("(string)", 240, 241);
case IS_ARRAY: PREFIX_OP("(array)", 240, 241);
case IS_OBJECT: PREFIX_OP("(object)", 240, 241);
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_EMPTY:
FUNC_OP("empty");
case ZEND_AST_ISSET:
FUNC_OP("isset");
case ZEND_AST_SILENCE:
PREFIX_OP("@", 240, 241);
case ZEND_AST_SHELL_EXEC:
smart_str_appendc(str, '`');
if (ast->child[0]->kind == ZEND_AST_ENCAPS_LIST) {
zend_ast_export_encaps_list(str, '`', (zend_ast_list*)ast->child[0], indent);
} else {
zval *zv;
ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_ZVAL);
zv = zend_ast_get_zval(ast->child[0]);
ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
zend_ast_export_qstr(str, '`', Z_STR_P(zv));
}
smart_str_appendc(str, '`');
break;
case ZEND_AST_CLONE:
PREFIX_OP("clone ", 270, 271);
case ZEND_AST_EXIT:
if (ast->child[0]) {
FUNC_OP("exit");
} else {
APPEND_STR("exit");
}
break;
case ZEND_AST_PRINT:
PREFIX_OP("print ", 60, 61);
case ZEND_AST_INCLUDE_OR_EVAL:
switch (ast->attr) {
case ZEND_INCLUDE_ONCE: FUNC_OP("include_once");
case ZEND_INCLUDE: FUNC_OP("include");
case ZEND_REQUIRE_ONCE: FUNC_OP("require_once");
case ZEND_REQUIRE: FUNC_OP("require");
case ZEND_EVAL: FUNC_OP("eval");
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_UNARY_OP:
switch (ast->attr) {
case ZEND_BW_NOT: PREFIX_OP("~", 240, 241);
case ZEND_BOOL_NOT: PREFIX_OP("!", 240, 241);
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_PRE_INC:
PREFIX_OP("++", 240, 241);
case ZEND_AST_PRE_DEC:
PREFIX_OP("--", 240, 241);
case ZEND_AST_POST_INC:
POSTFIX_OP("++", 240, 241);
case ZEND_AST_POST_DEC:
POSTFIX_OP("--", 240, 241);
case ZEND_AST_GLOBAL:
APPEND_NODE_1("global");
case ZEND_AST_UNSET:
FUNC_OP("unset");
case ZEND_AST_RETURN:
APPEND_NODE_1("return");
case ZEND_AST_LABEL:
zend_ast_export_name(str, ast->child[0], 0, indent);
smart_str_appendc(str, ':');
break;
case ZEND_AST_REF:
smart_str_appendc(str, '&');
ast = ast->child[0];
goto tail_call;
case ZEND_AST_HALT_COMPILER:
APPEND_STR("__HALT_COMPILER()");
case ZEND_AST_ECHO:
APPEND_NODE_1("echo");
case ZEND_AST_THROW:
APPEND_NODE_1("throw");
case ZEND_AST_GOTO:
smart_str_appends(str, "goto ");
zend_ast_export_name(str, ast->child[0], 0, indent);
break;
case ZEND_AST_BREAK:
APPEND_NODE_1("break");
case ZEND_AST_CONTINUE:
APPEND_NODE_1("continue");
/* 2 child nodes */
case ZEND_AST_DIM:
zend_ast_export_ex(str, ast->child[0], 260, indent);
smart_str_appendc(str, '[');
if (ast->child[1]) {
zend_ast_export_ex(str, ast->child[1], 0, indent);
}
smart_str_appendc(str, ']');
break;
case ZEND_AST_PROP:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, "->");
zend_ast_export_var(str, ast->child[1], 0, indent);
break;
case ZEND_AST_STATIC_PROP:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::$");
zend_ast_export_var(str, ast->child[1], 0, indent);
break;
case ZEND_AST_CALL:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_CLASS_CONST:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
zend_ast_export_name(str, ast->child[1], 0, indent);
break;
case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90);
case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90);
case ZEND_AST_ASSIGN_OP:
switch (ast->attr) {
case ZEND_ASSIGN_ADD: BINARY_OP(" += ", 90, 91, 90);
case ZEND_ASSIGN_SUB: BINARY_OP(" -= ", 90, 91, 90);
case ZEND_ASSIGN_MUL: BINARY_OP(" *= ", 90, 91, 90);
case ZEND_ASSIGN_DIV: BINARY_OP(" /= ", 90, 91, 90);
case ZEND_ASSIGN_MOD: BINARY_OP(" %= ", 90, 91, 90);
case ZEND_ASSIGN_SL: BINARY_OP(" <<= ", 90, 91, 90);
case ZEND_ASSIGN_SR: BINARY_OP(" >>= ", 90, 91, 90);
case ZEND_ASSIGN_CONCAT: BINARY_OP(" .= ", 90, 91, 90);
case ZEND_ASSIGN_BW_OR: BINARY_OP(" |= ", 90, 91, 90);
case ZEND_ASSIGN_BW_AND: BINARY_OP(" &= ", 90, 91, 90);
case ZEND_ASSIGN_BW_XOR: BINARY_OP(" ^= ", 90, 91, 90);
case ZEND_ASSIGN_POW: BINARY_OP(" **= ", 90, 91, 90);
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_BINARY_OP:
switch (ast->attr) {
case ZEND_ADD: BINARY_OP(" + ", 200, 200, 201);
case ZEND_SUB: BINARY_OP(" - ", 200, 200, 201);
case ZEND_MUL: BINARY_OP(" * ", 210, 210, 211);
case ZEND_DIV: BINARY_OP(" / ", 210, 210, 211);
case ZEND_MOD: BINARY_OP(" % ", 210, 210, 211);
case ZEND_SL: BINARY_OP(" << ", 190, 190, 191);
case ZEND_SR: BINARY_OP(" >> ", 190, 190, 191);
case ZEND_CONCAT: BINARY_OP(" . ", 200, 200, 201);
case ZEND_BW_OR: BINARY_OP(" | ", 140, 140, 141);
case ZEND_BW_AND: BINARY_OP(" & ", 160, 160, 161);
case ZEND_BW_XOR: BINARY_OP(" ^ ", 150, 150, 151);
case ZEND_IS_IDENTICAL: BINARY_OP(" === ", 170, 171, 171);
case ZEND_IS_NOT_IDENTICAL: BINARY_OP(" !== ", 170, 171, 171);
case ZEND_IS_EQUAL: BINARY_OP(" == ", 170, 171, 171);
case ZEND_IS_NOT_EQUAL: BINARY_OP(" != ", 170, 171, 171);
case ZEND_IS_SMALLER: BINARY_OP(" < ", 180, 181, 181);
case ZEND_IS_SMALLER_OR_EQUAL: BINARY_OP(" <= ", 180, 181, 181);
case ZEND_POW: BINARY_OP(" ** ", 250, 251, 250);
case ZEND_BOOL_XOR: BINARY_OP(" xor ", 40, 40, 41);
case ZEND_SPACESHIP: BINARY_OP(" <=> ", 180, 181, 181);
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
case ZEND_AST_GREATER: BINARY_OP(" > ", 180, 181, 181);
case ZEND_AST_GREATER_EQUAL: BINARY_OP(" >= ", 180, 181, 181);
case ZEND_AST_AND: BINARY_OP(" && ", 130, 130, 131);
case ZEND_AST_OR: BINARY_OP(" || ", 120, 120, 121);
case ZEND_AST_ARRAY_ELEM:
if (ast->child[1]) {
zend_ast_export_ex(str, ast->child[1], 80, indent);
smart_str_appends(str, " => ");
}
zend_ast_export_ex(str, ast->child[0], 80, indent);
break;
case ZEND_AST_NEW:
smart_str_appends(str, "new ");
if (ast->child[0]->kind == ZEND_AST_CLASS) {
smart_str_appends(str, "class");
if (zend_ast_get_list(ast->child[1])->children) {
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
}
zend_ast_export_class_no_header(str, (zend_ast_decl *) ast->child[0], indent);
} else {
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
}
break;
case ZEND_AST_INSTANCEOF:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, " instanceof ");
zend_ast_export_ns_name(str, ast->child[1], 0, indent);
break;
case ZEND_AST_YIELD:
if (priority > 70) smart_str_appendc(str, '(');
smart_str_appends(str, "yield ");
if (ast->child[0]) {
if (ast->child[1]) {
zend_ast_export_ex(str, ast->child[1], 70, indent);
smart_str_appends(str, " => ");
}
zend_ast_export_ex(str, ast->child[0], 70, indent);
}
if (priority > 70) smart_str_appendc(str, ')');
break;
case ZEND_AST_YIELD_FROM:
PREFIX_OP("yield from ", 85, 86);
case ZEND_AST_COALESCE: BINARY_OP(" ?? ", 110, 111, 110);
case ZEND_AST_STATIC:
smart_str_appends(str, "static $");
zend_ast_export_name(str, ast->child[0], 0, indent);
APPEND_DEFAULT_VALUE(1);
case ZEND_AST_WHILE:
smart_str_appends(str, "while (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_DO_WHILE:
smart_str_appends(str, "do {\n");
zend_ast_export_stmt(str, ast->child[0], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appends(str, "} while (");
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_IF_ELEM:
if (ast->child[0]) {
smart_str_appends(str, "if (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
} else {
smart_str_appends(str, "else {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
}
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_SWITCH:
smart_str_appends(str, "switch (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_ex(str, ast->child[1], 0, indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_SWITCH_CASE:
zend_ast_export_indent(str, indent);
if (ast->child[0]) {
smart_str_appends(str, "case ");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ":\n");
} else {
smart_str_appends(str, "default:\n");
}
zend_ast_export_stmt(str, ast->child[1], indent + 1);
break;
case ZEND_AST_DECLARE:
smart_str_appends(str, "declare(");
ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_CONST_DECL);
zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent);
smart_str_appendc(str, ')');
if (ast->child[1]) {
smart_str_appends(str, " {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
} else {
smart_str_appendc(str, ';');
}
break;
case ZEND_AST_PROP_ELEM:
smart_str_appendc(str, '$');
/* break missing intentionally */
case ZEND_AST_CONST_ELEM:
zend_ast_export_name(str, ast->child[0], 0, indent);
APPEND_DEFAULT_VALUE(1);
case ZEND_AST_USE_TRAIT:
smart_str_appends(str, "use ");
zend_ast_export_ex(str, ast->child[0], 0, indent);
if (ast->child[1]) {
smart_str_appends(str, " {\n");
zend_ast_export_ex(str, ast->child[1], 0, indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appends(str, "}");
} else {
smart_str_appends(str, ";");
}
break;
case ZEND_AST_TRAIT_PRECEDENCE:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, " insteadof ");
zend_ast_export_ex(str, ast->child[1], 0, indent);
break;
case ZEND_AST_METHOD_REFERENCE:
if (ast->child[0]) {
zend_ast_export_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
}
zend_ast_export_name(str, ast->child[1], 0, indent);
break;
case ZEND_AST_NAMESPACE:
smart_str_appends(str, "namespace");
if (ast->child[0]) {
smart_str_appendc(str, ' ');
zend_ast_export_name(str, ast->child[0], 0, indent);
}
if (ast->child[1]) {
smart_str_appends(str, " {\n");
zend_ast_export_stmt(str, ast->child[1], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appends(str, "}\n");
} else {
smart_str_appendc(str, ';');
}
break;
case ZEND_AST_USE_ELEM:
case ZEND_AST_TRAIT_ALIAS:
zend_ast_export_name(str, ast->child[0], 0, indent);
if (ast->attr & ZEND_ACC_PUBLIC) {
smart_str_appends(str, " as public");
} else if (ast->attr & ZEND_ACC_PROTECTED) {
smart_str_appends(str, " as protected");
} else if (ast->attr & ZEND_ACC_PRIVATE) {
smart_str_appends(str, " as private");
} else if (ast->child[1]) {
smart_str_appends(str, " as");
}
if (ast->child[1]) {
smart_str_appendc(str, ' ');
zend_ast_export_name(str, ast->child[1], 0, indent);
}
break;
/* 3 child nodes */
case ZEND_AST_METHOD_CALL:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, "->");
zend_ast_export_var(str, ast->child[1], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[2], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_STATIC_CALL:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
zend_ast_export_var(str, ast->child[1], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[2], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_CONDITIONAL:
if (priority > 100) smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[0], 100, indent);
if (ast->child[1]) {
smart_str_appends(str, " ? ");
zend_ast_export_ex(str, ast->child[1], 101, indent);
smart_str_appends(str, " : ");
} else {
smart_str_appends(str, " ?: ");
}
zend_ast_export_ex(str, ast->child[2], 101, indent);
if (priority > 100) smart_str_appendc(str, ')');
break;
case ZEND_AST_TRY:
smart_str_appends(str, "try {\n");
zend_ast_export_stmt(str, ast->child[0], indent + 1);
zend_ast_export_indent(str, indent);
zend_ast_export_ex(str, ast->child[1], 0, indent);
if (ast->child[2]) {
smart_str_appends(str, "} finally {\n");
zend_ast_export_stmt(str, ast->child[2], indent + 1);
zend_ast_export_indent(str, indent);
}
smart_str_appendc(str, '}');
break;
case ZEND_AST_CATCH:
smart_str_appends(str, "} catch (");
zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
smart_str_appends(str, " $");
zend_ast_export_var(str, ast->child[1], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[2], indent + 1);
zend_ast_export_indent(str, indent);
break;
case ZEND_AST_PARAM:
if (ast->child[0]) {
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appendc(str, ' ');
}
if (ast->attr & ZEND_PARAM_REF) {
smart_str_appendc(str, '&');
}
if (ast->attr & ZEND_PARAM_VARIADIC) {
smart_str_appends(str, "...");
}
smart_str_appendc(str, '$');
zend_ast_export_name(str, ast->child[1], 0, indent);
APPEND_DEFAULT_VALUE(2);
/* 4 child nodes */
case ZEND_AST_FOR:
smart_str_appends(str, "for (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appendc(str, ';');
if (ast->child[1]) {
smart_str_appendc(str, ' ');
zend_ast_export_ex(str, ast->child[1], 0, indent);
}
smart_str_appendc(str, ';');
if (ast->child[2]) {
smart_str_appendc(str, ' ');
zend_ast_export_ex(str, ast->child[2], 0, indent);
}
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[3], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_FOREACH:
smart_str_appends(str, "foreach (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, " as ");
if (ast->child[2]) {
zend_ast_export_ex(str, ast->child[2], 0, indent);
smart_str_appends(str, " => ");
}
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[3], indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
EMPTY_SWITCH_DEFAULT_CASE();
}
return;
binary_op:
if (priority > p) smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[0], pl, indent);
smart_str_appends(str, op);
zend_ast_export_ex(str, ast->child[1], pr, indent);
if (priority > p) smart_str_appendc(str, ')');
return;
prefix_op:
if (priority > p) smart_str_appendc(str, '(');
smart_str_appends(str, op);
zend_ast_export_ex(str, ast->child[0], pl, indent);
if (priority > p) smart_str_appendc(str, ')');
return;
postfix_op:
if (priority > p) smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[0], pl, indent);
smart_str_appends(str, op);
if (priority > p) smart_str_appendc(str, ')');
return;
func_op:
smart_str_appends(str, op);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appendc(str, ')');
return;
append_node_1:
smart_str_appends(str, op);
if (ast->child[0]) {
smart_str_appendc(str, ' ');
ast = ast->child[0];
goto tail_call;
}
return;
append_str:
smart_str_appends(str, op);
return;
append_default_value:
if (ast->child[p]) {
smart_str_appends(str, " = ");
ast = ast->child[p];
goto tail_call;
}
return;
}
ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix)
{
smart_str str = {0};
smart_str_appends(&str, prefix);
zend_ast_export_ex(&str, ast, 0, 0);
smart_str_appends(&str, suffix);
smart_str_0(&str);
return str.s;
}