#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;
}
}
}
+
+/*
+ * 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 =>
+ * 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 < s->len; i++) {
+ unsigned char c = s->val[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 < s->len; i++) {
+ unsigned char c = s->val[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 PHP_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(smart_str *str, zend_ast_list *list, int indent)
+{
+ uint32_t i = 0;
+
+ while (i < list->children) {
+ if (i != 0) {
+ smart_str_appends(str, ", ");
+ }
+ zend_ast_export_name(str, list->child[i], 0, indent);
+ 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->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, key->val, key->len);
+ 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();
+ }
+}
+
+#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)
+{
+ zval *zv;
+ 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:
+
+ /* 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, decl->name->val, decl->name->len);
+ }
+ 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, decl->name->val, decl->name->len);
+ 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, "}\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_LIST:
+ smart_str_appends(str, "list(");
+ zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
+ smart_str_appendc(str, ')');
+ 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_RESOLVE_CLASS_NAME:
+ zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ APPEND_STR("::class");
+ 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 {
+ zend_ast_export_ex(str, ast->child[0], 0, indent);
+ }
+ 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_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 ");
+ 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: BINARY_OP(" instanceof ", 230, 231, 231);
+ 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_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_ns_name(str, ast->child[0], 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;
+}