Browse Source

Avoid too much overhead in layout_init (#6716)

* Avoid initializing primitive fields in layout_init

* Avoid initializing string/bytes/message fields in layout_init

* Lazily create map when needed

* Lazily create repeated fields

* Change layout_init to only do memcpy

* Fix test for php-7.0

* Fix conformance test where default value of string/message map is not encoded

* Fix test for zts

* Clean up

* Fix comments
Paul Yang 5 years ago
parent
commit
c53e5b8e11

+ 2 - 0
.gitignore

@@ -139,6 +139,8 @@ composer.lock
 php/tests/generated/
 php/tests/generated/
 php/tests/old_protoc
 php/tests/old_protoc
 php/tests/protobuf/
 php/tests/protobuf/
+php/tests/core
+php/tests/vgcore*
 php/ext/google/protobuf/.libs/
 php/ext/google/protobuf/.libs/
 php/ext/google/protobuf/Makefile.fragments
 php/ext/google/protobuf/Makefile.fragments
 php/ext/google/protobuf/Makefile.global
 php/ext/google/protobuf/Makefile.global

+ 13 - 0
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(
 void repeated_field_create_with_field(
     zend_class_entry *ce, const upb_fielddef *field,
     zend_class_entry *ce, const upb_fielddef *field,
     CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) {
     CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) {

+ 139 - 83
php/ext/google/protobuf/encode_decode.c

@@ -139,6 +139,15 @@ static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) {
   return hd_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 {
 typedef struct {
   void* closure;
   void* closure;
   stringsink sink;
   stringsink sink;
@@ -163,16 +172,18 @@ static const void *newunknownfieldshandlerdata(upb_handlers* h) {
 }
 }
 
 
 typedef struct {
 typedef struct {
+  const upb_fielddef *fd;
   size_t ofs;
   size_t ofs;
   const upb_msgdef *md;
   const upb_msgdef *md;
 } submsg_handlerdata_t;
 } 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,
 static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs,
                                         const upb_fielddef* f) {
                                         const upb_fielddef* f) {
   submsg_handlerdata_t* hd =
   submsg_handlerdata_t* hd =
       (submsg_handlerdata_t*)malloc(sizeof(submsg_handlerdata_t));
       (submsg_handlerdata_t*)malloc(sizeof(submsg_handlerdata_t));
   PHP_PROTO_ASSERT(hd != NULL);
   PHP_PROTO_ASSERT(hd != NULL);
+  hd->fd = f;
   hd->ofs = ofs;
   hd->ofs = ofs;
   hd->md = upb_fielddef_msgsubdef(f);
   hd->md = upb_fielddef_msgsubdef(f);
   upb_handlers_addcleanup(h, hd, free);
   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).
 // this field (such an instance always exists even in an empty message).
 static void *startseq_handler(void* closure, const void* hd) {
 static void *startseq_handler(void* closure, const void* hd) {
   MessageHeader* msg = closure;
   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.
 // Handlers that append primitive values to a repeated field.
@@ -322,15 +336,6 @@ static void *empty_php_string(zval* value_ptr) {
 }
 }
 #endif
 #endif
 #if PHP_MAJOR_VERSION < 7
 #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) {
 static void new_php_string(zval** value_ptr, const char* str, size_t len) {
   SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
   SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
   if (Z_TYPE_PP(value_ptr) == IS_STRING &&
   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);
   ZVAL_STRINGL(*value_ptr, str, len, 1);
 }
 }
 #else
 #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) {
 static void new_php_string(zval* value_ptr, const char* str, size_t len) {
   if (Z_TYPE_P(value_ptr) == IS_STRING) {
   if (Z_TYPE_P(value_ptr) == IS_STRING) {
     zend_string_release(Z_STR_P(value_ptr));
     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) {
 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;
   stringfields_parseframe_t* frame = closure;
   const size_t *ofs = hd;
   const size_t *ofs = hd;
   MessageHeader* msg = (MessageHeader*)frame->closure;
   MessageHeader* msg = (MessageHeader*)frame->closure;
@@ -430,26 +443,60 @@ static void *submsg_handler(void *closure, const void *hd) {
   zval* submsg_php;
   zval* submsg_php;
   MessageHeader* submsg;
   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
 #if PHP_MAJOR_VERSION < 7
     zval val;
     zval val;
     ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC));
     ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC));
     MessageHeader* intern = UNBOX(MessageHeader, &val);
     MessageHeader* intern = UNBOX(MessageHeader, &val);
     custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
     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);
     zval_dtor(&val);
 #else
 #else
     zend_object* obj = subklass->create_object(subklass TSRMLS_CC);
     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);
     MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj);
     custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
     custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
 #endif
 #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);
   submsg = UNBOX(MessageHeader, submsg_php);
   return submsg;
   return submsg;
@@ -457,7 +504,7 @@ static void *submsg_handler(void *closure, const void *hd) {
 
 
 // Handler data for startmap/endmap handlers.
 // Handler data for startmap/endmap handlers.
 typedef struct {
 typedef struct {
-  size_t ofs;
+  const upb_fielddef* fd;
   const upb_msgdef* value_md;
   const upb_msgdef* value_md;
   upb_fieldtype_t key_field_type;
   upb_fieldtype_t key_field_type;
   upb_fieldtype_t value_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) {
 static void *startmapentry_handler(void *closure, const void *hd) {
   MessageHeader* msg = closure;
   MessageHeader* msg = closure;
   const map_handlerdata_t* mapdata = hd;
   const map_handlerdata_t* mapdata = hd;
+  CACHED_VALUE* cache = find_zval_property(msg, mapdata->fd);
   TSRMLS_FETCH();
   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);
   map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
   frame->data = ALLOC(map_parse_frame_data_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
 // 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.
 // pass the handlerdata down to the sub-message handler setup.
 static map_handlerdata_t* new_map_handlerdata(
 static map_handlerdata_t* new_map_handlerdata(
-    size_t ofs,
+    const upb_fielddef* field,
     const upb_msgdef* mapentry_def,
     const upb_msgdef* mapentry_def,
     Descriptor* desc) {
     Descriptor* desc) {
   const upb_fielddef* key_field;
   const upb_fielddef* key_field;
@@ -671,7 +719,7 @@ static map_handlerdata_t* new_map_handlerdata(
   map_handlerdata_t* hd =
   map_handlerdata_t* hd =
       (map_handlerdata_t*)malloc(sizeof(map_handlerdata_t));
       (map_handlerdata_t*)malloc(sizeof(map_handlerdata_t));
   PHP_PROTO_ASSERT(hd != NULL);
   PHP_PROTO_ASSERT(hd != NULL);
-  hd->ofs = ofs;
+  hd->fd = field;
   key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD);
   key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD);
   PHP_PROTO_ASSERT(key_field != NULL);
   PHP_PROTO_ASSERT(key_field != NULL);
   hd->key_field_type = upb_fielddef_type(key_field);
   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,
                                             const upb_fielddef *f,
                                             size_t offset) {
                                             size_t offset) {
   upb_handlerattr attr = UPB_HANDLERATTR_INIT;
   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);
   upb_handlers_setstartseq(h, f, startseq_handler, &attr);
 
 
   switch (upb_fielddef_type(f)) {
   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.
 // Set up handlers for a singular field.
 static void add_handlers_for_singular_field(upb_handlers *h,
 static void add_handlers_for_singular_field(upb_handlers *h,
                                             const upb_fielddef *f,
                                             const upb_fielddef *f,
-                                            size_t offset) {
+                                            size_t offset, bool is_map) {
   switch (upb_fielddef_type(f)) {
   switch (upb_fielddef_type(f)) {
 #define SET_HANDLER(utype, ltype)                          \
 #define SET_HANDLER(utype, ltype)                          \
   case utype: {                                            \
   case utype: {                                            \
@@ -908,16 +956,29 @@ static void add_handlers_for_singular_field(upb_handlers *h,
     case UPB_TYPE_STRING:
     case UPB_TYPE_STRING:
     case UPB_TYPE_BYTES: {
     case UPB_TYPE_BYTES: {
       upb_handlerattr attr = UPB_HANDLERATTR_INIT;
       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_setstartstr(h, f, str_handler, &attr);
       upb_handlers_setstring(h, f, stringdata_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;
       break;
     }
     }
     case UPB_TYPE_MESSAGE: {
     case UPB_TYPE_MESSAGE: {
       upb_handlerattr attr = UPB_HANDLERATTR_INIT;
       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;
       break;
     }
     }
   }
   }
@@ -929,7 +990,7 @@ static void add_handlers_for_mapfield(upb_handlers* h,
                                       size_t offset,
                                       size_t offset,
                                       Descriptor* desc) {
                                       Descriptor* desc) {
   const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef);
   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_handlerattr attr = UPB_HANDLERATTR_INIT;
 
 
   upb_handlers_addcleanup(h, hd, free);
   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,
   add_handlers_for_singular_field(h, key_field,
                                   offsetof(map_parse_frame_data_t,
                                   offsetof(map_parse_frame_data_t,
-                                           key_storage));
+                                           key_storage), true);
   add_handlers_for_singular_field(h, value_field,
   add_handlers_for_singular_field(h, value_field,
                                   offsetof(map_parse_frame_data_t,
                                   offsetof(map_parse_frame_data_t,
-                                           value_storage));
+                                           value_storage), true);
 }
 }
 
 
 // Set up handlers for a oneof field.
 // 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)) {
     } else if (upb_fielddef_isseq(f)) {
       add_handlers_for_repeated_field(h, f, offset);
       add_handlers_for_repeated_field(h, f, offset);
     } else {
     } 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* type_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_TYPE);
   const upb_fielddef* value_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_VALUE);
   const upb_fielddef* value_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_VALUE);
 
 
-  uint32_t type_url_offset;
   zval* type_url_php_str;
   zval* type_url_php_str;
   const upb_msgdef *payload_type = NULL;
   const upb_msgdef *payload_type = NULL;
 
 
   upb_sink_startmsg(sink);
   upb_sink_startmsg(sink);
 
 
   /* Handle type url */
   /* 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) {
   if (Z_STRLEN_P(type_url_php_str) > 0) {
     putstr(type_url_php_str, type_field, sink, false);
     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;
     zval* value_php_str;
     const char* value_str;
     const char* value_str;
     size_t value_len;
     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_str = Z_STRVAL_P(value_php_str);
     value_len = Z_STRLEN_P(value_php_str);
     value_len = Z_STRLEN_P(value_php_str);
 
 
@@ -1355,17 +1410,21 @@ static void putjsonlistvalue(
 
 
   upb_sink_startmsg(sink);
   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_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
     upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
     upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
   } else {
   } 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);
   upb_sink_endmsg(sink, &status);
@@ -1384,16 +1443,20 @@ static void putjsonstruct(
 
 
   upb_sink_startmsg(sink);
   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_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
     upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
     upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
   } else {
   } 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);
   upb_sink_endmsg(sink, &status);
@@ -1455,28 +1518,24 @@ static void putrawmsg(MessageHeader* msg, const Descriptor* desc,
     }
     }
 
 
     if (is_map_field(f)) {
     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);
         putmap(map, f, sink, depth, is_json TSRMLS_CC);
       }
       }
     } else if (upb_fielddef_isseq(f)) {
     } 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);
         putarray(array, f, sink, depth, is_json TSRMLS_CC);
       }
       }
     } else if (upb_fielddef_isstring(f)) {
     } 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)) ||
       if (containing_oneof || (is_json && is_wrapper_msg(desc->msgdef)) ||
           Z_STRLEN_P(str) > 0) {
           Z_STRLEN_P(str) > 0) {
         putstr(str, f, sink, is_json && is_wrapper_msg(desc->msgdef));
         putstr(str, f, sink, is_json && is_wrapper_msg(desc->msgdef));
       }
       }
     } else if (upb_fielddef_issubmsg(f)) {
     } 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 {
     } else {
       upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
       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);
       value_field = map_field_value(f);
       if (!upb_fielddef_issubmsg(value_field)) continue;
       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);
       Map* intern = UNBOX(Map, map_php);
       for (map_begin(map_php, &map_it TSRMLS_CC);
       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)) {
     } else if (upb_fielddef_isseq(f)) {
       if (!upb_fielddef_issubmsg(f)) continue;
       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;
       int size, i;
       RepeatedField* intern = UNBOX(RepeatedField, array_php);
       RepeatedField* intern = UNBOX(RepeatedField, array_php);
@@ -1890,8 +1947,7 @@ static void discard_unknown_fields(MessageHeader* msg) {
         discard_unknown_fields(submsg);
         discard_unknown_fields(submsg);
       }
       }
     } else if (upb_fielddef_issubmsg(f)) {
     } 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;
       if (Z_TYPE_P(submsg_php) == IS_NULL) continue;
       MessageHeader* submsg = UNBOX(MessageHeader, submsg_php);
       MessageHeader* submsg = UNBOX(MessageHeader, submsg_php);
       discard_unknown_fields(submsg);
       discard_unknown_fields(submsg);

+ 12 - 0
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;
 map_field_handlers->get_gc = map_field_get_gc;
 PHP_PROTO_INIT_CLASS_END
 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,
 void map_field_create_with_field(const zend_class_entry *ce,
                                  const upb_fielddef *field,
                                  const upb_fielddef *field,
                                  CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
                                  CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {

+ 4 - 5
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);
       zend_get_property_info(Z_OBJCE_P(object), Z_STR_P(member), true);
 #endif
 #endif
   return layout_get(
   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);
       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;
     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);
              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) {
                       MessageHeader* intern PHP_PROTO_TSRMLS_DC) {
   Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(ce));
   Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(ce));
   intern->data = ALLOC_N(uint8_t, desc->layout->size);
   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
   // We wrap first so that everything in the message object is GC-rooted in
   // case a collection happens during object creation in layout_init().
   // case a collection happens during object creation in layout_init().
   intern->descriptor = desc;
   intern->descriptor = desc;
@@ -575,9 +574,9 @@ PHP_METHOD(Message, readOneof) {
   const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
   const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
 
 
   // Unlike singular fields, oneof fields share cached property. So we cannot
   // 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.
   // 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);
              ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
 }
 }
 
 

+ 12 - 3
php/ext/google/protobuf/protobuf.h

@@ -200,7 +200,7 @@
 
 
 #define CACHED_VALUE zval*
 #define CACHED_VALUE zval*
 #define CACHED_TO_ZVAL_PTR(VALUE) (VALUE)
 #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_PTR(VALUE) (&VALUE)
 #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (VALUE)
 #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (VALUE)
 #define ZVAL_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_VALUE zval
 #define CACHED_TO_ZVAL_PTR(VALUE) (&VALUE)
 #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_PTR(VALUE) (VALUE)
 #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (*VALUE)
 #define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (*VALUE)
 #define ZVAL_TO_CACHED_VALUE(VALUE) (VALUE)
 #define ZVAL_TO_CACHED_VALUE(VALUE) (VALUE)
@@ -935,6 +935,7 @@ struct MessageField {
 
 
 struct MessageLayout {
 struct MessageLayout {
   const upb_msgdef* msgdef;
   const upb_msgdef* msgdef;
+  void* empty_template;  // Can memcpy() onto a layout to clear it.
   MessageField* fields;
   MessageField* fields;
   size_t size;
   size_t size;
 };
 };
@@ -948,7 +949,7 @@ PHP_PROTO_WRAP_OBJECT_END
 MessageLayout* create_layout(const upb_msgdef* msgdef);
 MessageLayout* create_layout(const upb_msgdef* msgdef);
 void layout_init(MessageLayout* layout, void* storage,
 void layout_init(MessageLayout* layout, void* storage,
                  zend_object* object PHP_PROTO_TSRMLS_DC);
                  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);
                  const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC);
 void layout_set(MessageLayout* layout, MessageHeader* header,
 void layout_set(MessageLayout* layout, MessageHeader* header,
                 const upb_fielddef* field, zval* val TSRMLS_DC);
                 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_key(const upb_msgdef* msgdef);
 const upb_fielddef* map_entry_value(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,
 void map_field_create_with_field(const zend_class_entry* ce,
                                  const upb_fielddef* field,
                                  const upb_fielddef* field,
                                  CACHED_VALUE* map_field PHP_PROTO_TSRMLS_DC);
                                  CACHED_VALUE* map_field PHP_PROTO_TSRMLS_DC);
@@ -1147,6 +1150,9 @@ PHP_PROTO_WRAP_OBJECT_START(RepeatedFieldIter)
   long position;
   long position;
 PHP_PROTO_WRAP_OBJECT_END
 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(
 void repeated_field_create_with_field(
     zend_class_entry* ce, const upb_fielddef* field,
     zend_class_entry* ce, const upb_fielddef* field,
     CACHED_VALUE* repeated_field PHP_PROTO_TSRMLS_DC);
     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 FREE(object) efree(object)
 #define PEFREE(object) pefree(object, 1)
 #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.
 // String argument.
 #define STR(str) (str), strlen(str)
 #define STR(str) (str), strlen(str)
 
 

+ 89 - 73
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
 #undef CASE_TYPE
     case UPB_TYPE_STRING:
     case UPB_TYPE_STRING:
     case UPB_TYPE_BYTES:
     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:
     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;
     default: return false;
   }
   }
 }
 }
@@ -599,6 +597,8 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
   // Reserve space for unknown fields.
   // Reserve space for unknown fields.
   off += sizeof(void*);
   off += sizeof(void*);
 
 
+  layout->empty_template = NULL;
+
   TSRMLS_FETCH();
   TSRMLS_FETCH();
   Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msgdef));
   Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msgdef));
   layout->fields = ALLOC_N(MessageField, nfields);
   layout->fields = ALLOC_N(MessageField, nfields);
@@ -744,64 +744,35 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
   layout->size = off;
   layout->size = off;
   layout->msgdef = msgdef;
   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;
   return layout;
 }
 }
 
 
 void free_layout(MessageLayout* layout) {
 void free_layout(MessageLayout* layout) {
+  FREE(layout->empty_template);
   FREE(layout->fields);
   FREE(layout->fields);
   FREE(layout);
   FREE(layout);
 }
 }
 
 
 void layout_init(MessageLayout* layout, void* storage,
 void layout_init(MessageLayout* layout, void* storage,
                  zend_object* object PHP_PROTO_TSRMLS_DC) {
                  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_STRING:
     case UPB_TYPE_BYTES:
     case UPB_TYPE_BYTES:
     case UPB_TYPE_MESSAGE:
     case UPB_TYPE_MESSAGE:
-      memory = DEREF(memory, CACHED_VALUE*);
-      break;
+      return cache;
     default:
     default:
       // No operation
       // No operation
       break;
       break;
@@ -809,8 +780,17 @@ static void* value_memory(const upb_fielddef* field, void* memory) {
   return 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 upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC) {
+  const void* storage = message_data(header);
   void* memory = slot_memory(layout, storage, field);
   void* memory = slot_memory(layout, storage, field);
   uint32_t* oneof_case = slot_oneof_case(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)) {
     if (*oneof_case != upb_fielddef_number(field)) {
       native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
       native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
     } else {
     } 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);
     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) {
   } 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);
     return CACHED_PTR_TO_ZVAL_PTR(cache);
   } else {
   } 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);
                     cache TSRMLS_CC);
     return CACHED_PTR_TO_ZVAL_PTR(cache);
     return CACHED_PTR_TO_ZVAL_PTR(cache);
   }
   }
@@ -868,8 +855,8 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
     *oneof_case = upb_fielddef_number(field);
     *oneof_case = upb_fielddef_number(field);
   } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
   } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
     // Works for both repeated and map fields
     // 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)) {
     if (EXPECTED(property_ptr != val)) {
       zend_class_entry *subce = NULL;
       zend_class_entry *subce = NULL;
@@ -901,7 +888,7 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
                              &converted_value);
                              &converted_value);
       }
       }
 #if PHP_MAJOR_VERSION < 7
 #if PHP_MAJOR_VERSION < 7
-      REPLACE_ZVAL_VALUE((zval**)memory, &converted_value, 1);
+      REPLACE_ZVAL_VALUE((zval**)cached, &converted_value, 1);
 #else
 #else
       php_proto_zval_ptr_dtor(property_ptr);
       php_proto_zval_ptr_dtor(property_ptr);
       ZVAL_ZVAL(property_ptr, &converted_value, 1, 0);
       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));
       Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
       ce = desc->klass;
       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);
   upb_fieldtype_t type = upb_fielddef_type(field);
   zend_class_entry* ce = NULL;
   zend_class_entry* ce = NULL;
   if (!native_slot_is_default(type, from_memory)) {
   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
 #undef CASE_TYPE
       case UPB_TYPE_STRING:
       case UPB_TYPE_STRING:
       case UPB_TYPE_BYTES:
       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;
         break;
       case UPB_TYPE_MESSAGE: {
       case UPB_TYPE_MESSAGE: {
         const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
         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;
         ce = desc->klass;
         if (native_slot_is_default(type, to_memory)) {
         if (native_slot_is_default(type, to_memory)) {
 #if PHP_MAJOR_VERSION < 7
 #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
 #endif
           CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(
           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 =
           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);
           custom_data_init(ce, submsg PHP_PROTO_TSRMLS_CC);
         }
         }
 
 
         MessageHeader* sub_from =
         MessageHeader* sub_from =
             UNBOX(MessageHeader,
             UNBOX(MessageHeader,
-                  CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*)));
+                  CACHED_PTR_TO_ZVAL_PTR(from_memory));
         MessageHeader* sub_to =
         MessageHeader* sub_to =
             UNBOX(MessageHeader,
             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);
         layout_merge(desc->layout, sub_from, sub_to PHP_PROTO_TSRMLS_CC);
         break;
         break;
@@ -1069,10 +1058,17 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
       int size, key_length, value_length;
       int size, key_length, value_length;
       MapIter map_it;
       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* to_map = UNBOX(Map, to_map_php);
       Map* from_map = UNBOX(Map, from_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) {
     } 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* to_array = UNBOX(RepeatedField, to_array_php);
       RepeatedField* from_array = UNBOX(RepeatedField, from_array_php);
       RepeatedField* from_array = UNBOX(RepeatedField, from_array_php);
 
 
@@ -1129,7 +1133,19 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
         }
         }
       }
       }
     } else {
     } 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;
+      }
     }
     }
   }
   }
 }
 }