diff --git a/.gitignore b/.gitignore index dbbec532115..4e909aecea4 100644 --- a/.gitignore +++ b/.gitignore @@ -139,6 +139,8 @@ composer.lock php/tests/generated/ php/tests/old_protoc php/tests/protobuf/ +php/tests/core +php/tests/vgcore* php/ext/google/protobuf/.libs/ php/ext/google/protobuf/Makefile.fragments php/ext/google/protobuf/Makefile.global diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c index e69bef42998..b52bdf62ca7 100644 --- a/php/ext/google/protobuf/array.c +++ b/php/ext/google/protobuf/array.c @@ -259,6 +259,19 @@ void repeated_field_push_native(RepeatedField *intern, void *value) { } } +void repeated_field_ensure_created( + const upb_fielddef *field, + CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) { + if (ZVAL_IS_NULL(CACHED_PTR_TO_ZVAL_PTR(repeated_field))) { + zval_ptr_dtor(repeated_field); +#if PHP_MAJOR_VERSION < 7 + MAKE_STD_ZVAL(CACHED_PTR_TO_ZVAL_PTR(repeated_field)); +#endif + repeated_field_create_with_field(repeated_field_type, field, + repeated_field PHP_PROTO_TSRMLS_CC); + } +} + void repeated_field_create_with_field( zend_class_entry *ce, const upb_fielddef *field, CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) { diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 166fb91baba..39304a57113 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -139,6 +139,15 @@ static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) { return hd_ofs; } +static const void* newhandlerfielddata( + upb_handlers* h, const upb_fielddef* field) { + const void** hd_field = malloc(sizeof(void*)); + PHP_PROTO_ASSERT(hd_field != NULL); + *hd_field = field; + upb_handlers_addcleanup(h, hd_field, free); + return hd_field; +} + typedef struct { void* closure; stringsink sink; @@ -163,16 +172,18 @@ static const void *newunknownfieldshandlerdata(upb_handlers* h) { } typedef struct { + const upb_fielddef *fd; size_t ofs; const upb_msgdef *md; } submsg_handlerdata_t; -// Creates a handlerdata that contains offset and submessage type information. +// Creates a handlerdata that contains field and submessage type information. static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, const upb_fielddef* f) { submsg_handlerdata_t* hd = (submsg_handlerdata_t*)malloc(sizeof(submsg_handlerdata_t)); PHP_PROTO_ASSERT(hd != NULL); + hd->fd = f; hd->ofs = ofs; hd->md = upb_fielddef_msgsubdef(f); upb_handlers_addcleanup(h, hd, free); @@ -221,8 +232,11 @@ static const void *newoneofhandlerdata(upb_handlers *h, // this field (such an instance always exists even in an empty message). static void *startseq_handler(void* closure, const void* hd) { MessageHeader* msg = closure; - const size_t *ofs = hd; - return CACHED_PTR_TO_ZVAL_PTR(DEREF(message_data(msg), *ofs, CACHED_VALUE*)); + const upb_fielddef** field = (const upb_fielddef**) hd; + CACHED_VALUE* cache = find_zval_property(msg, *field); + TSRMLS_FETCH(); + repeated_field_ensure_created(*field, cache PHP_PROTO_TSRMLS_CC); + return CACHED_PTR_TO_ZVAL_PTR(cache); } // Handlers that append primitive values to a repeated field. @@ -322,15 +336,6 @@ static void *empty_php_string(zval* value_ptr) { } #endif #if PHP_MAJOR_VERSION < 7 -static void *empty_php_string2(zval** value_ptr) { - SEPARATE_ZVAL_IF_NOT_REF(value_ptr); - if (Z_TYPE_PP(value_ptr) == IS_STRING && - !IS_INTERNED(Z_STRVAL_PP(value_ptr))) { - FREE(Z_STRVAL_PP(value_ptr)); - } - ZVAL_EMPTY_STRING(*value_ptr); - return (void*)(*value_ptr); -} static void new_php_string(zval** value_ptr, const char* str, size_t len) { SEPARATE_ZVAL_IF_NOT_REF(value_ptr); if (Z_TYPE_PP(value_ptr) == IS_STRING && @@ -340,13 +345,6 @@ static void new_php_string(zval** value_ptr, const char* str, size_t len) { ZVAL_STRINGL(*value_ptr, str, len, 1); } #else -static void *empty_php_string2(zval* value_ptr) { - if (Z_TYPE_P(value_ptr) == IS_STRING) { - zend_string_release(Z_STR_P(value_ptr)); - } - ZVAL_EMPTY_STRING(value_ptr); - return value_ptr; -} static void new_php_string(zval* value_ptr, const char* str, size_t len) { if (Z_TYPE_P(value_ptr) == IS_STRING) { zend_string_release(Z_STR_P(value_ptr)); @@ -371,6 +369,21 @@ static void* str_handler(void *closure, } static bool str_end_handler(void *closure, const void *hd) { + stringfields_parseframe_t* frame = closure; + const upb_fielddef **field = (const upb_fielddef **) hd; + MessageHeader* msg = (MessageHeader*)frame->closure; + + CACHED_VALUE* cached = find_zval_property(msg, *field); + + new_php_string(cached, frame->sink.ptr, frame->sink.len); + + stringsink_uninit(&frame->sink); + free(frame); + + return true; +} + +static bool map_str_end_handler(void *closure, const void *hd) { stringfields_parseframe_t* frame = closure; const size_t *ofs = hd; MessageHeader* msg = (MessageHeader*)frame->closure; @@ -430,26 +443,60 @@ static void *submsg_handler(void *closure, const void *hd) { zval* submsg_php; MessageHeader* submsg; - if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(message_data(msg), submsgdata->ofs, - CACHED_VALUE*))) == IS_NULL) { + CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { #if PHP_MAJOR_VERSION < 7 zval val; ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC)); MessageHeader* intern = UNBOX(MessageHeader, &val); custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); - REPLACE_ZVAL_VALUE(DEREF(message_data(msg), submsgdata->ofs, zval**), - &val, 1); + REPLACE_ZVAL_VALUE(cached, &val, 1); zval_dtor(&val); #else zend_object* obj = subklass->create_object(subklass TSRMLS_CC); - ZVAL_OBJ(DEREF(message_data(msg), submsgdata->ofs, zval*), obj); + ZVAL_OBJ(cached, obj); MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj); custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); #endif } - submsg_php = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*)); + submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); + + submsg = UNBOX(MessageHeader, submsg_php); + return submsg; +} + +static void *map_submsg_handler(void *closure, const void *hd) { + MessageHeader* msg = closure; + const submsg_handlerdata_t* submsgdata = hd; + TSRMLS_FETCH(); + Descriptor* subdesc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md)); + zend_class_entry* subklass = subdesc->klass; + zval* submsg_php; + MessageHeader* submsg; + + CACHED_VALUE* cached = + DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) { +#if PHP_MAJOR_VERSION < 7 + zval val; + ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC)); + MessageHeader* intern = UNBOX(MessageHeader, &val); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); + REPLACE_ZVAL_VALUE(cached, &val, 1); + zval_dtor(&val); +#else + zend_object* obj = subklass->create_object(subklass TSRMLS_CC); + ZVAL_OBJ(cached, obj); + MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj); + custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC); +#endif + } + + submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached); submsg = UNBOX(MessageHeader, submsg_php); return submsg; @@ -457,7 +504,7 @@ static void *submsg_handler(void *closure, const void *hd) { // Handler data for startmap/endmap handlers. typedef struct { - size_t ofs; + const upb_fielddef* fd; const upb_msgdef* value_md; upb_fieldtype_t key_field_type; upb_fieldtype_t value_field_type; @@ -612,9 +659,10 @@ static void map_slot_value(upb_fieldtype_t type, const void* from, static void *startmapentry_handler(void *closure, const void *hd) { MessageHeader* msg = closure; const map_handlerdata_t* mapdata = hd; + CACHED_VALUE* cache = find_zval_property(msg, mapdata->fd); TSRMLS_FETCH(); - zval* map = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), mapdata->ofs, CACHED_VALUE*)); + map_field_ensure_created(mapdata->fd, cache PHP_PROTO_TSRMLS_CC); + zval* map = CACHED_PTR_TO_ZVAL_PTR(cache); map_parse_frame_t* frame = ALLOC(map_parse_frame_t); frame->data = ALLOC(map_parse_frame_data_t); @@ -662,7 +710,7 @@ static bool endmap_handler(void* closure, const void* hd, upb_status* s) { // key/value and endmsg handlers. The reason is that there is no easy way to // pass the handlerdata down to the sub-message handler setup. static map_handlerdata_t* new_map_handlerdata( - size_t ofs, + const upb_fielddef* field, const upb_msgdef* mapentry_def, Descriptor* desc) { const upb_fielddef* key_field; @@ -671,7 +719,7 @@ static map_handlerdata_t* new_map_handlerdata( map_handlerdata_t* hd = (map_handlerdata_t*)malloc(sizeof(map_handlerdata_t)); PHP_PROTO_ASSERT(hd != NULL); - hd->ofs = ofs; + hd->fd = field; key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD); PHP_PROTO_ASSERT(key_field != NULL); hd->key_field_type = upb_fielddef_type(key_field); @@ -844,7 +892,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, const upb_fielddef *f, size_t offset) { upb_handlerattr attr = UPB_HANDLERATTR_INIT; - attr.handler_data = newhandlerdata(h, offset); + attr.handler_data = newhandlerfielddata(h, f); upb_handlers_setstartseq(h, f, startseq_handler, &attr); switch (upb_fielddef_type(f)) { @@ -884,7 +932,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, // Set up handlers for a singular field. static void add_handlers_for_singular_field(upb_handlers *h, const upb_fielddef *f, - size_t offset) { + size_t offset, bool is_map) { switch (upb_fielddef_type(f)) { #define SET_HANDLER(utype, ltype) \ case utype: { \ @@ -908,16 +956,29 @@ static void add_handlers_for_singular_field(upb_handlers *h, case UPB_TYPE_STRING: case UPB_TYPE_BYTES: { upb_handlerattr attr = UPB_HANDLERATTR_INIT; - attr.handler_data = newhandlerdata(h, offset); + if (is_map) { + attr.handler_data = newhandlerdata(h, offset); + } else { + attr.handler_data = newhandlerfielddata(h, f); + } upb_handlers_setstartstr(h, f, str_handler, &attr); upb_handlers_setstring(h, f, stringdata_handler, &attr); - upb_handlers_setendstr(h, f, str_end_handler, &attr); + if (is_map) { + upb_handlers_setendstr(h, f, map_str_end_handler, &attr); + } else { + upb_handlers_setendstr(h, f, str_end_handler, &attr); + } break; } case UPB_TYPE_MESSAGE: { upb_handlerattr attr = UPB_HANDLERATTR_INIT; - attr.handler_data = newsubmsghandlerdata(h, offset, f); - upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); + if (is_map) { + attr.handler_data = newsubmsghandlerdata(h, offset, f); + upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr); + } else { + attr.handler_data = newsubmsghandlerdata(h, 0, f); + upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); + } break; } } @@ -929,7 +990,7 @@ static void add_handlers_for_mapfield(upb_handlers* h, size_t offset, Descriptor* desc) { const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef); - map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc); + map_handlerdata_t* hd = new_map_handlerdata(fielddef, map_msgdef, desc); upb_handlerattr attr = UPB_HANDLERATTR_INIT; upb_handlers_addcleanup(h, hd, free); @@ -951,10 +1012,10 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h, add_handlers_for_singular_field(h, key_field, offsetof(map_parse_frame_data_t, - key_storage)); + key_storage), true); add_handlers_for_singular_field(h, value_field, offsetof(map_parse_frame_data_t, - value_storage)); + value_storage), true); } // Set up handlers for a oneof field. @@ -1063,7 +1124,7 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) { } else if (upb_fielddef_isseq(f)) { add_handlers_for_repeated_field(h, f, offset); } else { - add_handlers_for_singular_field(h, f, offset); + add_handlers_for_singular_field(h, f, offset, false); } } } @@ -1259,16 +1320,13 @@ static void putjsonany(MessageHeader* msg, const Descriptor* desc, const upb_fielddef* type_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_TYPE); const upb_fielddef* value_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_VALUE); - uint32_t type_url_offset; zval* type_url_php_str; const upb_msgdef *payload_type = NULL; upb_sink_startmsg(sink); /* Handle type url */ - type_url_offset = desc->layout->fields[upb_fielddef_index(type_field)].offset; - type_url_php_str = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), type_url_offset, CACHED_VALUE*)); + type_url_php_str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, type_field)); if (Z_STRLEN_P(type_url_php_str) > 0) { putstr(type_url_php_str, type_field, sink, false); } @@ -1294,14 +1352,11 @@ static void putjsonany(MessageHeader* msg, const Descriptor* desc, } { - uint32_t value_offset; zval* value_php_str; const char* value_str; size_t value_len; - value_offset = desc->layout->fields[upb_fielddef_index(value_field)].offset; - value_php_str = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), value_offset, CACHED_VALUE*)); + value_php_str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, value_field)); value_str = Z_STRVAL_P(value_php_str); value_len = Z_STRLEN_P(value_php_str); @@ -1355,17 +1410,21 @@ static void putjsonlistvalue( upb_sink_startmsg(sink); - array = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - intern = UNBOX(RepeatedField, array); - ht = PHP_PROTO_HASH_OF(intern->array); - size = zend_hash_num_elements(ht); - - if (size == 0) { + array = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (ZVAL_IS_NULL(array)) { upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); } else { - putarray(array, f, sink, depth, true TSRMLS_CC); + intern = UNBOX(RepeatedField, array); + ht = PHP_PROTO_HASH_OF(intern->array); + size = zend_hash_num_elements(ht); + + if (size == 0) { + upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); + upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); + } else { + putarray(array, f, sink, depth, true TSRMLS_CC); + } } upb_sink_endmsg(sink, &status); @@ -1384,16 +1443,20 @@ static void putjsonstruct( upb_sink_startmsg(sink); - map = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - intern = UNBOX(Map, map); - size = upb_strtable_count(&intern->table); - - if (size == 0) { + map = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (ZVAL_IS_NULL(map)) { upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); } else { - putmap(map, f, sink, depth, true TSRMLS_CC); + intern = UNBOX(Map, map); + size = upb_strtable_count(&intern->table); + + if (size == 0) { + upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); + upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); + } else { + putmap(map, f, sink, depth, true TSRMLS_CC); + } } upb_sink_endmsg(sink, &status); @@ -1455,28 +1518,24 @@ static void putrawmsg(MessageHeader* msg, const Descriptor* desc, } if (is_map_field(f)) { - zval* map = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - if (map != NULL) { + zval* map = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (!ZVAL_IS_NULL(map)) { putmap(map, f, sink, depth, is_json TSRMLS_CC); } } else if (upb_fielddef_isseq(f)) { - zval* array = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - if (array != NULL) { + zval* array = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (!ZVAL_IS_NULL(array)) { putarray(array, f, sink, depth, is_json TSRMLS_CC); } } else if (upb_fielddef_isstring(f)) { - zval* str = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); + zval* str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); if (containing_oneof || (is_json && is_wrapper_msg(desc->msgdef)) || Z_STRLEN_P(str) > 0) { putstr(str, f, sink, is_json && is_wrapper_msg(desc->msgdef)); } } else if (upb_fielddef_issubmsg(f)) { - putsubmsg(CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)), - f, sink, depth, is_json TSRMLS_CC); + zval* submsg = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC); } else { upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); @@ -1847,9 +1906,8 @@ static void discard_unknown_fields(MessageHeader* msg) { value_field = map_field_value(f); if (!upb_fielddef_issubmsg(value_field)) continue; - zval* map_php = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - if (map_php == NULL) continue; + zval* map_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (ZVAL_IS_NULL(map_php)) continue; Map* intern = UNBOX(Map, map_php); for (map_begin(map_php, &map_it TSRMLS_CC); @@ -1868,9 +1926,8 @@ static void discard_unknown_fields(MessageHeader* msg) { } else if (upb_fielddef_isseq(f)) { if (!upb_fielddef_issubmsg(f)) continue; - zval* array_php = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); - if (array_php == NULL) continue; + zval* array_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); + if (ZVAL_IS_NULL(array_php)) continue; int size, i; RepeatedField* intern = UNBOX(RepeatedField, array_php); @@ -1890,8 +1947,7 @@ static void discard_unknown_fields(MessageHeader* msg) { discard_unknown_fields(submsg); } } else if (upb_fielddef_issubmsg(f)) { - zval* submsg_php = CACHED_PTR_TO_ZVAL_PTR( - DEREF(message_data(msg), offset, CACHED_VALUE*)); + zval* submsg_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f)); if (Z_TYPE_P(submsg_php) == IS_NULL) continue; MessageHeader* submsg = UNBOX(MessageHeader, submsg_php); discard_unknown_fields(submsg); diff --git a/php/ext/google/protobuf/map.c b/php/ext/google/protobuf/map.c index 0ce10190e6a..2764788b469 100644 --- a/php/ext/google/protobuf/map.c +++ b/php/ext/google/protobuf/map.c @@ -243,6 +243,18 @@ map_field_handlers->write_dimension = map_field_write_dimension; map_field_handlers->get_gc = map_field_get_gc; PHP_PROTO_INIT_CLASS_END +void map_field_ensure_created(const upb_fielddef *field, + CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) { + if (ZVAL_IS_NULL(CACHED_PTR_TO_ZVAL_PTR(map_field))) { + zval_ptr_dtor(map_field); +#if PHP_MAJOR_VERSION < 7 + MAKE_STD_ZVAL(CACHED_PTR_TO_ZVAL_PTR(map_field)); +#endif + map_field_create_with_field(map_field_type, field, + map_field PHP_PROTO_TSRMLS_CC); + } +} + void map_field_create_with_field(const zend_class_entry *ce, const upb_fielddef *field, CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) { diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 303f5d4741f..03dec75928e 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -178,7 +178,7 @@ static zval* message_get_property_internal(zval* object, zend_get_property_info(Z_OBJCE_P(object), Z_STR_P(member), true); #endif return layout_get( - self->descriptor->layout, message_data(self), field, + self->descriptor->layout, self, field, OBJ_PROP(Z_OBJ_P(object), property_info->offset) TSRMLS_CC); } @@ -191,7 +191,7 @@ static void message_get_oneof_property_internal(zval* object, zval* member, return; } - layout_get(self->descriptor->layout, message_data(self), field, + layout_get(self->descriptor->layout, self, field, ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC); } @@ -255,7 +255,6 @@ void custom_data_init(const zend_class_entry* ce, MessageHeader* intern PHP_PROTO_TSRMLS_DC) { Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(ce)); intern->data = ALLOC_N(uint8_t, desc->layout->size); - memset(message_data(intern), 0, desc->layout->size); // We wrap first so that everything in the message object is GC-rooted in // case a collection happens during object creation in layout_init(). intern->descriptor = desc; @@ -575,9 +574,9 @@ PHP_METHOD(Message, readOneof) { const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index); // Unlike singular fields, oneof fields share cached property. So we cannot - // let lay_get modify the cached property. Instead, we pass in the return + // let layout_get modify the cached property. Instead, we pass in the return // value directly. - layout_get(msg->descriptor->layout, message_data(msg), field, + layout_get(msg->descriptor->layout, msg, field, ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC); } diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 4abfecbdf51..86bc5b3317b 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -200,7 +200,7 @@ #define CACHED_VALUE zval* #define CACHED_TO_ZVAL_PTR(VALUE) (VALUE) -#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (*VALUE) +#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (*(CACHED_VALUE*)(VALUE)) #define ZVAL_PTR_TO_CACHED_PTR(VALUE) (&VALUE) #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (VALUE) #define ZVAL_TO_CACHED_VALUE(VALUE) (&VALUE) @@ -475,7 +475,7 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht, #define CACHED_VALUE zval #define CACHED_TO_ZVAL_PTR(VALUE) (&VALUE) -#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (VALUE) +#define CACHED_PTR_TO_ZVAL_PTR(VALUE) ((CACHED_VALUE*)(VALUE)) #define ZVAL_PTR_TO_CACHED_PTR(VALUE) (VALUE) #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (*VALUE) #define ZVAL_TO_CACHED_VALUE(VALUE) (VALUE) @@ -935,6 +935,7 @@ struct MessageField { struct MessageLayout { const upb_msgdef* msgdef; + void* empty_template; // Can memcpy() onto a layout to clear it. MessageField* fields; size_t size; }; @@ -948,7 +949,7 @@ PHP_PROTO_WRAP_OBJECT_END MessageLayout* create_layout(const upb_msgdef* msgdef); void layout_init(MessageLayout* layout, void* storage, zend_object* object PHP_PROTO_TSRMLS_DC); -zval* layout_get(MessageLayout* layout, const void* storage, +zval* layout_get(MessageLayout* layout, MessageHeader* header, const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC); void layout_set(MessageLayout* layout, MessageHeader* header, const upb_fielddef* field, zval* val TSRMLS_DC); @@ -1089,6 +1090,8 @@ upb_value map_iter_value(MapIter* iter, int* len); const upb_fielddef* map_entry_key(const upb_msgdef* msgdef); const upb_fielddef* map_entry_value(const upb_msgdef* msgdef); +void map_field_ensure_created(const upb_fielddef *field, + CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC); void map_field_create_with_field(const zend_class_entry* ce, const upb_fielddef* field, CACHED_VALUE* map_field PHP_PROTO_TSRMLS_DC); @@ -1147,6 +1150,9 @@ PHP_PROTO_WRAP_OBJECT_START(RepeatedFieldIter) long position; PHP_PROTO_WRAP_OBJECT_END +void repeated_field_ensure_created( + const upb_fielddef *field, + CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC); void repeated_field_create_with_field( zend_class_entry* ce, const upb_fielddef* field, CACHED_VALUE* repeated_field PHP_PROTO_TSRMLS_DC); @@ -1489,6 +1495,9 @@ size_t stringsink_string(void *_sink, const void *hd, const char *ptr, #define FREE(object) efree(object) #define PEFREE(object) pefree(object, 1) +// Find corresponding zval property for the field. +CACHED_VALUE* find_zval_property(MessageHeader* msg, const upb_fielddef* field); + // String argument. #define STR(str) (str), strlen(str) diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 1c28b1c1855..e6050d0eb3e 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -75,11 +75,9 @@ static bool native_slot_is_default(upb_fieldtype_t type, const void* memory) { #undef CASE_TYPE case UPB_TYPE_STRING: case UPB_TYPE_BYTES: - return Z_STRLEN_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(memory, CACHED_VALUE*))) == - 0; + return Z_STRLEN_P(CACHED_PTR_TO_ZVAL_PTR(memory)) == 0; case UPB_TYPE_MESSAGE: - return Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(memory, CACHED_VALUE*))) == - IS_NULL; + return Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(memory)) == IS_NULL; default: return false; } } @@ -599,6 +597,8 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { // Reserve space for unknown fields. off += sizeof(void*); + layout->empty_template = NULL; + TSRMLS_FETCH(); Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msgdef)); layout->fields = ALLOC_N(MessageField, nfields); @@ -744,64 +744,35 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { layout->size = off; layout->msgdef = msgdef; + // Create the empty message template. + layout->empty_template = ALLOC_N(char, layout->size); + memset(layout->empty_template, 0, layout->size); + return layout; } void free_layout(MessageLayout* layout) { + FREE(layout->empty_template); FREE(layout->fields); FREE(layout); } void layout_init(MessageLayout* layout, void* storage, zend_object* object PHP_PROTO_TSRMLS_DC) { - int i; - upb_msg_field_iter it; - - // Init unknown fields - memset(storage, 0, sizeof(void*)); - - for (upb_msg_field_begin(&it, layout->msgdef), i = 0; !upb_msg_field_done(&it); - upb_msg_field_next(&it), i++) { - const upb_fielddef* field = upb_msg_iter_field(&it); - void* memory = slot_memory(layout, storage, field); - uint32_t* oneof_case = slot_oneof_case(layout, storage, field); - int cache_index = slot_property_cache(layout, storage, field); - CACHED_VALUE* property_ptr = OBJ_PROP(object, cache_index); - - if (upb_fielddef_containingoneof(field)) { - memset(memory, 0, NATIVE_SLOT_MAX_SIZE); - *oneof_case = ONEOF_CASE_NONE; - } else if (is_map_field(field)) { - zval_ptr_dtor(property_ptr); -#if PHP_MAJOR_VERSION < 7 - MAKE_STD_ZVAL(*property_ptr); -#endif - map_field_create_with_field(map_field_type, field, - property_ptr PHP_PROTO_TSRMLS_CC); - DEREF(memory, CACHED_VALUE*) = property_ptr; - } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - zval_ptr_dtor(property_ptr); -#if PHP_MAJOR_VERSION < 7 - MAKE_STD_ZVAL(*property_ptr); -#endif - repeated_field_create_with_field(repeated_field_type, field, - property_ptr PHP_PROTO_TSRMLS_CC); - DEREF(memory, CACHED_VALUE*) = property_ptr; - } else { - native_slot_init(upb_fielddef_type(field), memory, property_ptr); - } - } + memcpy(storage, layout->empty_template, layout->size); } -// For non-singular fields, the related memory needs to point to the actual -// zval in properties table first. -static void* value_memory(const upb_fielddef* field, void* memory) { - switch (upb_fielddef_type(field)) { +// Switch memory for processing for singular fields based on field type. +// * primitive fields: memory +// * others (string, bytes and message): cache (the correspond zval +// property) +static void* value_memory( + upb_fieldtype_t type, void* memory, CACHED_VALUE* cache) { + switch (type) { case UPB_TYPE_STRING: case UPB_TYPE_BYTES: case UPB_TYPE_MESSAGE: - memory = DEREF(memory, CACHED_VALUE*); - break; + return cache; default: // No operation break; @@ -809,8 +780,17 @@ static void* value_memory(const upb_fielddef* field, void* memory) { return memory; } -zval* layout_get(MessageLayout* layout, const void* storage, +CACHED_VALUE* find_zval_property( + MessageHeader* header, const upb_fielddef* field) { + int property_cache_index = + header->descriptor->layout->fields[upb_fielddef_index(field)] + .cache_index; + return OBJ_PROP(&header->std, property_cache_index); +} + +zval* layout_get(MessageLayout* layout, MessageHeader* header, const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC) { + const void* storage = message_data(header); void* memory = slot_memory(layout, storage, field); uint32_t* oneof_case = slot_oneof_case(layout, storage, field); @@ -818,14 +798,21 @@ zval* layout_get(MessageLayout* layout, const void* storage, if (*oneof_case != upb_fielddef_number(field)) { native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC); } else { - native_slot_get(upb_fielddef_type(field), value_memory(field, memory), - cache TSRMLS_CC); + upb_fieldtype_t type = upb_fielddef_type(field); + CACHED_VALUE* stored_cache = find_zval_property(header, field); + native_slot_get( + type, value_memory(type, memory, stored_cache), cache TSRMLS_CC); } return CACHED_PTR_TO_ZVAL_PTR(cache); + } else if (is_map_field(field)) { + map_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); + return CACHED_PTR_TO_ZVAL_PTR(cache); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); } else { - native_slot_get(upb_fielddef_type(field), value_memory(field, memory), + upb_fieldtype_t type = upb_fielddef_type(field); + native_slot_get(type, value_memory(type, memory, cache), cache TSRMLS_CC); return CACHED_PTR_TO_ZVAL_PTR(cache); } @@ -868,8 +855,8 @@ void layout_set(MessageLayout* layout, MessageHeader* header, *oneof_case = upb_fielddef_number(field); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { // Works for both repeated and map fields - memory = DEREF(memory, void**); - zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory); + CACHED_VALUE* cached = find_zval_property(header, field); + zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR(cached); if (EXPECTED(property_ptr != val)) { zend_class_entry *subce = NULL; @@ -901,7 +888,7 @@ void layout_set(MessageLayout* layout, MessageHeader* header, &converted_value); } #if PHP_MAJOR_VERSION < 7 - REPLACE_ZVAL_VALUE((zval**)memory, &converted_value, 1); + REPLACE_ZVAL_VALUE((zval**)cached, &converted_value, 1); #else php_proto_zval_ptr_dtor(property_ptr); ZVAL_ZVAL(property_ptr, &converted_value, 1, 0); @@ -916,12 +903,16 @@ void layout_set(MessageLayout* layout, MessageHeader* header, Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg)); ce = desc->klass; } - native_slot_set(type, ce, value_memory(field, memory), val TSRMLS_CC); + CACHED_VALUE* cache = find_zval_property(header, field); + native_slot_set( + type, ce, value_memory(upb_fielddef_type(field), memory, cache), + val TSRMLS_CC); } } -static void native_slot_merge(const upb_fielddef* field, const void* from_memory, - void* to_memory PHP_PROTO_TSRMLS_DC) { +static void native_slot_merge( + const upb_fielddef* field, const void* from_memory, + void* to_memory PHP_PROTO_TSRMLS_DC) { upb_fieldtype_t type = upb_fielddef_type(field); zend_class_entry* ce = NULL; if (!native_slot_is_default(type, from_memory)) { @@ -943,9 +934,8 @@ static void native_slot_merge(const upb_fielddef* field, const void* from_memory #undef CASE_TYPE case UPB_TYPE_STRING: case UPB_TYPE_BYTES: - native_slot_set(type, NULL, value_memory(field, to_memory), - CACHED_PTR_TO_ZVAL_PTR(DEREF( - from_memory, CACHED_VALUE*)) PHP_PROTO_TSRMLS_CC); + native_slot_set(type, NULL, to_memory, + CACHED_PTR_TO_ZVAL_PTR(from_memory) PHP_PROTO_TSRMLS_CC); break; case UPB_TYPE_MESSAGE: { const upb_msgdef* msg = upb_fielddef_msgsubdef(field); @@ -953,22 +943,21 @@ static void native_slot_merge(const upb_fielddef* field, const void* from_memory ce = desc->klass; if (native_slot_is_default(type, to_memory)) { #if PHP_MAJOR_VERSION < 7 - SEPARATE_ZVAL_IF_NOT_REF((zval**)value_memory(field, to_memory)); + SEPARATE_ZVAL_IF_NOT_REF((zval**)to_memory); #endif CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR( - CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)), ce); + CACHED_PTR_TO_ZVAL_PTR(to_memory), ce); MessageHeader* submsg = - UNBOX(MessageHeader, - CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*))); + UNBOX(MessageHeader, CACHED_PTR_TO_ZVAL_PTR(to_memory)); custom_data_init(ce, submsg PHP_PROTO_TSRMLS_CC); } MessageHeader* sub_from = UNBOX(MessageHeader, - CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*))); + CACHED_PTR_TO_ZVAL_PTR(from_memory)); MessageHeader* sub_to = UNBOX(MessageHeader, - CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*))); + CACHED_PTR_TO_ZVAL_PTR(to_memory)); layout_merge(desc->layout, sub_from, sub_to PHP_PROTO_TSRMLS_CC); break; @@ -1069,10 +1058,17 @@ void layout_merge(MessageLayout* layout, MessageHeader* from, int size, key_length, value_length; MapIter map_it; - zval* to_map_php = - CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)); - zval* from_map_php = - CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*)); + CACHED_VALUE* from_cache = find_zval_property(from, field); + CACHED_VALUE* to_cache = find_zval_property(to, field); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(from_cache)) == IS_NULL) { + continue; + } + map_field_ensure_created(field, to_cache PHP_PROTO_TSRMLS_CC); + + zval* to_map_php = CACHED_PTR_TO_ZVAL_PTR(to_cache); + zval* from_map_php = CACHED_PTR_TO_ZVAL_PTR(from_cache); + Map* to_map = UNBOX(Map, to_map_php); Map* from_map = UNBOX(Map, from_map_php); @@ -1098,8 +1094,16 @@ void layout_merge(MessageLayout* layout, MessageHeader* from, } } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - zval* to_array_php = CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)); - zval* from_array_php = CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*)); + CACHED_VALUE* from_cache = find_zval_property(from, field); + CACHED_VALUE* to_cache = find_zval_property(to, field); + + if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(from_cache)) == IS_NULL) { + continue; + } + repeated_field_ensure_created(field, to_cache PHP_PROTO_TSRMLS_CC); + + zval* to_array_php = CACHED_PTR_TO_ZVAL_PTR(to_cache); + zval* from_array_php = CACHED_PTR_TO_ZVAL_PTR(from_cache); RepeatedField* to_array = UNBOX(RepeatedField, to_array_php); RepeatedField* from_array = UNBOX(RepeatedField, from_array_php); @@ -1129,7 +1133,19 @@ void layout_merge(MessageLayout* layout, MessageHeader* from, } } } else { - native_slot_merge(field, from_memory, to_memory PHP_PROTO_TSRMLS_CC); + switch (upb_fielddef_type(field)) { + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + case UPB_TYPE_MESSAGE: { + CACHED_VALUE* from_cache = find_zval_property(from, field); + CACHED_VALUE* to_cache = find_zval_property(to, field); + native_slot_merge(field, from_cache, to_cache PHP_PROTO_TSRMLS_CC); + break; + } + default: + native_slot_merge(field, from_memory, to_memory PHP_PROTO_TSRMLS_CC); + break; + } } } }