Skip to content

Commit

Permalink
anonymous class support, fix krakjoe#505 krakjoe#658 krakjoe#659
Browse files Browse the repository at this point in the history
  • Loading branch information
sirsnyder committed Mar 5, 2017
1 parent aa5f7f7 commit c28c4b9
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 42 deletions.
99 changes: 57 additions & 42 deletions src/prepare.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ static void pthreads_prepared_resource_dtor(zval *zv); /* }}} */
static void pthreads_prepared_entry_static_members(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
if (candidate->default_static_members_count) {
int i;
if(prepared->default_static_members_table != NULL) {
efree(prepared->default_static_members_table);
}
prepared->default_static_members_table = (zval*) ecalloc(
sizeof(zval), candidate->default_static_members_count);
prepared->default_static_members_count = candidate->default_static_members_count;
Expand All @@ -67,27 +70,9 @@ static void pthreads_prepared_entry_static_members(pthreads_object_t* thread, ze
} else prepared->default_static_members_count = 0;
} /* }}} */

/* {{{ */
static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_class_entry *candidate, zend_bool prepare_static_members) {
zend_class_entry *prepared;

if (candidate->ce_flags & ZEND_ACC_ANON_CLASS) {
if (!(candidate->ce_flags & ZEND_ACC_ANON_BOUND)) {
return NULL;
}
}

prepared = (zend_class_entry*) emalloc(sizeof(zend_class_entry));
prepared->name = zend_string_new(candidate->name);
prepared->type = candidate->type;

zend_initialize_class_data(prepared, 1);

zend_hash_index_update_ptr(&PTHREADS_ZG(resolve), (zend_ulong) candidate, prepared);

prepared->ce_flags = candidate->ce_flags;
prepared->refcount = 1;

/* {{{ */
static zend_class_entry* pthreads_complete_entry(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared, zend_bool prepare_static_members) {
if (candidate->parent) {
if (zend_hash_index_exists(&PTHREADS_ZG(resolve), (zend_ulong) candidate->parent)) {
prepared->parent = zend_hash_index_find_ptr(&PTHREADS_ZG(resolve), (zend_ulong) candidate->parent);
Expand Down Expand Up @@ -175,7 +160,7 @@ static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_cla
case 5: prepared->get_static_method = candidate->get_static_method; break;
}
}
} while(++umethod < 7);
} while(++umethod < 6);
}

{
Expand All @@ -184,9 +169,13 @@ static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_cla

ZEND_HASH_FOREACH_STR_KEY_PTR(&candidate->function_table, key, value) {
zend_string *name = zend_string_new(key);
value = pthreads_copy_function(value);
zend_hash_add_ptr(
&prepared->function_table, name, value);

if (!zend_hash_exists(&prepared->function_table, name)) {
value = pthreads_copy_function(value);

zend_hash_add_ptr(
&prepared->function_table, name, value);
}
zend_string_release(name);
} ZEND_HASH_FOREACH_END();
}
Expand All @@ -198,10 +187,10 @@ static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_cla
if ((func = zend_hash_str_find_ptr(&prepared->function_table, "__construct", sizeof("__construct")-1))) {
prepared->constructor = func;
} else {
if ((func = zend_hash_find_ptr(&prepared->function_table, prepared->name))) {
prepared->constructor = func;
if ((func = zend_hash_find_ptr(&prepared->function_table, prepared->name))) {
prepared->constructor = func;
}
}
}
}

#define FIND_AND_SET(f, n) do {\
Expand Down Expand Up @@ -247,14 +236,14 @@ while(0)
} else dup.ce = pthreads_prepared_entry(thread, info->ce);
}

if (!zend_hash_str_add_mem(&prepared->properties_info, name->val, name->len, &dup, sizeof(zend_property_info))) {
if (!zend_hash_str_add_mem(&prepared->properties_info, name->val, name->len, &dup, sizeof(zend_property_info))) {
zend_string_release(dup.name);
if (dup.doc_comment)
zend_string_release(dup.doc_comment);
}
} ZEND_HASH_FOREACH_END();
}

#define SET_ITERATOR_FUNC(f) do { \
if (candidate->iterator_funcs.f) { \
prepared->iterator_funcs.f = zend_hash_index_find_ptr( \
Expand All @@ -276,6 +265,10 @@ while(0)

if (candidate->default_properties_count) {
int i;

if(prepared->default_properties_table != NULL) {
efree(prepared->default_properties_table);
}
prepared->default_properties_table = emalloc(
sizeof(zval) * candidate->default_properties_count);

Expand All @@ -298,16 +291,6 @@ while(0)
pthreads_prepared_entry_static_members(thread, candidate, prepared);
}

memcpy(&prepared->info.user, &candidate->info.user, sizeof(candidate->info.user));

if ((thread->options & PTHREADS_INHERIT_COMMENTS) &&
(candidate->info.user.doc_comment)) {
prepared->info.user.doc_comment = zend_string_new(candidate->info.user.doc_comment);
} else prepared->info.user.doc_comment = NULL;

if (prepared->info.user.filename)
prepared->info.user.filename = zend_string_new(candidate->info.user.filename);

{
zend_string *key;
zval *value;
Expand Down Expand Up @@ -355,10 +338,39 @@ while(0)
zend_string_release(name);
} ZEND_HASH_FOREACH_END();
}

return prepared;
} /* }}} */

/* {{{ */
static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_class_entry *candidate, zend_bool prepare_static_members) {
zend_class_entry *prepared;

prepared = (zend_class_entry*) emalloc(sizeof(zend_class_entry));
prepared->name = zend_string_new(candidate->name);
prepared->type = candidate->type;

zend_initialize_class_data(prepared, 1);

zend_hash_index_update_ptr(&PTHREADS_ZG(resolve), (zend_ulong) candidate, prepared);

prepared->ce_flags = candidate->ce_flags;
prepared->refcount = 1;

memcpy(&prepared->info.user, &candidate->info.user, sizeof(candidate->info.user));

if ((thread->options & PTHREADS_INHERIT_COMMENTS) &&
(candidate->info.user.doc_comment)) {
prepared->info.user.doc_comment = zend_string_new(candidate->info.user.doc_comment);
} else prepared->info.user.doc_comment = NULL;

if (prepared->info.user.filename) {
prepared->info.user.filename = zend_string_new(candidate->info.user.filename);
}

return pthreads_complete_entry(thread, candidate, prepared, prepare_static_members);
} /* }}} */

/* {{{ */
static inline int pthreads_prepared_entry_function_prepare(zval *bucket, int argc, va_list argv, zend_hash_key *key) {
zend_function *function = (zend_function*) Z_PTR_P(bucket);
Expand Down Expand Up @@ -405,11 +417,14 @@ zend_class_entry* pthreads_prepared_entry_internal(pthreads_object_t* thread, ze
if (candidate->type == ZEND_INTERNAL_CLASS) {
return zend_lookup_class(candidate->name);
}

lookup = zend_string_tolower(candidate->name);

if ((prepared = zend_hash_find_ptr(EG(class_table), lookup))) {
zend_string_release(lookup);
zend_string_release(lookup);

if(prepared->create_object == NULL && candidate->create_object != NULL) {
return pthreads_complete_entry(thread, candidate, prepared, 1);
}
return prepared;
}

Expand Down
100 changes: 100 additions & 0 deletions tests/inherited-anon-class-outside-context.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
--TEST--
Basic anonymous class support, fix #505
--DESCRIPTION--
Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
--FILE--
<?php
class Test extends Thread {
/**
* doccomment run
*/
public function run() {
$this->alive = true;

/**
* doccomment anonymous
*/
$this->anonymous = new class extends Thread {
const CONSTANT = 'constant';

/**
* @var
*/
public $pubProp;

protected $protProp;

private $privProp;

public static $staticProp;

public function run() {
var_dump('anonymous run');
$this->ready = true;
}

public function method() {
var_dump('method executed');
}

public static function staticMethod() {}
};

var_dump($this->anonymous);

$this->anonymous->start();
$this->anonymous->join();

while($this->alive) {}
}
}
$test = new Test();
$test->start();

while(true) {
if(isset($test->anonymous, $test->anonymous->ready)) {
var_dump($test->anonymous);

$test->anonymous->run();
$test->anonymous->method();
/*
$class = new ReflectionClass($test->anonymous);
var_dump($class);
var_dump($class->getConstants());
//var_dump($class->getMethods());
var_dump($class->getProperties());
$class->setStaticPropertyValue('staticProp', 'property injected');
var_dump($class->getStaticProperties());
*/
$test->alive = false;
break;
}
}
$test->join();
--EXPECT--
object(class@anonymous)#2 (3) {
["pubProp"]=>
NULL
["protProp"]=>
NULL
["privProp"]=>
NULL
}
string(13) "anonymous run"
object(class@anonymous)#2 (4) {
["pubProp"]=>
NULL
["protProp"]=>
NULL
["privProp"]=>
NULL
["ready"]=>
bool(true)
}
string(13) "anonymous run"
string(15) "method executed"
20 changes: 20 additions & 0 deletions tests/inherited-anon-class.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Test fix for #658 with inheritance
--DESCRIPTION--
Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
--FILE--
<?php
$task = new class extends Thread {

public function run()
{
$this->prop = new class extends Threaded {};

var_dump($this->prop);
}
};

$task->start() && $task->join();
--EXPECT--
object(class@anonymous)#2 (0) {
}
19 changes: 19 additions & 0 deletions tests/no-inheritance-anon-class.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Test fix for #659
--DESCRIPTION--
Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
--FILE--
<?php
$task = new class extends Thread {

public function run()
{
$this->prop = new class {};

var_dump($this->prop); /* we do expect null: anon classes cannot be serialized */
}
};

$task->start() && $task->join();
--EXPECT--
NULL

0 comments on commit c28c4b9

Please sign in to comment.