Skip to content

AST Export / External Linkage #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions Zend/tests/attributes/012-ast-export.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
Attributes AST can be exported.
--FILE--
<?php

assert(0 && ($a = <<A1>><<A2>> function ($a, <<A3(1)>> $b) { }));

assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn () => 1));

assert(0 && ($a = new <<A1>> class() {
<<A1>><<A2>> const FOO = 'foo';
<<A2>> public $x;
<<A3>> function a() { }
}));

assert(0 && ($a = function () {
<<A1>> class Test1 { }
<<A2>> interface Test2 { }
<<A3>> trait Test3 { }
}));

?>
--EXPECTF--
Warning: assert(): assert(0 && ($a = <<A1>> <<A2>> function ($a, <<A3(1)>> $b) {
})) failed in %s on line %d

Warning: assert(): assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn() => 1)) failed in %s on line %d

Warning: assert(): assert(0 && ($a = new <<A1>> class {
<<A1>>
<<A2>>
const FOO = 'foo';
<<A2>>
public $x;
<<A3>>
public function a() {
}

})) failed in %s on line %d

Warning: assert(): assert(0 && ($a = function () {
<<A1>>
class Test1 {
}

<<A2>>
interface Test2 {
}

<<A3>>
trait Test3 {
}

})) failed in %s on line %d
55 changes: 54 additions & 1 deletion Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,41 @@ static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_d
smart_str_appends(str, "}");
}

static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) {
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;

for (i = 0; i < list->children; i++) {
zend_ast *attr = list->child[i];

smart_str_appends(str, "<<");
smart_str_append(str, zend_ast_get_str(attr->child[0]));

if (attr->child[1]) {
zend_ast_list *args = zend_ast_get_list(attr->child[1]);
uint32_t j;

smart_str_appendc(str, '(');
for (j = 0; j < args->children; j++) {
if (j) {
smart_str_appends(str, ", ");
}
zend_ast_export_ex(str, args->child[j], 0, indent);
}
smart_str_appendc(str, ')');
}

smart_str_appends(str, ">>");

if (newlines) {
smart_str_appendc(str, '\n');
zend_ast_export_indent(str, indent);
} else {
smart_str_appendc(str, ' ');
}
}
}

static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) {
if (ast->kind == ZEND_AST_TYPE_UNION) {
zend_ast_list *list = zend_ast_get_list(ast);
Expand Down Expand Up @@ -1410,6 +1445,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_ARROW_FUNC:
case ZEND_AST_METHOD:
decl = (zend_ast_decl *) ast;
if (decl->attributes) {
zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC) ? 0 : 1;
zend_ast_export_attributes(str, decl->attributes, indent, newlines);
}
if (decl->flags & ZEND_ACC_PUBLIC) {
smart_str_appends(str, "public ");
} else if (decl->flags & ZEND_ACC_PROTECTED) {
Expand Down Expand Up @@ -1466,6 +1505,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_CLASS:
decl = (zend_ast_decl *) ast;
if (decl->attributes) {
zend_ast_export_attributes(str, decl->attributes, indent, 1);
}
if (decl->flags & ZEND_ACC_INTERFACE) {
smart_str_appends(str, "interface ");
} else if (decl->flags & ZEND_ACC_TRAIT) {
Expand Down Expand Up @@ -1521,6 +1563,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast *type_ast = ast->child[0];
zend_ast *prop_ast = ast->child[1];

if (ast->child[2]) {
zend_ast_export_attributes(str, ast->child[2], indent, 1);
}
if (ast->attr & ZEND_ACC_PUBLIC) {
smart_str_appends(str, "public ");
} else if (ast->attr & ZEND_ACC_PROTECTED) {
Expand All @@ -1546,6 +1591,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appends(str, "const ");
goto simple_list;
case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES:
zend_ast_export_attributes(str, ast->child[1], indent, 1);
zend_ast_export_ex(str, ast->child[0], 0, indent);
break;
case ZEND_AST_NAME_LIST:
Expand Down Expand Up @@ -1789,13 +1835,17 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_NEW:
smart_str_appends(str, "new ");
if (ast->child[0]->kind == ZEND_AST_CLASS) {
zend_ast_decl *decl = (zend_ast_decl *) ast->child[0];
if (decl->attributes) {
zend_ast_export_attributes(str, decl->attributes, indent, 0);
}
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);
zend_ast_export_class_no_header(str, decl, indent);
} else {
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
smart_str_appendc(str, '(');
Expand Down Expand Up @@ -2006,6 +2056,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast_export_indent(str, indent);
break;
case ZEND_AST_PARAM:
if (ast->child[3]) {
zend_ast_export_attributes(str, ast->child[3], indent, 0);
}
if (ast->child[0]) {
zend_ast_export_type(str, ast->child[0], indent);
smart_str_appendc(str, ' ');
Expand Down
29 changes: 19 additions & 10 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#include "zend_API.h"
#include "zend_attributes.h"

ZEND_API zend_class_entry *zend_ce_php_attribute;
ZEND_API zend_class_entry *zend_ce_php_compiler_attribute;

static HashTable internal_validators;

void zend_attribute_validate_phpattribute(zend_attribute *attr, int target)
{
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
Expand All @@ -14,9 +19,22 @@ void zend_attribute_validate_phpcompilerattribute(zend_attribute *attr, int targ
zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead");
}

ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname)
{
return zend_hash_find_ptr(&internal_validators, lcname);
}

ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator)
{
zend_string *lcname = zend_string_tolower_ex(ce->name, 1);

zend_hash_update_ptr(&internal_validators, lcname, validator);
zend_string_release(lcname);
}

void zend_register_attribute_ce(void)
{
zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1);
zend_hash_init(&internal_validators, 8, NULL, NULL, 1);

zend_class_entry ce;

Expand All @@ -32,12 +50,3 @@ void zend_register_attribute_ce(void)

zend_compiler_attribute_register(zend_ce_php_compiler_attribute, zend_attribute_validate_phpcompilerattribute);
}

void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator)
{
zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1);

zend_hash_update_ptr(&zend_attributes_internal_validators, attribute_name, validator);

zend_string_release(attribute_name);
}
44 changes: 27 additions & 17 deletions Zend/zend_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
#define ZEND_ATTRIBUTE_TARGET_PARAMETER 32
#define ZEND_ATTRIBUTE_TARGET_ALL 63

zend_class_entry *zend_ce_php_attribute;
zend_class_entry *zend_ce_php_compiler_attribute;

#define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval))

BEGIN_EXTERN_C()

extern ZEND_API zend_class_entry *zend_ce_php_attribute;
extern ZEND_API zend_class_entry *zend_ce_php_compiler_attribute;

typedef struct _zend_attribute {
zend_string *name;
zend_string *lcname;
Expand All @@ -22,7 +24,9 @@ typedef struct _zend_attribute {
zval argv[1];
} zend_attribute;

static zend_always_inline void zend_attribute_release(zend_attribute *attr)
typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target);

static zend_always_inline void zend_attribute_free(zend_attribute *attr)
{
uint32_t i;

Expand All @@ -36,37 +40,43 @@ static zend_always_inline void zend_attribute_release(zend_attribute *attr)
efree(attr);
}

static zend_always_inline zend_bool zend_has_attribute(HashTable *attributes, zend_string *name, uint32_t offset)
static zend_always_inline zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *name, uint32_t offset)
{
if (attributes) {
zend_attribute *attr;

ZEND_HASH_FOREACH_PTR(attributes, attr) {
if (attr->offset == offset && zend_string_equals(attr->lcname, name)) {
return 1;
return attr;
}
} ZEND_HASH_FOREACH_END();
}

return 0;
return NULL;
}

static zend_always_inline zend_bool zend_has_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
static zend_always_inline zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
{
zend_bool result = 0;

if (attributes) {
zend_string *name = zend_string_init(str, len, 0);
result = zend_has_attribute(attributes, name, offset);
zend_string_release(name);
zend_attribute *attr;

ZEND_HASH_FOREACH_PTR(attributes, attr) {
if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) {
if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) {
return attr;
}
}
} ZEND_HASH_FOREACH_END();
}

return result;
return NULL;
}

typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target);
HashTable zend_attributes_internal_validators;
ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator);
ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname);

void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator);
void zend_register_attribute_ce(void);

END_EXTERN_C()

#endif
7 changes: 4 additions & 3 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5743,10 +5743,11 @@ static zend_attribute *zend_compile_attribute(zend_ast *ast, uint32_t offset) /*
}
/* }}} */

static void attribute_ptr_dtor(zval *v)
static void attribute_ptr_dtor(zval *v) /* {{{ */
{
zend_attribute_release((zend_attribute *) Z_PTR_P(v));
zend_attribute_free((zend_attribute *) Z_PTR_P(v));
}
/* }}} */

static zend_always_inline HashTable *create_attribute_array(uint32_t size) /* {{{ */
{
Expand All @@ -5772,7 +5773,7 @@ static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32
zend_attribute *attr = zend_compile_attribute(list->child[i], offset);

// Validate internal attribute
zend_attributes_internal_validator validator = zend_hash_find_ptr(&zend_attributes_internal_validators, attr->lcname);
zend_attributes_internal_validator validator = zend_attribute_get_validator(attr->lcname);

if (validator != NULL) {
validator(attr, target);
Expand Down
Loading