Skip to content

Commit f3be8d1

Browse files
committed
First look at shimming std property handlers
This works by pushing values from shared storage to local cache on read, and then letting std handlers operate on the local property table. Vice versa for write. This allows us to get support for stuff like 8.4 property hooks without copy pasting lots of code and having inconsistencies. this isn't fully stable yet, but should be working.
1 parent 2d0d49f commit f3be8d1

File tree

8 files changed

+319
-264
lines changed

8 files changed

+319
-264
lines changed

classes/thread_safe.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ ThreadSafe_method(synchronized)
8383

8484
if (pmmpthread_monitor_lock(&threaded->monitor)) {
8585
/* synchronize property tables */
86-
pmmpthread_store_sync_local_properties(Z_OBJ_P(getThis()));
86+
pmmpthread_store_clean_stale_cache(Z_OBJ_P(getThis()));
8787

8888
zend_try {
8989
/* call the closure */

src/handlers.c

+50-89
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ zval *pmmpthread_get_property_ptr_ptr_stub(zend_object *object, zend_string *mem
6666

6767
/* {{{ */
6868
zval* pmmpthread_read_dimension(PMMPTHREAD_READ_DIMENSION_PASSTHRU_D) {
69-
if (pmmpthread_store_read(object, member, type, rv) == FAILURE) {
69+
if (pmmpthread_store_read(object, member, NULL, type, rv) == FAILURE) {
7070
//TODO: this ought to generate warnings, but this is a pain right now due to key type juggling
7171
//for now this maintains the v4 behaviour of silently generating NULL, which is better than segfaulting
7272
if (!EG(exception)) {
@@ -81,39 +81,27 @@ zval* pmmpthread_read_dimension(PMMPTHREAD_READ_DIMENSION_PASSTHRU_D) {
8181

8282
zval* pmmpthread_read_property(PMMPTHREAD_READ_PROPERTY_PASSTHRU_D) {
8383
zval zmember;
84-
zend_guard* guard;
85-
86-
ZVAL_STR(&zmember, member);
84+
zval result;
8785

88-
if (object->ce->__get && (guard = zend_get_property_guard(object, member)) && !((*guard) & IN_GET)) {
89-
(*guard) |= IN_GET;
90-
zend_call_known_instance_method_with_1_params(object->ce->__get, object, rv, &zmember);
91-
(*guard) &= ~IN_GET;
86+
zend_property_info* info = zend_get_property_info(object->ce, member, 0);
87+
if (info != NULL && info != ZEND_WRONG_PROPERTY_INFO) {
88+
ZVAL_STR(&zmember, info->name);
9289
} else {
93-
zend_property_info* info = zend_get_property_info(object->ce, member, 0);
94-
if (info == ZEND_WRONG_PROPERTY_INFO) {
95-
rv = &EG(uninitialized_zval);
96-
} else if (info == NULL || !PMMPTHREAD_OBJECT_PROPERTY(info)) { //dynamic property
97-
if (pmmpthread_store_read(object, &zmember, type, rv) == FAILURE) {
98-
if (type != BP_VAR_IS) {
99-
zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(member));
100-
}
101-
rv = &EG(uninitialized_zval);
102-
}
103-
} else {
104-
//defined property, use mangled name
105-
ZVAL_STR(&zmember, info->name);
106-
107-
if (pmmpthread_store_read(object, &zmember, type, rv) == FAILURE) {
108-
if (type != BP_VAR_IS && !EG(exception)) {
109-
zend_throw_error(NULL, "Typed property %s::$%s must not be accessed before initialization",
110-
ZSTR_VAL(info->ce->name),
111-
ZSTR_VAL(member));
112-
}
113-
rv = &EG(uninitialized_zval);
114-
}
90+
ZVAL_STR(&zmember, member);
91+
}
92+
//this moves the value to cache for zend_std_read_property() to work on
93+
pmmpthread_store_read_ex(object, &zmember, info, type, rv, 1);
94+
if (EG(exception)) {
95+
rv = &EG(uninitialized_zval);
96+
} else {
97+
//no cache for now - we don't want the VM bypassing this handler
98+
zend_std_read_property(object, member, type, NULL, rv);
99+
//tidy property cache so we don't read wrong values later
100+
if (!pmmpthread_store_retain_in_local_cache(rv)) {
101+
pmmpthread_store_clean_local_property(object, &zmember, info);
115102
}
116103
}
104+
117105
return rv;
118106
}
119107
/* }}} */
@@ -129,7 +117,7 @@ zval* pmmpthread_read_property_deny(PMMPTHREAD_READ_PROPERTY_PASSTHRU_D) {
129117

130118
/* {{{ */
131119
void pmmpthread_write_dimension(PMMPTHREAD_WRITE_DIMENSION_PASSTHRU_D) {
132-
if (pmmpthread_store_write(object, member, value, PMMPTHREAD_STORE_NO_COERCE_ARRAY) == FAILURE && !EG(exception)){
120+
if (pmmpthread_store_write(object, member, NULL, value, PMMPTHREAD_STORE_NO_COERCE_ARRAY) == FAILURE && !EG(exception)){
133121
zend_throw_error(
134122
pmmpthread_ce_nts_value_error,
135123
"Cannot assign non-thread-safe value of type %s to %s",
@@ -144,56 +132,42 @@ zval* pmmpthread_write_property(PMMPTHREAD_WRITE_PROPERTY_PASSTHRU_D) {
144132
zval tmp;
145133
zend_guard* guard;
146134

147-
ZVAL_STR(&zmember, member);
148-
ZVAL_UNDEF(&tmp);
149-
150-
if (object->ce->__set && (guard = zend_get_property_guard(object, member)) && !((*guard) & IN_SET)) {
151-
zval rv;
152-
ZVAL_UNDEF(&rv);
135+
//no cache for now - cache would allow the VM to bypass this handler
136+
//std_write may coerce the var to a different type, so we need to use the result
137+
value = zend_std_write_property(object, member, value, NULL);
153138

154-
(*guard) |= IN_SET;
155-
zend_call_known_instance_method_with_2_params(object->ce->__set, object, &rv, &zmember, value);
156-
(*guard) &= ~IN_SET;
157-
158-
if (Z_TYPE(rv) != IS_UNDEF)
159-
zval_dtor(&rv);
160-
} else {
161-
bool ok = true;
139+
if (value != &EG(error_zval)) {
140+
zval* real_value = NULL;
141+
zval zmember;
142+
ZVAL_UNDEF(&zmember);
162143
zend_property_info* info = zend_get_property_info(object->ce, member, 0);
163-
if (info != ZEND_WRONG_PROPERTY_INFO) {
164-
if (info != NULL && PMMPTHREAD_OBJECT_PROPERTY(info)) {
165-
ZVAL_STR(&zmember, info->name); //use mangled name to avoid private member shadowing issues
166-
167-
zend_execute_data* execute_data = EG(current_execute_data);
168-
bool strict = execute_data
169-
&& execute_data->func
170-
&& ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data));
171-
172-
//zend_verify_property_type() might modify the value
173-
//value is not copied before we receive it, so it might be
174-
//from opcache protected memory which we can't modify
175-
ZVAL_COPY(&tmp, value);
176-
value = &tmp;
177-
178-
if (ZEND_TYPE_IS_SET(info->type) && !zend_verify_property_type(info, value, strict)) {
179-
ok = false;
180-
}
144+
if (info != NULL) {
145+
if (info != ZEND_WRONG_PROPERTY_INFO) {
146+
ZVAL_STR(&zmember, info->name);
147+
real_value = OBJ_PROP(object, info->offset);
181148
}
182-
183-
if (ok && pmmpthread_store_write(object, &zmember, value, PMMPTHREAD_STORE_NO_COERCE_ARRAY) == FAILURE && !EG(exception)) {
149+
} else if (object->properties != NULL) {
150+
ZVAL_STR(&zmember, member);
151+
real_value = zend_hash_find(object->properties, member);
152+
}
153+
if (real_value != NULL) {
154+
zend_bool cached = 0;
155+
if (pmmpthread_store_write_ex(object, &zmember, info, real_value, PMMPTHREAD_STORE_NO_COERCE_ARRAY, &cached) == FAILURE && !EG(exception)) {
184156
zend_throw_error(
185157
pmmpthread_ce_nts_value_error,
186158
"Cannot assign non-thread-safe value of type %s to thread-safe class property %s::$%s",
187159
zend_zval_type_name(value),
188160
ZSTR_VAL(object->ce->name),
189161
ZSTR_VAL(member)
190162
);
163+
value = &EG(error_zval);
164+
}
165+
if (!cached) {
166+
pmmpthread_store_clean_local_property(object, &zmember, info);
191167
}
192168
}
193169
}
194170

195-
zval_ptr_dtor(&tmp);
196-
197171
return EG(exception) ? &EG(error_zval) : value;
198172
}
199173
/* }}} */
@@ -249,34 +223,21 @@ int pmmpthread_has_property_deny(PMMPTHREAD_HAS_PROPERTY_PASSTHRU_D) {
249223

250224
/* {{{ */
251225
void pmmpthread_unset_dimension(PMMPTHREAD_UNSET_DIMENSION_PASSTHRU_D) {
252-
pmmpthread_store_delete(object, member);
226+
pmmpthread_store_delete(object, member, NULL);
253227
}
254228

255229
void pmmpthread_unset_property(PMMPTHREAD_UNSET_PROPERTY_PASSTHRU_D) {
256230
zval zmember;
257-
zend_guard* guard;
258-
259-
ZVAL_STR(&zmember, member);
260-
261-
if (object->ce->__unset && (guard = zend_get_property_guard(object, member)) && !((*guard) & IN_UNSET)) {
262-
zval rv;
263-
ZVAL_UNDEF(&rv);
264-
265-
(*guard) |= IN_UNSET;
266-
zend_call_known_instance_method_with_1_params(object->ce->__unset, object, &rv, &zmember);
267-
(*guard) &= ~IN_UNSET;
268-
269-
if (Z_TYPE(rv) != IS_UNDEF) {
270-
zval_dtor(&rv);
271-
}
272-
} else {
231+
232+
zend_std_unset_property(object, member, NULL);
233+
if (!EG(exception)) {
273234
zend_property_info* info = zend_get_property_info(object->ce, member, 0);
274-
if (info != ZEND_WRONG_PROPERTY_INFO) {
275-
if (info != NULL && PMMPTHREAD_OBJECT_PROPERTY(info)) {
276-
ZVAL_STR(&zmember, info->name); //defined property, use mangled name
277-
}
278-
pmmpthread_store_delete(object, &zmember);
235+
if (info != NULL && info != ZEND_WRONG_PROPERTY_INFO) {
236+
ZVAL_STR(&zmember, info->name);
237+
} else {
238+
ZVAL_STR(&zmember, member);
279239
}
240+
pmmpthread_store_delete(object, &zmember, info);
280241
}
281242
}
282243
/* }}} */

src/object.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ static inline void pmmpthread_base_write_property_defaults(pmmpthread_zend_objec
258258
value = OBJ_PROP(&base->std, info->offset);
259259
if (!Z_ISUNDEF_P(value)) {
260260
result = pmmpthread_store_write(
261-
&base->std, &key,
261+
&base->std,
262+
&key,
263+
info,
262264
value,
263265
PMMPTHREAD_STORE_NO_COERCE_ARRAY
264266
);

src/routine.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,15 @@ zend_bool pmmpthread_join(pmmpthread_zend_object_t* thread) {
242242

243243
//now, synchronize all object properties that may have been assigned by the thread
244244
if (pmmpthread_monitor_lock(&thread->ts_obj->monitor)) {
245-
pmmpthread_store_full_sync_local_properties(&thread->std);
245+
pmmpthread_store_cache_all(&thread->std);
246246
pmmpthread_monitor_unlock(&thread->ts_obj->monitor);
247247
}
248248
if (thread->worker_data != NULL) {
249249
pmmpthread_worker_sync_collectable_tasks(thread->worker_data);
250250
}
251251
pmmpthread_zend_object_t* user_globals = PMMPTHREAD_ZG(thread_shared_globals);
252252
if (pmmpthread_monitor_lock(&user_globals->ts_obj->monitor)) {
253-
pmmpthread_store_full_sync_local_properties(&user_globals->std);
253+
pmmpthread_store_cache_all(&user_globals->std);
254254
pmmpthread_monitor_unlock(&user_globals->ts_obj->monitor);
255255
}
256256

0 commit comments

Comments
 (0)